MIRA
ChannelSynchronizer.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Contact: info@mira-project.org
9  *
10  * Commercial Usage:
11  * Licensees holding valid commercial licenses may use this file in
12  * accordance with the commercial license agreement provided with the
13  * software or, alternatively, in accordance with the terms contained in
14  * a written agreement between you and MLAB or NICR.
15  *
16  * GNU General Public License Usage:
17  * Alternatively, this file may be used under the terms of the GNU
18  * General Public License version 3.0 as published by the Free Software
19  * Foundation and appearing in the file LICENSE.GPL3 included in the
20  * packaging of this file. Please review the following information to
21  * ensure the GNU General Public License version 3.0 requirements will be
22  * met: http://www.gnu.org/copyleft/gpl.html.
23  * Alternatively you may (at your option) use any later version of the GNU
24  * General Public License if such license has been publicly approved by
25  * MLAB and NICR (or its successors, if any).
26  *
27  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
28  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
29  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
30  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
33  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
34  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
35  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
36  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
37  */
38 
47 #pragma once
48 
49 #include <tuple>
50 
51 #include <fw/Framework.h>
52 
53 // Needed for bind_with_variadic_arguments, see inside ChannelSynchronizer class
54 // See: https://stackoverflow.com/questions/21192659/variadic-templates-and-stdbind#21193316
55 template<int> // begin with 0 here!
57 {
58 };
59 
60 // Needs to be in namespace std!!
61 namespace std {
62 
64 
65 template<int N>
66 struct is_placeholder<placeholder_template<N>>: integral_constant<int, N+1> // the 1 is important
67 {
68 };
69 
71 
72 } // namespace std
73 
74 
75 namespace mira {
76 
78 
80 {
81 public:
83  {
86  };
87 };
88 
89 template <typename ...Ts>
91 {
92 
93 private:
94 
95  // Just some helper functions. Will be evaluated at compile time (compile time recursion).
96  template<int C, typename A, typename... As>
97  void subscribeToChannelHelper(Authority& authority, const Duration& t)
98  {
99  std::get<C>(this->mChannels) = authority.subscribe(this->mChannelIDs[C], &ChannelSynchronizer::channelCallback<C, A>, this, t);
100 
101  // recursion
102  subscribeToChannelHelper<C+1, As...>(authority, t);
103  }
104 
106  template<int C>
107  void subscribeToChannelHelper(Authority& authority, const Duration& t)
108  {
109  }
110 
111  template<int C, typename A, typename... As>
112  bool isValidHelper() const
113  {
114  if(!std::get<C>(this->mChannelReads).isValid())
115  {
116  return false;
117  }
118 
119  return isValidHelper<C + 1, As...>();
120  }
121 
122  template<int C>
123  bool isValidHelper() const
124  {
125  return true;
126  }
127 
128  template<int C, typename A, typename... As>
129  bool waitForPublisherHelper(const Duration& timeout ) const
130  {
131  if(!std::get<C>(this->mChannels).waitForPublisher(timeout))
132  {
133  return false;
134  }
135 
136  return waitForPublisherHelper<C + 1, As...>(timeout);
137  }
138 
139  template<int C>
140  bool waitForPublisherHelper(const Duration& timeout) const
141  {
142  return true;
143  }
144 
145 
146  template<int C, typename A, typename... As>
147  void unsubscribeHelper(Authority& authority) const
148  {
149  authority.unsubscribe<A>(std::get<C>(this->mChannels).getID());
150  unsubscribeHelper<C + 1, As...>(authority);
151  }
152 
153  template<int C>
154  void unsubscribeHelper(Authority& authority) const
155  {
156  }
157 
158  template<int C, typename A, typename... As>
159  bool getNearestChannelReadObjectsOfAllChannels(
160  const int& idOfChannelWhoseCallbackGotCalledFirst,
161  std::tuple<ChannelRead<Ts>...>& tmpChannelReads,
162  const Time& timestamp,
163  const uint32_t sequenceID = 0)
164  {
165  if(C == idOfChannelWhoseCallbackGotCalledFirst)
166  {
167  // We do not get a ChannelRead object for this ID,
168  // as the callback of the channel / type with this ID
169  // was called prior to this function.
170  return getNearestChannelReadObjectsOfAllChannels<C + 1, As...>(idOfChannelWhoseCallbackGotCalledFirst, tmpChannelReads, timestamp, sequenceID);
171  }
172 
173  try
174  {
175  if(this->mSynchronizationMode == SynchronizationMode::BY_TIMESTAMP)
176  {
177  std::get<C>(tmpChannelReads) = std::get<C>(this->mChannels).read(timestamp, this->mTolerance);
178  }
179  else if(this->mSynchronizationMode == SynchronizationMode::BY_SEQUENCE_ID)
180  {
181  std::get<C>(tmpChannelReads) = std::get<C>(this->mChannels).read(sequenceID, this->mTolerance);
182  }
183  }
184  catch (mira::Exception& ex)
185  {
186  // std::cout << ex.getInfo().message << "\n";
187  return false;
188  }
189  return getNearestChannelReadObjectsOfAllChannels<C + 1, As...>(idOfChannelWhoseCallbackGotCalledFirst, tmpChannelReads, timestamp, sequenceID);
190  }
191 
192  template<int C>
193  bool getNearestChannelReadObjectsOfAllChannels(
194  const int& idOfChannelWhoseCallbackGotCalledFirst,
195  std::tuple<ChannelRead<Ts>...>& tmpChannelReads,
196  const Time& timestamp,
197  const uint32_t sequenceID = 0)
198  {
199  return true;
200  }
201 
202  template<int C, typename A, typename... As>
203  bool checkForNewTimestampsAndUpdate(std::tuple<ChannelRead<Ts>...>& tmpChannelReads)
204  {
205  ChannelRead<A>& channelRead = std::get<C>(tmpChannelReads);
206  if(!channelRead.isValid())
207  {
208  return false;
209  }
210 
211  const Time& newTimestamp = channelRead.getTimestamp();
212  if(this->mLastTimes[C] == newTimestamp)
213  {
214  return false;
215  }
216  this->mLastTimes[C] = newTimestamp;
217 
218  return checkForNewTimestampsAndUpdate<C + 1, As...>(tmpChannelReads);
219  }
220 
221  template<int C>
222  bool checkForNewTimestampsAndUpdate(std::tuple<ChannelRead<Ts>...>& tmpChannelReads)
223  {
224  return true;
225  }
226 
227  template<int C, typename A, typename... As>
228  bool getOldestTimestampOfChannelReadsHelper(Time& oldestTimestamp)
229  {
230  const Time& timestamp = std::get<C>(this->mChannels).read().getTimestamp();
231  if(timestamp < oldestTimestamp)
232  {
233  // Get copy and save in oldestTimestamp.
234  oldestTimestamp = timestamp;
235  }
236 
237  return getOldestTimestampOfChannelReadsHelper<C + 1, As...>(oldestTimestamp);
238  }
239 
240  template<int C>
241  bool getOldestTimestampOfChannelReadsHelper(Time& oldestTimestamp)
242  {
243  return true;
244  }
245 
246  template<int C, typename A, typename... As>
247  bool getOldestSequenceIDOfChannelReadsHelper(uint32_t& oldestSequenceID)
248  {
249  const uint32_t sequenceID = std::get<C>(this->mChannels).read()->sequenceID;
250  if(sequenceID < oldestSequenceID)
251  {
252  oldestSequenceID = sequenceID;
253  }
254 
255 
256  return getOldestSequenceIDOfChannelReadsHelper<C + 1, As...>(oldestSequenceID);
257  }
258 
259  template<int C>
260  bool getOldestSequenceIDOfChannelReadsHelper(uint32_t& oldestSequenceID)
261  {
262  return true;
263  }
264 
301  template<typename A>
302  struct StringArgumentHelper
303  {
304  StringArgumentHelper() {}
305  StringArgumentHelper(const std::string& str) : string(str) {}
306  StringArgumentHelper(const char* cStr) : string(cStr) {}
307 
308  std::string string;
309  };
310 
311  template<std::size_t I = 0, std::size_t end, typename... Tp>
312  inline typename std::enable_if<I >= end, void>::type
313  stringArgumentHelperUnpack(std::tuple<Tp...>& t, std::vector<std::string>& resultVector) { }
314 
315  template<std::size_t I = 0, std::size_t end, typename... Tp>
317  stringArgumentHelperUnpack(std::tuple<Tp...>& t, std::vector<std::string>& resultVector)
318  {
319  auto d = std::get<I>(t);
320  resultVector.push_back(d.string);
321  stringArgumentHelperUnpack<I + 1, end, Tp...>(t, resultVector);
322  }
323 
324  // C++11 style of unpacking a tuple to individual variables.
325  // See: https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer
326  // #######################################
327  template<int ...>
328  struct sequence { };
329 
330  template<int N, int ...S>
331  struct generateIntegerSequence : generateIntegerSequence<N-1, N-1, S...> { };
332 
333  template<int ...S>
334  struct generateIntegerSequence<0, S...> {
335  typedef sequence<S...> type;
336  };
337 
338  template<int ...S>
339  void upCallFunctionHelper(sequence<S...>)
340  {
341  // S will be <0, 1, 2, ..> depending on the number of template parameters.
342  // If we have 4 template parameters, for example, the following would look like this:
343  // this->mFunction(std::get<0, 1, 2, 3>(this->mChannelReads));
344  // Pretty as hell, isn't it?
345  this->mFunction(std::get<S>(this->mChannelReads)...);
346  }
347 
348  template<int SeqLength = sizeof...(Ts)>
349  void upCallFunctionHelper()
350  {
351  // Do the sequence generation internally
352  upCallFunctionHelper(typename generateIntegerSequence<SeqLength>::type());
353  }
354 
355  // bind_with_variadic_arguments (also please check at the beginning of this
356  // file for definition of a specialized std is_placeholder type_trait).
357  template<class Class, class... Args, int... Is>
358  std::function<void (Args...)> bind_with_variadic_placeholders(void (Class::*p)(Args...), Class* obj, sequence<Is...>)
359  {
360  // std::function<void (Args...)> y = std::bind(p, obj, boost::placeholders::_1, boost::placeholders::_2);
361  std::function<void (Args...)> x = std::bind(p, obj, placeholder_template<Is>{}...);
362  return x;
363  }
364 
365  template<class Class, class... Args>
366  std::function<void (Args...)> bind_with_variadic_placeholders(void (Class::*p)(Args...), Class* obj)
367  {
368  return bind_with_variadic_placeholders(p, obj, typename generateIntegerSequence<sizeof...(Args)>::type());
369  }
370 
371 
372  template<int C, typename A>
373  void channelCallback(mira::ChannelRead<A> channelRead)
374  {
375  std::tuple<ChannelRead<Ts>...> tmpChannelReads;
376  std::get<C>(tmpChannelReads) = channelRead;
377 
378  // From all other channels, obtain a ChannelRead object from the slot
379  // at the specified timestamp/sequence id. See mira::ChannelRead::read() documentation for more information.
380  // If no slot with the exact timestamp exists (most of the time there will be no such slot) a slot will be chosen according to the query mode (see SlotQueryMode).
381  // In this case, SlotQueryMode will be NEAREST_SLOT.
382  if(!getNearestChannelReadObjectsOfAllChannels<0, Ts...>(C, tmpChannelReads, channelRead.getTimestamp(), channelRead->sequenceID))
383  return;
384 
385  this->call(tmpChannelReads);
386  }
387 
388  void call(std::tuple<ChannelRead<Ts>...>& tmpChannelReads)
389  {
390  // Check if the new ChannelReads have not been received before:
391  // We compare the timestamps of all ChannelReads to the last known
392  // time stamps for each channel.
393  // This is necessary, as tmpChannelReads was filled using the read function
394  // (manual read, as only one of the ChannelReads in tmpChannelReads was
395  // received by a callback), so it is possible that we get the same
396  // ChannelRead object twice (for example if one channel publishes fast
397  // and another one quite slow).
398  if(!checkForNewTimestampsAndUpdate<0, Ts...>(tmpChannelReads))
399  {
400  // Not all channels have new data.
401  return;
402  }
403 
404  // mLastTimes has been updated already in checkForNewTimestampsAndUpdate(...), so no
405  // need to update it here like in the old ChannelSynchronizer code.
406  this->mChannelReads = tmpChannelReads;
407 
408  // Call callback, if specified.
409  if (this->mFunction)
410  {
411  // upCallFunctionHelper will unpack the tuple of channel reads and pass the values
412  // to mFunction.
413  upCallFunctionHelper();
414  }
415  }
416 
417 public:
418 
420  // Initialize our mLastTimes vector with the number of elements needed.
421  : mLastTimes(std::vector<Time>(sizeof...(Ts), Time::unixEpoch())),
422  mSynchronizationMode(BY_TIMESTAMP)
423  {
424  assert(sizeof...(Ts) > 0 && "Number of template arguments of ChannelSynchronizer has to be > 0!");
425  }
426 
431  void unsubscribe(Authority& authority)\
432  {
433  unsubscribeHelper<0, Ts...>(authority);
434  }
435 
444  void subscribe(Authority& authority,
445  StringArgumentHelper<Ts>... channelIDs,
446  const Duration& t = Duration::milliseconds(100))
447  {
448  std::tuple<StringArgumentHelper<Ts>...> tuple(channelIDs...);
449  this->mChannelIDs.clear();
450 
451  stringArgumentHelperUnpack<0, sizeof...(Ts), StringArgumentHelper<Ts>...>(tuple, this->mChannelIDs);
452  this->mFunction = NULL;
453 
454  this->mTolerance = t;
455 
456  subscribeToChannelHelper<0, Ts...>(authority, t);
457  }
458 
467  void subscribe(Authority& authority,
468  StringArgumentHelper<Ts>... channelIDs,
469  std::function<void (ChannelRead<Ts>...)> fn,
470  const Duration& t = Duration::milliseconds(100))
471  {
472  std::tuple<StringArgumentHelper<Ts>...> tuple(channelIDs...);
473 
474  std::vector<std::string> tmpChannelIDs;
475  stringArgumentHelperUnpack<0, sizeof...(Ts), StringArgumentHelper<Ts>...>(tuple, tmpChannelIDs);
476 
477  subscribe(authority, tmpChannelIDs, fn, t);
478  }
479 
483  void subscribe(Authority& authority,
484  const std::vector<std::string>& channelIDs,
485  std::function<void (ChannelRead<Ts>...)> fn,
486  const Duration& t = Duration::milliseconds(100))
487  {
488  this->mChannelIDs = channelIDs;
489  this->mFunction = fn;
490  this->mTolerance = t;
491 
492  subscribeToChannelHelper<0, Ts...>(authority, t);
493  }
494 
498  template<typename Class>
499  void subscribe(Authority& authority,
500  StringArgumentHelper<Ts>... channelIDs,
501  void (Class::*f)(ChannelRead<Ts>...),
502  Class* obj,
503  const Duration& t = Duration::milliseconds(100))
504  {
505  std::tuple<StringArgumentHelper<Ts>...> tuple(channelIDs...);
506 
507  std::vector<std::string> tmpChannelIDs;
508  stringArgumentHelperUnpack<0, sizeof...(Ts), StringArgumentHelper<Ts>...>(tuple, tmpChannelIDs);
509 
510  subscribe(authority, tmpChannelIDs, f, obj, t);
511  }
512 
516  template<typename Class>
517  void subscribe(Authority& authority,
518  std::vector<std::string> channelIDs,
519  void (Class::*f)(ChannelRead<Ts>...),
520  Class* obj,
521  const Duration& t = Duration::milliseconds(100))
522  {
523  std::function<void (ChannelRead<Ts>...)> upcallFunction = bind_with_variadic_placeholders<Class, ChannelRead<Ts>...>(f, obj);
524  subscribe(authority, channelIDs, upcallFunction, t);
525  }
526 
533  std::tuple<ChannelRead<Ts>... > read()
534  {
535  return this->mChannelReads;
536  }
537 
541  bool isValid() const
542  {
543  return isValidHelper<0, Ts...>();
544  }
545 
552  std::tuple< ChannelRead<Ts>... > waitForData(const Duration& timeout = Duration::infinity())
553  {
554  Time end;
555  if(!timeout.isValid() || timeout.isInfinity())
556  end = Time::eternity();
557  else
558  end = Time::now() + timeout;
559 
560  while(!boost::this_thread::interruption_requested())
561  {
562  if(this->mSynchronizationMode == ChannelSynchronizerBase::SynchronizationMode::BY_TIMESTAMP)
563  {
564  Time oldestTimestamp = Time::now();
565  try
566  {
567  if(getOldestTimestampOfChannelReadsHelper<0, Ts...>(oldestTimestamp))
568  {
569  std::tuple<ChannelRead<Ts>...> tmpChannelReads;
570  if(getNearestChannelReadObjectsOfAllChannels<0, Ts...>(-1, tmpChannelReads, oldestTimestamp))
571  {
572  this->mChannelReads = tmpChannelReads;
573  return read();
574  }
575  }
576  }
577  catch(XInvalidRead& ex) {}
578 
579 
580  }
581  else
582  {
583  uint32_t oldestSequenceID = std::numeric_limits<uint32_t>::max();
584  try
585  {
586  if(getOldestSequenceIDOfChannelReadsHelper<0, Ts...>(oldestSequenceID))
587  {
588  std::tuple<ChannelRead<Ts>...> tmpChannelReads;
589 
590  if(getNearestChannelReadObjectsOfAllChannels<0, Ts...>(-1, tmpChannelReads, Time(), oldestSequenceID))
591  {
592  this->mChannelReads = tmpChannelReads;
593  return read();
594  }
595  }
596  }
597  catch(XInvalidRead& ex) {}
598  }
599  if(Time::now() > end) /* handle timeout */
600  break;
601  MIRA_SLEEP(50);
602 
603  }
604 
605  return this->mChannelReads;
606  }
607 
608  bool waitForPublisher(const Duration& timeout = Duration::infinity()) const
609  {
610  return waitForPublisherHelper<0, Ts...>(timeout);
611  }
612 
613  void setSynchronizationMode(const SynchronizationMode& synchronizationMode)
614  {
615  this->mSynchronizationMode = synchronizationMode;
616  }
617 
618 private:
619 
620  std::tuple<Channel<Ts>...> mChannels;
621  std::tuple<ChannelRead<Ts>...> mChannelReads;
622  std::vector<std::string> mChannelIDs;
623  std::vector<Time> mLastTimes;
624 
625  Duration mTolerance;
626  SynchronizationMode mSynchronizationMode;
627  std::function<void (ChannelRead<Ts>...)> mFunction;
628 };
629 
631 
632 } // namespace mira
633 
634 // For backwards compatibility.
635 // Old ChannelSynchronizers were named ChannelSynchronizer3 for 3 types,
636 // ChannelSynchronizer4 for 4 types etc.
637 // This is not necessary anymore, but as we want to provide backwards compatibility,
638 // we generate ChannelSynchronizer3 to ChannelSynchronizer10 as separate classes.
639 // They don't have to be used and for future projects, you should just ChannelSynchronizer.
640 #define MIRA_FW_INTERNAL_NUMBER(z, n, data) \
641  BOOST_PP_COMMA_IF(n) BOOST_PP_CAT(data,n)
642 
643 #define MIRA_CHANNEL_SYNCHRONIZER( TNAME, TNUM ) \
644  template < \
645  BOOST_PP_REPEAT(TNUM,MIRA_FW_INTERNAL_NUMBER,typename type) \
646  > class TNAME : public ChannelSynchronizer<BOOST_PP_REPEAT(TNUM,MIRA_FW_INTERNAL_NUMBER,type)>\
647  {\
648  };
649 
650 #define MIRA_FW_GEN_CHANNEL_SYNCHRONIZER( z, n, data) \
651  MIRA_CHANNEL_SYNCHRONIZER(BOOST_PP_CAT(ChannelSynchronizer,n), n)\
652 
653 namespace mira {
654 
656 
657 // Generate predefined channel synchronizer for 3 to 9 channels.
658 // Only for backwards compatibility! Not necessary!
659 BOOST_PP_REPEAT_FROM_TO(3,10,MIRA_FW_GEN_CHANNEL_SYNCHRONIZER,)
660 
661 
663 } // namespace mira
664 
665 #undef MIRA_FW_INTERNAL_NUMBER
666 #undef MIRA_CHANNEL_SYNCHRONIZER
667 #undef MIRA_FW_GEN_CHANNEL_SYNCHRONIZER
void subscribe(Authority &authority, StringArgumentHelper< Ts >... channelIDs, void(Class::*f)(ChannelRead< Ts >...), Class *obj, const Duration &t=Duration::milliseconds(100))
Same as above but with a function and object pointer.
Definition: ChannelSynchronizer.h:499
void subscribe(Authority &authority, StringArgumentHelper< Ts >... channelIDs, std::function< void(ChannelRead< Ts >...)> fn, const Duration &t=Duration::milliseconds(100))
Call this instead of Authority::subscribe()
Definition: ChannelSynchronizer.h:467
Definition: ChannelSynchronizer.h:84
void subscribe(Authority &authority, const std::vector< std::string > &channelIDs, std::function< void(ChannelRead< Ts >...)> fn, const Duration &t=Duration::milliseconds(100))
Same as above but with the channel IDs as std::vector<std::string>.
Definition: ChannelSynchronizer.h:483
tick_type milliseconds() const
Returns normalized number of milliseconds (0..999)
Definition: Time.h:288
Definition: ChannelSynchronizer.h:90
bool isValid() const
Checks if this duration is invalid.
Definition: Time.h:260
specialize cv::DataType for our ImgPixel and inherit from cv::DataType<Vec>
Definition: IOService.h:67
bool isValid() const
Return true if all ChannelRead objects contain valid data.
Definition: ChannelSynchronizer.h:541
Class object which supports some kind of class reflection.
Definition: Class.h:97
Definition: ChannelSynchronizer.h:85
STL namespace.
void unsubscribe(const std::string &channelID)
Unsubscribe from a given channel.
SynchronizationMode
Definition: ChannelSynchronizer.h:82
Definition: ChannelSynchronizer.h:79
An object that allows read access to data of a channel.
Definition: ChannelReadWrite.h:435
Wrapper class for boost::posix_time::ptime for adding more functionality to it.
Definition: Time.h:421
#define MIRA_FW_GEN_CHANNEL_SYNCHRONIZER(z, n, data)
Definition: ChannelSynchronizer.h:650
bool isValid() const
Returns true, if data was assigned to the ChannelRead or ChannelWrite and if this data is locked...
Definition: ChannelReadWrite.h:183
PropertyHint type(const std::string &t)
Sets the attribute "type" to the specified value.
Definition: PropertyHint.h:295
Use this class to represent time durations.
Definition: Time.h:104
Authorities act as a facade to the framework.
Definition: Authority.h:93
std::tuple< ChannelRead< Ts >... > read()
Return (synchronized) ChannelRead objects.
Definition: ChannelSynchronizer.h:533
ChannelSynchronizer()
Definition: ChannelSynchronizer.h:419
Definition: ChannelSynchronizer.h:56
void subscribe(Authority &authority, std::vector< std::string > channelIDs, void(Class::*f)(ChannelRead< Ts >...), Class *obj, const Duration &t=Duration::milliseconds(100))
Same as above but with a function and object pointer.
Definition: ChannelSynchronizer.h:517
Base class for exceptions.
Definition: Exception.h:194
static Duration infinity()
Returns a special duration time representing positive infinity.
Definition: Time.h:245
bool isInfinity() const
Checks if this duration is infinity.
Definition: Time.h:267
static Time now() static Time eternity()
Returns the current utc based time.
Definition: Time.h:484
std::tuple< ChannelRead< Ts >... > waitForData(const Duration &timeout=Duration::infinity())
Return the latest (synchronized) element once it is available.
Definition: ChannelSynchronizer.h:552
void subscribe(Authority &authority, StringArgumentHelper< Ts >... channelIDs, const Duration &t=Duration::milliseconds(100))
Call this instead of Authority::subscribe() Function provided for convenience (if no callback functio...
Definition: ChannelSynchronizer.h:444
void setSynchronizationMode(const SynchronizationMode &synchronizationMode)
Definition: ChannelSynchronizer.h:613
#define MIRA_SLEEP(ms)
Sleeps for ms milliseconds This is a thread interruption point - if interruption of the current threa...
Definition: Thread.h:95
The framework that holds all manager classes and provides startup and shutdown of all framework relat...
Channel< T > subscribe(const std::string &channelID, const Duration &storageDuration=Duration::seconds(0))
Subscribes authority to a given channel.
bool waitForPublisher(const Duration &timeout=Duration::infinity()) const
Definition: ChannelSynchronizer.h:608
void unsubscribe(Authority &authority)
Call this instead of Authority::unsubscribe(...) to unsubscribe all the channels of the synchronizer...
Definition: ChannelSynchronizer.h:431
const Time & getTimestamp() const
Definition: ChannelReadWrite.h:203