property_from_yaml.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2019, Bielefeld University
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Bielefeld University nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Robert Haschke */
36 
37 #include "property_factory.h"
38 #include <yaml.h>
41 
42 namespace mtc = ::moveit::task_constructor;
43 
50 namespace {
51 
52 class ScopedYamlEvent
53 {
54 public:
55  ~ScopedYamlEvent() { yaml_event_delete(&event_); }
56  operator yaml_event_t const&() const { return event_; }
57  operator yaml_event_t&() { return event_; }
58 
59 private:
60  yaml_event_t event_;
61 };
62 
63 // Event-based YAML parser, creating an rviz::Property tree
64 // https://www.wpsoftware.net/andrew/pages/libyaml.html
65 class Parser
66 {
67 public:
68  static const int YAML_ERROR_EVENT = 255;
69 
70  Parser(const std::string& value);
71  ~Parser();
72 
73  rviz::Property* process(const QString& name, const QString& description, rviz::Property* old) const;
74 
75 private:
76  static rviz::Property* createScalar(const QString& name, const QString& description, const QByteArray& value,
77  rviz::Property* old);
78 
79  // return true if there was no error so far
80  bool noError() const { return parser_.error == YAML_NO_ERROR; }
81  // parse a single event and return it's type, YAML_ERROR_EVENT on parsing error
82  int parse(yaml_event_t& event) const;
83  // process events: scalar, start mapping, start sequence
84  rviz::Property* process(const yaml_event_t& event, const QString& name, const QString& description,
85  rviz::Property* old) const;
86 
87  inline static QByteArray byteArray(const yaml_event_t& event) {
88  assert(event.type == YAML_SCALAR_EVENT);
89  return QByteArray::fromRawData(reinterpret_cast<const char*>(event.data.scalar.value), event.data.scalar.length);
90  }
91  // Try to set value of existing rviz::Property (expecting matching types). Return false on error.
92  static bool setValue(rviz::Property* old, const QByteArray& value);
93 
94  static rviz::Property* createParent(const QString& name, const QString& description, rviz::Property* old);
95  rviz::Property* processMapping(const QString& name, const QString& description, rviz::Property* old) const;
96  rviz::Property* processSequence(const QString& name, const QString& description, rviz::Property* old) const;
97 
98 private:
99  mutable yaml_parser_t parser_;
100 };
101 
102 Parser::Parser(const std::string& value) {
103  yaml_parser_initialize(&parser_);
104  yaml_parser_set_input_string(&parser_, reinterpret_cast<const yaml_char_t*>(value.c_str()), value.size());
105 }
106 
107 Parser::~Parser() {
108  yaml_parser_delete(&parser_);
109 }
110 
111 int Parser::parse(yaml_event_t& event) const {
112  if (!yaml_parser_parse(&parser_, &event)) {
113  return YAML_ERROR_EVENT;
114  }
115  return event.type;
116 }
117 
118 // main processing function
119 rviz::Property* Parser::process(const QString& name, const QString& description, rviz::Property* old) const {
120  bool stop = false;
121  while (!stop) {
122  ScopedYamlEvent event;
123  switch (parse(event)) {
124  case YAML_ERROR_EVENT:
125  return Parser::createScalar(name, description, "YAML error", old);
126  case YAML_STREAM_END_EVENT:
127  stop = true;
128  break;
129 
130  case YAML_SEQUENCE_START_EVENT:
131  case YAML_MAPPING_START_EVENT:
132  case YAML_SCALAR_EVENT:
133  return process(event, name, description, old);
134 
135  default:
136  break;
137  }
138  }
139  // if we get here, there was no content in the yaml stream
140  return createScalar(name, description, "undefined", old);
141 }
142 
143 // default processing for scalar, start mapping, start sequence events
144 rviz::Property* Parser::process(const yaml_event_t& event, const QString& name, const QString& description,
145  rviz::Property* old) const {
146  switch (event.type) {
147  case YAML_SEQUENCE_START_EVENT:
148  return processSequence(name, description, old);
149  case YAML_MAPPING_START_EVENT:
150  return processMapping(name, description, old);
151  case YAML_SCALAR_EVENT:
152  return createScalar(name, description, byteArray(event), old);
153  default:
154  throw std::runtime_error("Unhandled YAML event");
155  }
156 }
157 
158 // Try to set numeric or arbitrary scalar value from YAML node. Needs to match old's type.
159 bool Parser::setValue(rviz::Property* old, const QByteArray& value) {
160  if (rviz::FloatProperty* p = dynamic_cast<rviz::FloatProperty*>(old)) {
161  bool ok = true;
162  double v = value.toDouble(&ok);
163  if (ok)
164  p->setValue(v);
165  return ok;
166  }
167  if (rviz::StringProperty* p = dynamic_cast<rviz::StringProperty*>(old)) {
168  // value should be an arbitrary string. If not throws YAML::BadConversion
169  p->setValue(value);
170  return true;
171  }
172  return false;
173 }
174 
175 // Update existing old rviz:Property or create a new one from scalar YAML node
176 rviz::Property* Parser::createScalar(const QString& name, const QString& description, const QByteArray& value,
177  rviz::Property* old) {
178  // try to update value, expecting matching rviz::Property
179  if (old && setValue(old, value)) {
180  // only if setValue succeeded, also update the rest
181  old->setName(name);
182  old->setDescription(description);
183  return old;
184  }
185 
186  bool ok = true;
187  double v = value.toDouble(&ok);
188  if (ok) // if value is a number, create a FloatProperty
189  old = new rviz::FloatProperty(name, v, description);
190  else // otherwise create a StringProperty
191  old = new rviz::StringProperty(name, value, description);
192 
193  old->setReadOnly(true);
194  return old;
195 }
196 
197 // Reuse old property (or create new one) as parent for a sequence or map
198 rviz::Property* Parser::createParent(const QString& name, const QString& description, rviz::Property* old) {
199  // don't reuse float or string properties (they are for scalars)
200  if (dynamic_cast<rviz::FloatProperty*>(old) || dynamic_cast<rviz::StringProperty*>(old))
201  old = nullptr;
202  if (!old) {
203  old = new rviz::Property(name, QVariant(), description);
204  old->setReadOnly(true);
205  } else {
206  old->setName(name);
207  old->setDescription(description);
208  }
209  return old;
210 }
211 
212 // Hierarchically create property from YAML map node
213 rviz::Property* Parser::processMapping(const QString& name, const QString& description, rviz::Property* root) const {
214  root = createParent(name, description, root);
215  int index = 0; // current child index in root
216  bool stop = false;
217  while (!stop && noError()) { // parse all map items
218  ScopedYamlEvent event;
219  switch (parse(event)) { // parse key
220  case YAML_MAPPING_END_EVENT: // all fine, reached end of mapping
221  stop = true;
222  break;
223 
224  case YAML_SCALAR_EVENT: { // key
225  QByteArray key = byteArray(event);
226  int num = root->numChildren();
227  // find first child with name >= it->name
228  int next = index;
229  while (next < num && root->childAt(next)->getName() < key)
230  ++next;
231  // and remove all children in range [index, next) at once
232  root->removeChildren(index, next - index);
233  num = root->numChildren();
234 
235  // if names differ, insert a new child, otherwise reuse existing
236  rviz::Property* old_child = index < num ? root->childAt(index) : nullptr;
237  if (old_child && old_child->getName() != key)
238  old_child = nullptr;
239 
240  rviz::Property* new_child = nullptr;
241  switch (parse(event)) { // parse value
242  case YAML_MAPPING_START_EVENT:
243  case YAML_SEQUENCE_START_EVENT:
244  case YAML_SCALAR_EVENT:
245  new_child = process(event, key, "", old_child);
246  break;
247  default: // all other events are an error
248  new_child = createScalar(key, "", parser_.problem, old_child);
249  root->setValue("YAML error");
250  break;
251  }
252 
253  if (new_child != old_child)
254  root->addChild(new_child, index);
255  ++index;
256  break;
257  }
258 
259  default: // unexpected event
260  root->setValue("YAML error");
261  stop = true;
262  break;
263  }
264  }
265  // remove remaining children
266  root->removeChildren(index, root->numChildren() - index);
267  return root;
268 }
269 
270 // Hierarchically create property from YAML sequence node. Items are named [#].
271 rviz::Property* Parser::processSequence(const QString& name, const QString& description, rviz::Property* root) const {
272  root = createParent(name, description, root);
273  int index = 0; // current child index in root
274  bool stop = false;
275  while (!stop && noError()) { // parse all map items
276  ScopedYamlEvent event;
277  switch (parse(event)) {
278  case YAML_SEQUENCE_END_EVENT: // all fine, reached end of sequence
279  stop = true;
280  break;
281 
282  case YAML_MAPPING_START_EVENT:
283  case YAML_SEQUENCE_START_EVENT:
284  case YAML_SCALAR_EVENT: {
285  rviz::Property* old_child = root->childAt(index); // nullptr for invalid index
286  rviz::Property* new_child = process(event, QString("[%1]").arg(index), "", old_child);
287  if (new_child != old_child)
288  root->addChild(new_child, index);
289  if (++index >= 10)
290  stop = true; // limit number of shown entries
291  break;
292  }
293 
294  default: // unexpected event
295  root->setValue("YAML error");
296  stop = true;
297  break;
298  }
299  }
300  // remove remaining children
301  root->removeChildren(index, root->numChildren() - index);
302  return root;
303 }
304 } // namespace
305 
306 namespace moveit_rviz_plugin {
307 
308 rviz::Property* PropertyFactory::createDefault(const std::string& name, const std::string& /*type*/,
309  const std::string& description, const std::string& value,
310  rviz::Property* old) {
311  QString qname = QString::fromStdString(name);
312  QString qdesc = QString::fromStdString(description);
313  Parser parser(value);
314  return parser.process(qname, qdesc, old);
315 }
316 } // namespace moveit_rviz_plugin
rviz::Property::setName
virtual void setName(const QString &name)
getName
ROSCONSOLE_CONSOLE_IMPL_DECL std::string getName(void *handle)
property_factory.h
ok
ROSCPP_DECL bool ok()
float_property.h
rviz::FloatProperty
rviz::Property
moveit::task_constructor
rviz::StringProperty
value
float value
next
EndPoint * next[3]
moveit_rviz_plugin
rviz::Property::setDescription
virtual void setDescription(const QString &description)
rviz::Property::getName
virtual QString getName() const
rviz::Property::setReadOnly
virtual void setReadOnly(bool read_only)
index
unsigned int index
v
v
root
root
string_property.h
moveit_rviz_plugin::PropertyFactory::createDefault
static rviz::Property * createDefault(const std::string &name, const std::string &type, const std::string &description, const std::string &value, rviz::Property *old=nullptr)
create rviz::Property for property of given name, type, description, and value
Definition: property_factory.cpp:183


visualization
Author(s): Robert Haschke
autogenerated on Thu Feb 27 2025 03:39:51