ControlThreadOutputBuffer.h
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package RobotAPI::RobotUnit
17  * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18  * @date 2017
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #pragma once
24 
25 #include <vector>
26 
27 // For unknown reasons, this needs to be included before expression exception.
28 // TODO: investigate
30 
31 // Remaining, normal includes.
36 
37 
38 #include "../ControlTargets/ControlTargetBase.h"
39 #include "../Devices/ControlDevice.h"
40 #include "../Devices/SensorDevice.h"
41 #include "../SensorValues/SensorValueBase.h"
43 #include "RtTiming.h"
44 
45 namespace armarx
46 {
47  class RobotUnit;
48  struct ControlThreadOutputBuffer;
49 } // namespace armarx
50 
52 {
53  class Logging;
54 }
55 
56 namespace armarx::detail
57 {
59  {
61  {
62  }
63 
64  virtual ~RtMessageLogEntryBase() = default;
65 
68  RtMessageLogEntryBase& deactivateSpamTag(std::uint64_t tag);
69 
70  void print(Ice::Int controlThreadId) const;
71  virtual std::size_t line() const = 0;
72  virtual std::string file() const = 0;
73  virtual std::string func() const = 0;
74  virtual std::string format() const = 0;
75  IceUtil::Time getTime() const;
76 
78  protected:
79  friend struct RtMessageLogBuffer;
80 
81  private:
83  float deactivateSpamSec{0};
84  bool printMsg{false};
85  std::uint64_t deactivateSpamTag_;
86  IceUtil::Time time;
87  };
88 
90  {
92 
93  protected:
94  std::string format() const final override;
95  std::size_t line() const final override;
96  std::string file() const final override;
97  std::string func() const final override;
98 
100  };
101 
103  {
105 
106  protected:
107  std::string
108  format() const final override
109  {
110  return "";
111  }
112 
113  std::size_t
114  line() const final override
115  {
116  return 0;
117  }
118 
119  std::string
120  file() const final override
121  {
122  return "";
123  }
124 
125  std::string
126  func() const final override
127  {
128  return "";
129  }
130 
132  };
133 
135  {
136  RtMessageLogBuffer(const RtMessageLogBuffer& other, bool minimize = false);
137  RtMessageLogBuffer(std::size_t bufferSize,
138  std::size_t numEntries,
139  std::size_t bufferMaxSize,
140  std::size_t bufferMaxNumberEntries);
142 
143  RtMessageLogBuffer() = delete;
145  RtMessageLogBuffer& operator=(RtMessageLogBuffer&&) = delete;
146  RtMessageLogBuffer& operator=(const RtMessageLogBuffer&) = delete;
147 
148  void reset(std::size_t& bufferSize, std::size_t& numEntries, std::size_t iterationCount);
149 
150  const std::vector<const RtMessageLogEntryBase*>& getEntries() const;
151 
152  std::size_t getMaximalBufferSize() const;
153  std::size_t getMaximalNumberOfBufferEntries() const;
154 
155  private:
156  void deleteAll();
157 
159  friend struct ::armarx::ControlThreadOutputBuffer;
160  friend class ::armarx::RobotUnit;
161  friend class ::armarx::RobotUnitModule::Logging;
162 
163  const std::size_t initialBufferSize;
164  const std::size_t initialBufferEntryNumbers;
165  const std::size_t bufferMaxSize;
166  const std::size_t bufferMaxNumberEntries;
167 
168  std::vector<std::uint8_t> buffer;
169  std::size_t bufferSpace;
170  void* bufferPlace;
171 
172  std::vector<const RtMessageLogEntryBase*> entries;
173  std::size_t entriesWritten{0};
174 
175  std::size_t requiredAdditionalBufferSpace{0};
176  std::size_t requiredAdditionalEntries{0};
177  std::size_t messagesLost{0};
178 
179  std::size_t maxAlign{1};
180  };
181 
183  {
184  //default ctors / ops
185 
187  const KeyValueVector<std::string, ControlDevicePtr>& controlDevices,
189  std::size_t messageBufferSize,
190  std::size_t messageBufferNumberEntries,
191  std::size_t messageBufferMaxSize,
192  std::size_t messageBufferMaxNumberEntries);
194  bool minimize = false);
195 
200 
201  std::size_t getDataBufferSize() const;
202 
203  //data
204  /// @brief Timestamp in wall time (never use the virtual time for this)
208  std::vector<PropagateConst<SensorValueBase*>> sensors;
209  std::vector<std::vector<PropagateConst<ControlTargetBase*>>> control;
210  std::size_t iteration{0};
211 
213 
214  private:
215  std::vector<std::uint8_t> buffer;
216  };
217 } // namespace armarx::detail
218 
219 namespace armarx
220 {
222  {
225  using ConsumerFunctor = std::function<void(const Entry&, std::size_t, std::size_t)>;
226  std::size_t initialize(std::size_t numEntries,
227  const KeyValueVector<std::string, ControlDevicePtr>& controlDevices,
229  std::size_t messageBufferSize,
230  std::size_t messageBufferNumberEntries,
231  std::size_t messageBufferMaxSize,
232  std::size_t messageBufferMaxNumberEntries);
233 
235 
238  {
239  return RtLoggingInstance;
240  }
241 
242  //write
243  Entry& getWriteBuffer();
244  void commitWrite();
245 
246  //read
247  const Entry& getReadBuffer() const;
248  bool updateReadBuffer() const;
249 
250  //logging read
251  void resetLoggingPosition() const;
252  void forEachNewLoggingEntry(ConsumerFunctor consumer);
253  void forLatestLoggingEntry(ConsumerFunctor consumer, size_t numberOfEntriesToLog);
254 
255  std::size_t getNumberOfBytes() const;
256 
257  template <class LoggingEntryT, class... Ts>
258  RtMessageLogEntryBase* addMessageToLog(Ts&&... args);
259 
260  private:
261  friend class RobotUnitModule::Logging; ///TODO change code to make this unnecessary
262  /// @brief this pointer is used for rt message logging and is not null in the control thread
263  static thread_local ControlThreadOutputBuffer* RtLoggingInstance;
264 
265  std::size_t toBounds(std::size_t idx) const;
266 
267  //settings and initialization:
268  bool isInitialized{false};
269  std::size_t numEntries{0};
270 
271  std::atomic_size_t writePosition{0};
272  mutable std::atomic_size_t onePastLoggingReadPosition{0};
273  mutable std::atomic_size_t onePastReadPosition{0};
274 
275  std::vector<Entry> entries;
276  std::vector<std::uint8_t> data;
277 
278  std::size_t messageBufferSize{0};
279  std::size_t messageBufferEntries{0};
280  };
281 
283 } // namespace armarx
284 
285 #define ARMARX_RT_LOGF(...) \
286  (*(_detail_ARMARX_RT_LOGF(__FILE__, ARMARX_FUNCTION, __LINE__, __VA_ARGS__, true)))
287 
288 #define _detail_ARMARX_RT_LOGF(file_, func_, line_, FormatString, ...) \
289  ( \
290  [&] \
291  { \
292  using namespace ::armarx; \
293  using RtMessageLogEntryBase = ControlThreadOutputBuffer::RtMessageLogEntryBase; \
294  struct RtMessageLogEntry : RtMessageLogEntryBase \
295  { \
296  using TupleT = decltype(std::make_tuple(__VA_ARGS__)); \
297  const TupleT tuple; \
298  ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER \
299  std::size_t \
300  line() const final override \
301  { \
302  return line_; \
303  } \
304  std::string \
305  file() const final override \
306  { \
307  return file_; \
308  } \
309  std::string \
310  func() const final override \
311  { \
312  return func_; \
313  } \
314  std::string \
315  format() const final override \
316  { \
317  return TupleToStringF<0, std::tuple_size<TupleT>::value - 1>(FormatString, \
318  tuple); \
319  } \
320  RtMessageLogEntry(TupleT tuple) : tuple{std::move(tuple)} \
321  { \
322  } \
323  RtMessageLogEntry(const RtMessageLogEntry&) = default; \
324  }; \
325  if (::armarx::ControlThreadOutputBuffer::GetRtLoggingInstance()) \
326  { \
327  return ::armarx::ControlThreadOutputBuffer::GetRtLoggingInstance() \
328  ->addMessageToLog<RtMessageLogEntry>(__VA_ARGS__); \
329  } \
330  else \
331  { \
332  *(loghelper(file_, line_, func_)) \
333  << "Redirected RT Logging:\n" \
334  << RtMessageLogEntry(std::make_tuple(__VA_ARGS__)).format(); \
335  return dynamic_cast<RtMessageLogEntryBase*>( \
336  &::armarx::detail::RtMessageLogEntryNull::Instance); \
337  } \
338  }())
339 
340 #define ARMARX_RT_LOGF_DEBUG(...) \
341  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::DEBUG)
342 #define ARMARX_RT_LOGF_VERBOSE(...) \
343  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::VERBOSE)
344 #define ARMARX_RT_LOGF_INFO(...) \
345  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::INFO)
346 #define ARMARX_RT_LOGF_IMPORTANT(...) \
347  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::IMPORTANT)
348 #define ARMARX_RT_LOGF_WARNING(...) \
349  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::WARN)
350 #define ARMARX_RT_LOGF_WARN(...) \
351  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::WARN)
352 #define ARMARX_RT_LOGF_ERROR(...) \
353  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::ERROR)
354 #define ARMARX_RT_LOGF_FATAL(...) \
355  ARMARX_RT_LOGF(__VA_ARGS__).setLoggingLevel(::armarx::MessageTypeT::FATAL)
356 
357 //impl
358 namespace armarx::detail
359 {
360  inline IceUtil::Time
362  {
363  return time;
364  }
365 
366  inline std::string
368  {
369  throw std::logic_error{"called 'format' of RtMessageLogEntryDummy"};
370  }
371 
372  inline std::size_t
374  {
375  throw std::logic_error{"called 'line' of RtMessageLogEntryDummy"};
376  }
377 
378  inline std::string
380  {
381  throw std::logic_error{"called 'file' of RtMessageLogEntryDummy"};
382  }
383 
384  inline std::string
386  {
387  throw std::logic_error{"called 'func' of RtMessageLogEntryDummy"};
388  }
389 
390  inline RtMessageLogBuffer::RtMessageLogBuffer(std::size_t bufferSize,
391  std::size_t numEntries,
392  std::size_t bufferMaxSize,
393  std::size_t bufferMaxNumberEntries) :
394  initialBufferSize{bufferSize},
395  initialBufferEntryNumbers{numEntries},
396  bufferMaxSize{bufferMaxSize},
397  bufferMaxNumberEntries{bufferMaxNumberEntries},
398  buffer(bufferSize, 0),
399  bufferSpace{buffer.size()},
400  bufferPlace{buffer.data()},
401  entries(numEntries, nullptr)
402  {
403  ARMARX_DEBUG << "RtMessageLogBuffer created with bufferSize=" << bufferSize
404  << " and numEntries=" << numEntries << " and bufferMaxSize=" << bufferMaxSize
405  << " and bufferMaxNumberEntries " << bufferMaxNumberEntries;
406  }
407 
409  {
410  deleteAll();
411  }
412 
413  inline const std::vector<const RtMessageLogEntryBase*>&
415  {
416  return entries;
417  }
418 
419  inline std::size_t
421  {
422  return bufferMaxSize;
423  }
424 
425  inline std::size_t
427  {
428  return bufferMaxNumberEntries;
429  }
430 
431  inline std::size_t
433  {
434  return buffer.size();
435  }
436 } // namespace armarx::detail
437 
438 namespace armarx
439 {
440  inline std::size_t
441  ControlThreadOutputBuffer::toBounds(std::size_t idx) const
442  {
443  return idx % numEntries;
444  }
445 
446  inline std::size_t
448  {
449  return data.size();
450  }
451 
452  template <class LoggingEntryT, class... Ts>
455  {
457  if (messages.entries.size() <= messages.entriesWritten)
458  {
459  ++messages.requiredAdditionalEntries;
460  ++messages.messagesLost;
462  }
463  messages.maxAlign = std::max(messages.maxAlign, alignof(LoggingEntryT));
464  void* place = std::align(alignof(LoggingEntryT),
465  sizeof(LoggingEntryT),
466  messages.bufferPlace,
467  messages.bufferSpace);
468  if (!place)
469  {
470  messages.requiredAdditionalBufferSpace +=
471  sizeof(LoggingEntryT) + alignof(LoggingEntryT) - 1;
472  ++messages.messagesLost;
474  }
475  RtMessageLogEntryBase* entry =
476  new (place) LoggingEntryT(std::make_tuple(std::forward<Ts>(args)...));
477  ARMARX_CHECK_NOT_NULL(entry);
478  messages.bufferPlace = static_cast<std::uint8_t*>(place) + sizeof(LoggingEntryT);
479 
480  messages.bufferSpace -= sizeof(LoggingEntryT);
481 
482  ARMARX_CHECK_IS_NULL(messages.entries.at(messages.entriesWritten));
483  messages.entries.at(messages.entriesWritten++) = entry;
484  return entry;
485  }
486 } // namespace armarx
armarx::detail::RtMessageLogBuffer::getMaximalBufferSize
std::size_t getMaximalBufferSize() const
Definition: ControlThreadOutputBuffer.h:420
armarx::detail::RtMessageLogEntryDummy::func
std::string func() const final override
Definition: ControlThreadOutputBuffer.h:385
armarx::MessageTypeT
MessageTypeT
Definition: LogSender.h:45
armarx::detail::RtMessageLogBuffer::~RtMessageLogBuffer
~RtMessageLogBuffer()
Definition: ControlThreadOutputBuffer.h:408
armarx::detail::RtMessageLogEntryBase::format
virtual std::string format() const =0
armarx::detail::RtMessageLogEntryBase::RtMessageLogBuffer
friend struct RtMessageLogBuffer
Definition: ControlThreadOutputBuffer.h:79
armarx::detail::RtMessageLogEntryDummy::line
std::size_t line() const final override
Definition: ControlThreadOutputBuffer.h:373
armarx::MessageTypeT::UNDEFINED
@ UNDEFINED
armarx::ControlThreadOutputBuffer::GetRtLoggingInstance
static ControlThreadOutputBuffer * GetRtLoggingInstance()
Definition: ControlThreadOutputBuffer.h:237
RtTiming.h
armarx::detail::RtMessageLogEntryNull::format
std::string format() const final override
Definition: ControlThreadOutputBuffer.h:108
armarx::detail::ControlThreadOutputBufferEntry::getDataBufferSize
std::size_t getDataBufferSize() const
Definition: ControlThreadOutputBuffer.h:432
armarx::detail::RtMessageLogEntryDummy::format
std::string format() const final override
Definition: ControlThreadOutputBuffer.h:367
armarx::detail::ControlThreadOutputBufferEntry::messages
RtMessageLogBuffer messages
Definition: ControlThreadOutputBuffer.h:212
ARMARX_CHECK_NOT_NULL
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
Definition: ExpressionException.h:206
ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER
#define ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER
Definition: HeterogenousContinuousContainerMacros.h:44
armarx::detail::ControlThreadOutputBufferEntry::sensors
std::vector< PropagateConst< SensorValueBase * > > sensors
Definition: ControlThreadOutputBuffer.h:208
armarx::detail::RtMessageLogEntryBase
Definition: ControlThreadOutputBuffer.h:58
HeterogenousContinuousContainer.h
ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER_BASE
#define ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER_BASE(CommonBaseType)
Definition: HeterogenousContinuousContainerMacros.h:28
armarx::detail::RtMessageLogEntryDummy
Definition: ControlThreadOutputBuffer.h:89
armarx::detail::RtMessageLogEntryDummy::Instance
static RtMessageLogEntryDummy Instance
Definition: ControlThreadOutputBuffer.h:91
armarx::detail::RtMessageLogEntryBase::deactivateSpam
RtMessageLogEntryBase & deactivateSpam(float sec)
Definition: ControlThreadOutputBuffer.cpp:247
armarx::detail::RtMessageLogEntryBase::deactivateSpamTag
RtMessageLogEntryBase & deactivateSpamTag(std::uint64_t tag)
Definition: ControlThreadOutputBuffer.cpp:255
armarx::detail::ControlThreadOutputBufferEntry::writeTimestamp
IceUtil::Time writeTimestamp
Timestamp in wall time (never use the virtual time for this)
Definition: ControlThreadOutputBuffer.h:205
armarx::SensorAndControl
detail::ControlThreadOutputBufferEntry SensorAndControl
Definition: NJointControllerBase.h:73
armarx::RobotUnitModule::Logging
This Module manages logging of data.
Definition: RobotUnitModuleLogging.h:120
armarx::detail::RtMessageLogBuffer
Definition: ControlThreadOutputBuffer.h:134
armarx::ControlThreadOutputBuffer
Definition: ControlThreadOutputBuffer.h:221
std::align
void * align(size_t alignment, size_t bytes, void *&bufferPlace, size_t &bufferSpace) noexcept
Definition: HeterogenousContinuousContainer.h:37
armarx::detail::RtMessageLogEntryNull
Definition: ControlThreadOutputBuffer.h:102
armarx::detail::ControlThreadOutputBufferEntry
Definition: ControlThreadOutputBuffer.h:182
ARMARX_CHECK_IS_NULL
#define ARMARX_CHECK_IS_NULL(ptr)
This macro evaluates whether ptr is null and if it turns out to be false it will throw an ExpressionE...
Definition: ExpressionException.h:194
armarx::detail::RtMessageLogBuffer::getEntries
const std::vector< const RtMessageLogEntryBase * > & getEntries() const
Definition: ControlThreadOutputBuffer.h:414
armarx::ControlThreadOutputBuffer::Entry
detail::ControlThreadOutputBufferEntry Entry
Definition: ControlThreadOutputBuffer.h:223
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
armarx::detail::RtMessageLogBuffer::RtMessageLogBuffer
RtMessageLogBuffer()=delete
armarx::detail::RtMessageLogEntryNull::Instance
static RtMessageLogEntryNull Instance
Definition: ControlThreadOutputBuffer.h:104
armarx::ControlThreadOutputBuffer::ConsumerFunctor
std::function< void(const Entry &, std::size_t, std::size_t)> ConsumerFunctor
Definition: ControlThreadOutputBuffer.h:225
armarx::detail::RtMessageLogBuffer::getMaximalNumberOfBufferEntries
std::size_t getMaximalNumberOfBufferEntries() const
Definition: ControlThreadOutputBuffer.h:426
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::detail
Definition: ApplicationNetworkStats.cpp:36
armarx::detail::RtMessageLogEntryBase::file
virtual std::string file() const =0
armarx::detail::RtMessageLogEntryDummy::file
std::string file() const final override
Definition: ControlThreadOutputBuffer.h:379
armarx::ControlThreadOutputBuffer::addMessageToLog
RtMessageLogEntryBase * addMessageToLog(Ts &&... args)
Definition: ControlThreadOutputBuffer.h:454
TripleBuffer.h
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
LoggingUtil.h
armarx::KeyValueVector< std::string, ControlDevicePtr >
ExpressionException.h
armarx::detail::RtMessageLogEntryBase::print
void print(Ice::Int controlThreadId) const
Definition: ControlThreadOutputBuffer.cpp:263
armarx::ControlThreadOutputBuffer::getWriteBuffer
Entry & getWriteBuffer()
Definition: ControlThreadOutputBuffer.cpp:34
armarx::detail::ControlThreadOutputBufferEntry::sensorValuesTimestamp
IceUtil::Time sensorValuesTimestamp
Definition: ControlThreadOutputBuffer.h:206
armarx::detail::RtMessageLogEntryNull::line
std::size_t line() const final override
Definition: ControlThreadOutputBuffer.h:114
armarx::detail::RtMessageLogEntryBase::~RtMessageLogEntryBase
virtual ~RtMessageLogEntryBase()=default
std
Definition: Application.h:66
armarx::detail::RtMessageLogEntryNull::file
std::string file() const final override
Definition: ControlThreadOutputBuffer.h:120
armarx::detail::RtMessageLogEntryBase::setLoggingLevel
RtMessageLogEntryBase & setLoggingLevel(MessageTypeT lvl)
Definition: ControlThreadOutputBuffer.cpp:239
armarx::detail::RtMessageLogEntryBase::func
virtual std::string func() const =0
armarx::detail::RtMessageLogEntryBase::getTime
IceUtil::Time getTime() const
Definition: ControlThreadOutputBuffer.h:361
armarx::VariantType::Int
const VariantTypeId Int
Definition: Variant.h:917
StringHelperTemplates.h
armarx::detail::RtMessageLogEntryBase::RtMessageLogEntryBase
RtMessageLogEntryBase()
Definition: ControlThreadOutputBuffer.h:60
armarx::detail::ControlThreadOutputBufferEntry::control
std::vector< std::vector< PropagateConst< ControlTargetBase * > > > control
Definition: ControlThreadOutputBuffer.h:209
armarx::RobotUnitModule
Definition: ControlDevice.h:34
armarx::detail::RtMessageLogEntryNull::func
std::string func() const final override
Definition: ControlThreadOutputBuffer.h:126
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::detail::ControlThreadOutputBufferEntry::timeSinceLastIteration
IceUtil::Time timeSinceLastIteration
Definition: ControlThreadOutputBuffer.h:207
armarx::ControlThreadOutputBuffer::getNumberOfBytes
std::size_t getNumberOfBytes() const
Definition: ControlThreadOutputBuffer.h:447
armarx::detail::RtMessageLogEntryBase::line
virtual std::size_t line() const =0
PropagateConst.h
armarx::rtNow
IceUtil::Time rtNow()
Definition: RtTiming.h:40