Fawkes API  Fawkes Development Version
filter_thread.cpp
1 
2 /***************************************************************************
3  * filter_thread.cpp - Thread that filters data in blackboard
4  *
5  * Created: Sun Mar 13 01:12:53 2011
6  * Copyright 2006-2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "filter_thread.h"
23 #include "filters/max_circle.h"
24 #include "filters/720to360.h"
25 #include "filters/1080to360.h"
26 #include "filters/deadspots.h"
27 #include "filters/cascade.h"
28 #include "filters/reverse_angle.h"
29 #include "filters/min_circle.h"
30 #include "filters/circle_sector.h"
31 #include "filters/min_merge.h"
32 #ifdef HAVE_TF
33 # include "filters/projection.h"
34  #include "filters/map_filter.h"
35 #endif
36 
37 #include <core/threading/barrier.h>
38 #include <core/threading/mutex.h>
39 #include <core/threading/wait_condition.h>
40 
41 #include <utils/time/time.h>
42 
43 #include <interfaces/Laser360Interface.h>
44 #include <interfaces/Laser720Interface.h>
45 #include <interfaces/Laser1080Interface.h>
46 
47 #include <cstring>
48 #include <memory>
49 #include <cstdio>
50 
51 using namespace fawkes;
52 
53 /** @class LaserFilterThread "filter_thread.h"
54  * Laser filter thread.
55  * This thread integrates into the Fawkes main loop at the sensor processing
56  * hook, reads data from specified interfaces, filters it with a given
57  * cascade, and then writes it back to an interface.
58  * @author Tim Niemueller
59  */
60 
61 
62 /** Constructor.
63  * @param cfg_name short name of configuration group
64  * @param cfg_prefix configuration path prefix
65  */
67  std::string &cfg_prefix)
68  : Thread("LaserFilterThread", Thread::OPMODE_WAITFORWAKEUP),
69  BlockedTimingAspect(BlockedTimingAspect::WAKEUP_HOOK_SENSOR_PROCESS)
70 {
71  set_name("LaserFilterThread(%s)", cfg_name.c_str());
72  __cfg_name = cfg_name;
73  __cfg_prefix = cfg_prefix;
74  __wait_barrier = NULL;
75 }
76 
77 
78 void
80 {
81  try {
82  open_interfaces(__cfg_prefix + "in/", __in, __in_bufs, false);
83  open_interfaces(__cfg_prefix + "out/", __out, __out_bufs, true);
84 
85  if (__in.empty()) {
86  throw Exception("No input interfaces defined for %s", __cfg_name.c_str());
87  }
88  if (__out.empty()) {
89  throw Exception("No output interfaces defined for %s", __cfg_name.c_str());
90  }
91 
92 
93  std::map<std::string, std::string> filters;
94 
95  std::string fpfx = __cfg_prefix + "filters/";
96 #if __cplusplus >= 201103L
97  std::unique_ptr<Configuration::ValueIterator> i(config->search(fpfx.c_str()));
98 #else
99  std::auto_ptr<Configuration::ValueIterator> i(config->search(fpfx.c_str()));
100 #endif
101 
102  while (i->next()) {
103 
104  std::string suffix = std::string(i->path()).substr(fpfx.length());
105  std::string filter_name = std::string(suffix.substr(0,suffix.find("/")));
106  std::string conf_key = std::string(suffix.substr(suffix.find("/")+1,suffix.length()));
107 
108  if (conf_key != "type") continue;
109 
110  if (! i->is_string()) {
111  throw Exception("Filter value %s is not a string", i->path());
112  }
113 
114  filters[filter_name] = i->get_string();
115  }
116  if (filters.empty()) {
117  throw Exception("No filters defined for %s", __cfg_name.c_str());
118  }
119 
120  if (filters.size() == 1) {
121  std::string filter_name = filters.begin()->first;
122  logger->log_debug(name(), "Adding filter %s (%s)",
123  filter_name.c_str(), filters[filter_name].c_str());
124  __filter = create_filter(__cfg_name + "/" + filter_name, filters[filter_name], fpfx + filter_name + "/",
125  __in[0].size, __in_bufs);
126  } else {
127  LaserDataFilterCascade *cascade =
128  new LaserDataFilterCascade(__cfg_name, __in[0].size, __in_bufs);
129 
130  try {
131  std::map<std::string, std::string>::iterator f;
132  for (f = filters.begin(); f != filters.end(); ++f) {
133  logger->log_debug(name(), "Adding filter %s (%s) %zu %zu",
134  f->first.c_str(), f->second.c_str(), __in_bufs.size(),
135  cascade->get_out_vector().size());
136  cascade->add_filter(create_filter(__cfg_name + "/" + f->first, f->second, fpfx + f->first + "/",
137  cascade->get_out_data_size(), cascade->get_out_vector()));
138  }
139  } catch (Exception &e) {
140  delete cascade;
141  throw;
142  }
143 
144  __filter = cascade;
145  }
146 
147  if (__out[0].size != __filter->get_out_data_size()) {
148  Exception e("Output interface and filter data size for %s do not match (%u != %u)",
149  __cfg_name.c_str(), __out[0].size, __filter->get_out_data_size());
150  delete __filter;
151  throw e;
152  }
153 
154  __filter->set_out_vector(__out_bufs);
155 
156  } catch (Exception &e) {
157  for (unsigned int i = 0; i < __in.size(); ++i) {
158  blackboard->close(__in[i].interface);
159  }
160  for (unsigned int i = 0; i < __out.size(); ++i) {
161  blackboard->close(__out[i].interface);
162  }
163  throw;
164  }
165 
166  std::list<LaserFilterThread *>::iterator wt;
167  for (wt = __wait_threads.begin(); wt != __wait_threads.end(); ++wt) {
168  logger->log_debug(name(), "Depending on %s", (*wt)->name());
169  }
170 
171  __wait_done = true;
172  __wait_mutex = new Mutex();
173  __wait_cond = new WaitCondition(__wait_mutex);
174 }
175 
176 
177 void
179 {
180  delete __filter;
181  delete __wait_cond;
182  delete __wait_mutex;
183 
184  for (unsigned int i = 0; i < __in.size(); ++i) {
185  blackboard->close(__in[i].interface);
186  }
187  __in.clear();
188  for (unsigned int i = 0; i < __out.size(); ++i) {
189  blackboard->close(__out[i].interface);
190  }
191  __out.clear();
192 }
193 
194 void
196 {
197  // Wait for dependencies
198  if (__wait_barrier) {
199  std::list<LaserFilterThread *>::iterator wt;
200  for (wt = __wait_threads.begin(); wt != __wait_threads.end(); ++wt) {
201  (*wt)->wait_done();
202  }
203  }
204 
205  // Read input interfaces
206  const size_t in_num = __in.size();
207  for (size_t i = 0; i != in_num; ++i) {
208  __in[i].interface->read();
209  if (__in[i].size == 360) {
210  __in_bufs[i]->frame = __in[i].interface_typed.as360->frame();
211  *__in_bufs[i]->timestamp = __in[i].interface_typed.as360->timestamp();
212  } else if (__in[i].size == 720) {
213  __in_bufs[i]->frame = __in[i].interface_typed.as720->frame();
214  *__in_bufs[i]->timestamp = __in[i].interface_typed.as720->timestamp();
215  } else if (__in[i].size == 1080) {
216  __in_bufs[i]->frame = __in[i].interface_typed.as1080->frame();
217  *__in_bufs[i]->timestamp = __in[i].interface_typed.as1080->timestamp();
218  }
219  }
220 
221  // Filter!
222  try {
223  __filter->filter();
224  } catch (Exception &e) {
225  logger->log_warn(name(), "Filtering failed, exception follows");
226  logger->log_warn(name(), e);
227  }
228 
229  // Write output interfaces
230  const size_t num = __out.size();
231  for (size_t i = 0; i < num; ++i) {
232  if (__out[i].size == 360) {
233  __out[i].interface_typed.as360->set_timestamp(__out_bufs[i]->timestamp);
234  __out[i].interface_typed.as360->set_frame(__out_bufs[i]->frame.c_str());
235  } else if (__out[i].size == 720) {
236  __out[i].interface_typed.as720->set_timestamp(__out_bufs[i]->timestamp);
237  __out[i].interface_typed.as720->set_frame(__out_bufs[i]->frame.c_str());
238  } else if (__out[i].size == 1080) {
239  __out[i].interface_typed.as1080->set_timestamp(__out_bufs[i]->timestamp);
240  __out[i].interface_typed.as1080->set_frame(__out_bufs[i]->frame.c_str());
241  }
242  __out[i].interface->write();
243  }
244 
245  if (__wait_barrier) {
246  __wait_mutex->lock();
247  __wait_done = false;
248  __wait_cond->wake_all();
249  __wait_mutex->unlock();
250  __wait_barrier->wait();
251  __wait_mutex->lock();
252  __wait_done = true;
253  __wait_mutex->unlock();
254  }
255 }
256 
257 
258 /** Wait until thread is done.
259  * This method blocks the calling thread until this instance's thread has
260  * finished filtering.
261  */
262 void
264 {
265  __wait_mutex->lock();
266  while (__wait_done) {
267  //logger->log_debug(name(), "%s is waiting", Thread::current_thread()->name());
268  __wait_cond->wait();
269  }
270  __wait_mutex->unlock();
271 }
272 
273 
274 void
275 LaserFilterThread::open_interfaces(std::string prefix,
276  std::vector<LaserInterface> &ifs,
277  std::vector<LaserDataFilter::Buffer *> &bufs, bool writing)
278 {
279 #if __cplusplus >= 201103L
280  std::unique_ptr<Configuration::ValueIterator> in(config->search(prefix.c_str()));
281 #else
282  std::auto_ptr<Configuration::ValueIterator> in(config->search(prefix.c_str()));
283 #endif
284  while (in->next()) {
285  if (! in->is_string()) {
286  throw Exception("Config value %s is not of type string", in->path());
287  } else {
288  std::string uid = in->get_string();
289  size_t sf;
290 
291  if ((sf = uid.find("::")) == std::string::npos) {
292  throw Exception("Interface '%s' is not a UID", uid.c_str());
293  }
294  std::string type = uid.substr(0, sf);
295  std::string id = uid.substr(sf + 2);
296 
297  LaserInterface lif;
298  lif.interface = NULL;
299 
300  if (type == "Laser360Interface") {
301  lif.size = 360;
302  } else if (type == "Laser720Interface") {
303  lif.size = 720;
304  } else if (type == "Laser1080Interface") {
305  lif.size = 1080;
306  } else {
307  throw Exception("Interfaces must be of type Laser360Interface, "
308  "Laser720Interface, or Laser1080Interface, "
309  "but it is '%s'", type.c_str());
310  }
311 
312  lif.id = id;
313  ifs.push_back(lif);
314  }
315  }
316 
317  if (ifs.empty()) {
318  throw Exception("No interfaces defined at %s", prefix.c_str());
319  }
320 
321  bufs.resize(ifs.size());
322 
323  unsigned int req_size = ifs[0].size;
324 
325  try {
326  if (writing) {
327  for (unsigned int i = 0; i < ifs.size(); ++i) {
328  if (req_size != ifs[i].size) {
329  throw Exception("Interfaces of mixed sizes for %s",
330  __cfg_name.c_str());
331  }
332 
333  if (ifs[i].size == 360) {
334  logger->log_debug(name(), "Opening writing Laser360Interface::%s", ifs[i].id.c_str());
335  Laser360Interface *laser360 =
336  blackboard->open_for_writing<Laser360Interface>(ifs[i].id.c_str());
337 
338  laser360->set_auto_timestamping(false);
339 
340  ifs[i].interface_typed.as360 = laser360;
341  ifs[i].interface = laser360;
342  bufs[i] = new LaserDataFilter::Buffer();
343  bufs[i]->name = laser360->uid();
344  bufs[i]->values = laser360->distances();
345 
346  } else if (ifs[i].size == 720) {
347  logger->log_debug(name(), "Opening writing Laser720Interface::%s", ifs[i].id.c_str());
348  Laser720Interface *laser720 =
349  blackboard->open_for_writing<Laser720Interface>(ifs[i].id.c_str());
350 
351  laser720->set_auto_timestamping(false);
352 
353  ifs[i].interface_typed.as720 = laser720;
354  ifs[i].interface = laser720;
355  bufs[i] = new LaserDataFilter::Buffer();
356  bufs[i]->name = laser720->uid();
357  bufs[i]->values = laser720->distances();
358 
359  } else if (ifs[i].size == 1080) {
360  logger->log_debug(name(), "Opening writing Laser1080Interface::%s",
361  ifs[i].id.c_str());
362  Laser1080Interface *laser1080 =
363  blackboard->open_for_writing<Laser1080Interface>(ifs[i].id.c_str());
364 
365  laser1080->set_auto_timestamping(false);
366 
367  ifs[i].interface_typed.as1080 = laser1080;
368  ifs[i].interface = laser1080;
369  bufs[i] = new LaserDataFilter::Buffer();
370  bufs[i]->name = laser1080->uid();
371  bufs[i]->values = laser1080->distances();
372  }
373  }
374  } else {
375  for (unsigned int i = 0; i < ifs.size(); ++i) {
376  if (ifs[i].size == 360) {
377  logger->log_debug(name(), "Opening reading Laser360Interface::%s", ifs[i].id.c_str());
378  Laser360Interface *laser360 =
379  blackboard->open_for_reading<Laser360Interface>(ifs[i].id.c_str());
380 
381  ifs[i].interface_typed.as360 = laser360;
382  ifs[i].interface = laser360;
383  bufs[i] = new LaserDataFilter::Buffer();
384  bufs[i]->name = laser360->uid();
385  bufs[i]->frame = laser360->frame();
386  bufs[i]->values = laser360->distances();
387 
388  } else if (ifs[i].size == 720) {
389  logger->log_debug(name(), "Opening reading Laser720Interface::%s", ifs[i].id.c_str());
390  Laser720Interface *laser720 =
391  blackboard->open_for_reading<Laser720Interface>(ifs[i].id.c_str());
392 
393  ifs[i].interface_typed.as720 = laser720;
394  ifs[i].interface = laser720;
395  bufs[i] = new LaserDataFilter::Buffer();
396  bufs[i]->name = laser720->uid();
397  bufs[i]->frame = laser720->frame();
398  bufs[i]->values = laser720->distances();
399 
400  } else if (ifs[i].size == 1080) {
401  logger->log_debug(name(), "Opening reading Laser1080Interface::%s",
402  ifs[i].id.c_str());
403  Laser1080Interface *laser1080 =
404  blackboard->open_for_reading<Laser1080Interface>(ifs[i].id.c_str());
405 
406  ifs[i].interface_typed.as1080 = laser1080;
407  ifs[i].interface = laser1080;
408  bufs[i] = new LaserDataFilter::Buffer();
409  bufs[i]->name = laser1080->uid();
410  bufs[i]->frame = laser1080->frame();
411  bufs[i]->values = laser1080->distances();
412  }
413  }
414  }
415  } catch (Exception &e) {
416  for (unsigned int i = 0; i < ifs.size(); ++i) {
417  blackboard->close(ifs[i].interface);
418  }
419  ifs.clear();
420  bufs.clear();
421  throw;
422  }
423 }
424 
425 
427 LaserFilterThread::create_filter(std::string filter_name,
428  std::string filter_type, std::string prefix,
429  unsigned int in_data_size,
430  std::vector<LaserDataFilter::Buffer *> &inbufs)
431 {
432  if (filter_type == "720to360") {
433  bool average = false;
434  try {
435  average = config->get_bool((prefix + "average").c_str());
436  } catch (Exception &e) {} // ignore
437  return new Laser720to360DataFilter(filter_name, average, in_data_size, inbufs);
438  } else if (filter_type == "1080to360") {
439  bool average = false;
440  try {
441  average = config->get_bool((prefix + "average").c_str());
442  } catch (Exception &e) {} // ignore
443  return new Laser1080to360DataFilter(filter_name, average, in_data_size, inbufs);
444  } else if (filter_type == "reverse") {
445  return new LaserReverseAngleDataFilter(filter_name, in_data_size, inbufs);
446  } else if (filter_type == "max_circle") {
447  float radius = config->get_float((prefix + "radius").c_str());
448  return new LaserMaxCircleDataFilter(filter_name, radius, in_data_size, inbufs);
449  } else if (filter_type == "min_circle") {
450  float radius = config->get_float((prefix + "radius").c_str());
451  return new LaserMinCircleDataFilter(filter_name, radius, in_data_size, inbufs);
452  } else if (filter_type == "circle_sector") {
453  unsigned int from = config->get_uint((prefix + "from").c_str());
454  unsigned int to = config->get_uint((prefix + "to").c_str());
455  return new LaserCircleSectorDataFilter(filter_name, from, to, in_data_size, inbufs);
456  } else if (filter_type == "deadspots") {
457  return new LaserDeadSpotsDataFilter(filter_name, config, logger, prefix, in_data_size, inbufs);
458  } else if (filter_type == "min_merge") {
459  std::string timestamp_selection;
460  try {
461  timestamp_selection = config->get_string((prefix + "timestamp_selection").c_str());
462  } catch (Exception &e) {} // ignored, use default
463 
464  if (timestamp_selection == "latest") {
465  return new LaserMinMergeDataFilter(filter_name, logger, in_data_size, inbufs,
467  } else if (timestamp_selection == "first") {
468  return new LaserMinMergeDataFilter(filter_name, logger, in_data_size, inbufs,
470  } else if (timestamp_selection == "index") {
471  unsigned int timestamp_if_index =
472  config->get_uint((prefix + "timestamp_index").c_str());
473  return new LaserMinMergeDataFilter(filter_name, logger, in_data_size, inbufs,
475  timestamp_if_index);
476  } else if (timestamp_selection != "") {
477  throw Exception("Laser filter: unknown timestamp selection method '%s'",
478  timestamp_selection.c_str());
479  } else {
480  return new LaserMinMergeDataFilter(filter_name, logger, in_data_size, inbufs,
482  }
483  } else if (filter_type == "projection") {
484 #ifdef HAVE_TF
485  const float not_from_x = config->get_float((prefix + "not_from_x").c_str());
486  const float not_to_x = config->get_float((prefix + "not_to_x").c_str());
487  const float not_from_y = config->get_float((prefix + "not_from_y").c_str());
488  const float not_to_y = config->get_float((prefix + "not_to_y").c_str());
489  const float only_from_z = config->get_float((prefix + "only_from_z").c_str());
490  const float only_to_z = config->get_float((prefix + "only_to_z").c_str());
491  const std::string frame =
492  config->get_string((prefix + "target_frame").c_str());
493  return new LaserProjectionDataFilter(filter_name, tf_listener, frame,
494  not_from_x, not_to_x,
495  not_from_y, not_to_y,
496  only_from_z, only_to_z,
497  in_data_size, inbufs);
498 #else
499  throw Exception("Projection filter unavailable, tf missing");
500 #endif
501  } else if (filter_type == "map_filter") {
502 #ifdef HAVE_TF
503  return new LaserMapFilterDataFilter(filter_name, in_data_size, inbufs, tf_listener, config, logger);
504 #else
505  throw Exception("Projection filter unavailable, tf missing");
506 #endif
507  } else {
508  throw Exception("Unknown filter type %s", filter_type.c_str());
509  }
510 }
511 
512 
513 /** Set threads to wait for in loop.
514  * The threads produce data this thread depends on as input, therefore this
515  * instance has to wait for these threads to get up to date data in each
516  * loop.
517  * @param threads threads this instance depends on
518  */
519 void
520 LaserFilterThread::set_wait_threads(std::list<LaserFilterThread *> &threads)
521 {
522  __wait_threads = threads;
523 }
524 
525 
526 /** Set wait barrier.
527  * If there are any dependencies between laser filter threads a common
528  * barrier is used to signal the end of filtering to reset internal
529  * variables for the next loop.
530  * @param barrier common "end of filtering" barrier
531  */
532 void
534 {
535  __wait_barrier = barrier;
536 }
Laser360Interface Fawkes BlackBoard Interface.
Wait until a given condition holds.
use the latest of all timestamps
Definition: min_merge.h:39
virtual void finalize()
Finalize the thread.
Laser1080Interface Fawkes BlackBoard Interface.
Erase beams outside specified circle sector.
Definition: circle_sector.h:28
void set_auto_timestamping(bool enabled)
Enable or disable automated timestamping.
Definition: interface.cpp:760
Fawkes library namespace.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
void unlock()
Unlock the mutex.
Definition: mutex.cpp:135
virtual void wait()
Wait for other threads.
Definition: barrier.cpp:157
void wake_all()
Wake up all waiting threads.
Removes static laser data (laser beams near occupied map cells)
Definition: map_filter.h:34
Erase dead spots (i.e.
Definition: deadspots.h:37
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
Downsample filter from 1080 to 360 values.
Definition: 1080to360.h:28
virtual bool next()=0
Check if there is another element and advance to this if possible.
Thread class encapsulation of pthreads.
Definition: thread.h:42
Downsample filter from 720 to 360 values.
Definition: 720to360.h:28
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:44
Erase beams below a certain minimum distance distance.
Definition: min_circle.h:28
char * frame() const
Get frame value.
void set_wait_barrier(fawkes::Barrier *barrier)
Set wait barrier.
Thread aspect to use blocked timing.
virtual unsigned int get_out_data_size()
Get size of filtered data array.
Definition: filter.cpp:176
LaserFilterThread(std::string &cfg_name, std::string &cfg_prefix)
Constructor.
Cascade of several laser filters to one.
Definition: cascade.h:30
virtual void filter()=0
Filter the incoming data.
virtual bool is_string() const =0
Check if current value is a string.
void set_name(const char *format,...)
Set name of thread.
Definition: thread.cpp:761
Base class for exceptions in Fawkes.
Definition: exception.h:36
char * frame() const
Get frame value.
Cut of laser data at max distance.
Definition: max_circle.h:28
iterator begin()
Get iterator for messages.
Definition: exception.cpp:700
virtual std::string get_string() const =0
Get string value.
const char * name() const
Get name of thread.
Definition: thread.h:95
Laser data buffer.
Definition: filter.h:35
const char * uid() const
Get unique identifier of interface.
Definition: interface.cpp:687
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
void set_wait_threads(std::list< LaserFilterThread *> &threads)
Set threads to wait for in loop.
virtual const char * path() const =0
Path of value.
Projects one laser into another laser&#39;s plane.
Definition: projection.h:41
float * distances() const
Get distances value.
void wait()
Wait for the condition forever.
use a specific index in the input buffer list
Definition: min_merge.h:41
void wait_done()
Wait until thread is done.
void add_filter(LaserDataFilter *filter)
Add a filter to the cascade.
Definition: cascade.cpp:72
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void init()
Initialize the thread.
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
float * distances() const
Get distances value.
void lock()
Lock this mutex.
Definition: mutex.cpp:89
use the first (oldest) of all timestamps
Definition: min_merge.h:40
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
Mutex mutual exclusion lock.
Definition: mutex.h:32
float * distances() const
Get distances value.
char * frame() const
Get frame value.
Reverse the angle of beams.
Definition: reverse_angle.h:28
virtual void set_out_vector(std::vector< Buffer *> &out)
Set filtered data array.
Definition: filter.cpp:129
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:44
Merge multiple laser data arrays into one.
Definition: min_merge.h:34
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual float get_float(const char *path)=0
Get value from configuration which is of type float.
Laser720Interface Fawkes BlackBoard Interface.
virtual std::vector< Buffer * > & get_out_vector()
Get filtered data array.
Definition: filter.cpp:116
A barrier is a synchronization tool which blocks until a given number of threads have reached the bar...
Definition: barrier.h:32
virtual void loop()
Code to execute in the thread.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
Laser data filter.
Definition: filter.h:32
virtual void close(Interface *interface)=0
Close interface.