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
43#include "RtTiming.h"
44
45namespace armarx
46{
47 class RobotUnit;
49} // namespace armarx
50
52{
53 class Logging;
54}
55
56namespace armarx::detail
57{
59 {
61 {
62 }
63
64 virtual ~RtMessageLogEntryBase() = default;
65
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
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
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)
205 IceUtil::Time writeTimestamp;
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
219namespace 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,
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
244 void commitWrite();
245
246 //read
247 const Entry& getReadBuffer() const;
248 bool updateReadBuffer() const;
249
250 //logging read
251 void resetLoggingPosition() const;
253 void forLatestLoggingEntry(ConsumerFunctor consumer, size_t numberOfEntriesToLog);
254
255 std::size_t getNumberOfBytes() const;
256
257 template <class LoggingEntryT, class... Ts>
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
358namespace 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
438namespace 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)...));
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
#define ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER
#define ARMARX_MINIMAL_PLACEMENT_CONSTRUCTION_HELPER_BASE(CommonBaseType)
This class is pretty much similar to a map.
This Module manages logging of data.
The RobotUnit class manages a robot and its controllers.
Definition RobotUnit.h:192
#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...
#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...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Time rtNow()
Definition RtTiming.h:40
MessageTypeT
Definition LogSender.h:46
detail::ControlThreadOutputBufferEntry SensorAndControl
This file is part of ArmarX.
void * align(size_t alignment, size_t bytes, void *&bufferPlace, size_t &bufferSpace) noexcept
std::size_t initialize(std::size_t numEntries, const KeyValueVector< std::string, ControlDevicePtr > &controlDevices, const KeyValueVector< std::string, SensorDevicePtr > &sensorDevices, std::size_t messageBufferSize, std::size_t messageBufferNumberEntries, std::size_t messageBufferMaxSize, std::size_t messageBufferMaxNumberEntries)
void forEachNewLoggingEntry(ConsumerFunctor consumer)
detail::RtMessageLogEntryBase RtMessageLogEntryBase
void forLatestLoggingEntry(ConsumerFunctor consumer, size_t numberOfEntriesToLog)
std::function< void(const Entry &, std::size_t, std::size_t)> ConsumerFunctor
static ControlThreadOutputBuffer * GetRtLoggingInstance()
detail::ControlThreadOutputBufferEntry Entry
RtMessageLogEntryBase * addMessageToLog(Ts &&... args)
IceUtil::Time writeTimestamp
Timestamp in wall time (never use the virtual time for this)
ControlThreadOutputBufferEntry & operator=(const ControlThreadOutputBufferEntry &)=delete
ControlThreadOutputBufferEntry(const KeyValueVector< std::string, ControlDevicePtr > &controlDevices, const KeyValueVector< std::string, SensorDevicePtr > &sensorDevices, std::size_t messageBufferSize, std::size_t messageBufferNumberEntries, std::size_t messageBufferMaxSize, std::size_t messageBufferMaxNumberEntries)
std::vector< PropagateConst< SensorValueBase * > > sensors
ControlThreadOutputBufferEntry(ControlThreadOutputBufferEntry &&)=delete
ControlThreadOutputBufferEntry & operator=(ControlThreadOutputBufferEntry &&)=delete
const std::vector< const RtMessageLogEntryBase * > & getEntries() const
RtMessageLogBuffer & operator=(RtMessageLogBuffer &&)=delete
RtMessageLogBuffer & operator=(const RtMessageLogBuffer &)=delete
RtMessageLogBuffer(RtMessageLogBuffer &&)=delete
void reset(std::size_t &bufferSize, std::size_t &numEntries, std::size_t iterationCount)
RtMessageLogBuffer(const RtMessageLogBuffer &other, bool minimize=false)
RtMessageLogEntryBase & setLoggingLevel(MessageTypeT lvl)
void print(Ice::Int controlThreadId) const
virtual std::string func() const =0
RtMessageLogEntryBase & deactivateSpamTag(std::uint64_t tag)
virtual std::string file() const =0
virtual std::string format() const =0
virtual std::size_t line() const =0
RtMessageLogEntryBase & deactivateSpam(float sec)
std::size_t line() const final override
std::string func() const final override
std::string format() const final override
std::string file() const final override
std::size_t line() const final override
std::string func() const final override
std::string format() const final override
std::string file() const final override