ErrorReporting.h
Go to the documentation of this file.
1#pragma once
2
3#include <atomic>
4#include <condition_variable>
5#include <filesystem>
6#include <fstream>
7#include <map>
8#include <memory>
9#include <mutex>
10#include <optional>
11#include <thread>
12
15
16#include "SlaveIdentifier.h"
17
18/**
19 * @defgroup Namespace-error error
20 * @ingroup Library-ethercat
21 * A description of the namespace error.
22 */
24{
25 /**
26 * @enum Type
27 * @ingroup Namespace-error
28 *
29 * @brief The Type enum
30 */
31 enum class Type
32 {
33 Bus, //!< Bussdfnödf
34 Slave, //!< sdfluieb,dj
37 };
38
39 std::ostream& operator<<(std::ostream& stream, const Type& rhs);
40
41 /**
42 * @enum Severity
43 * @ingroup Namespace-error
44 *
45 * @brief The Severity enum
46 */
55
56 std::ostream& operator<<(std::ostream& stream, const Severity& rhs);
57
58 class Reporter;
59 class Reporting;
60
61 /**
62 * @class Entry
63 * @ingroup Namespace-error
64 * @brief Brief description of class Entry.
65 *
66 * Detailed description of class Entry.
67 */
68 class Entry
69 {
70 static constexpr std::uint16_t DEVICE_NAME_BUFFER_SIZE = 64;
71
72 static constexpr std::uint16_t MESSAGE_BUFFER_SIZE = 1024;
73 static constexpr std::uint16_t IDENTIFIER_BUFFER_SIZE = 128;
74 static constexpr char OVERFLOW_MESSAGE[] = {"[ERROR MESSAGE TOO LONG]"};
75 static_assert(MESSAGE_BUFFER_SIZE > sizeof(OVERFLOW_MESSAGE) + 1);
76
77 static constexpr std::uint16_t FILE_STRING_BUFFER_SIZE = 256;
78 static constexpr std::uint16_t FUNCTION_STRING_BUFFER_SIZE = 256;
79
80 friend Reporter;
81 friend Reporting;
82
83 char m_message[MESSAGE_BUFFER_SIZE];
84 std::size_t m_message_size = 0;
85 char m_identifier[IDENTIFIER_BUFFER_SIZE];
86 std::size_t m_identifier_size = 0;
87 Type m_type;
88 SlaveIdentifier m_sid;
89 char m_device_name[DEVICE_NAME_BUFFER_SIZE];
90 Severity m_severity;
91 IceUtil::Time m_timestamp;
92 std::uint64_t m_bus_iteration_number = 0;
93 float m_deactivate_spam_seconds = 0;
94
95 struct MetaInfo
96 {
97 char file[FILE_STRING_BUFFER_SIZE];
98 int line;
99 char function[FUNCTION_STRING_BUFFER_SIZE];
100 };
101
102 MetaInfo m_metaInfo;
103
104 public:
105 Entry() = default;
106 ~Entry() = default;
107
108 template <typename... T>
109 Entry&
110 message(const char* fmt, T... args)
111 {
112#pragma GCC diagnostic ignored "-Wformat-security"
113 int sz = std::snprintf(m_message, sizeof(m_message), fmt, args...);
114 if (sz >= static_cast<int>(sizeof(m_message)))
115 {
116 std::snprintf(m_message + MESSAGE_BUFFER_SIZE - sizeof(OVERFLOW_MESSAGE),
117 sizeof(OVERFLOW_MESSAGE),
118 OVERFLOW_MESSAGE);
119 m_message_size = MESSAGE_BUFFER_SIZE;
120 }
121 else
122 {
123 m_message_size = static_cast<std::size_t>(sz);
124 }
125#pragma GCC diagnostic pop
126 return *this;
127 }
128
129 template <typename... T>
130 Entry&
131 identifier(const char* fmt, T... args)
132 {
133#pragma GCC diagnostic ignored "-Wformat-security"
134 int sz = std::snprintf(m_identifier, sizeof(m_identifier), fmt, args...);
135 if (sz >= static_cast<int>(sizeof(m_identifier)))
136 {
137 std::snprintf(m_identifier + IDENTIFIER_BUFFER_SIZE - sizeof(OVERFLOW_MESSAGE),
138 sizeof(OVERFLOW_MESSAGE),
139 OVERFLOW_MESSAGE);
140 m_identifier_size = MESSAGE_BUFFER_SIZE;
141 }
142 else
143 {
144 m_identifier_size = static_cast<std::size_t>(sz);
145 }
146#pragma GCC diagnostic pop
147 return *this;
148 }
149
151 Entry& errorType(Type type);
152 Entry& metaInfo(const char* file, int line, const char* function);
153 Entry& deviceName(const char* deviceName);
154 Entry& busIterationNumber(std::uint64_t iteration);
155
156 std::string toString() const;
157 std::string getIdentifier() const;
158 };
159
160 /**
161 * @class Reporter
162 * @ingroup Namespace-error
163 * @brief Brief description of class Reporter.
164 *
165 * Detailed description of class Reporter.
166 */
167 class Reporter
168 {
169 friend Reporting;
170
171 class ReportWrapper
172 {
173 public:
174 ReportWrapper(Entry& error, Reporter* reporter);
175 ~ReportWrapper();
176 ReportWrapper(const ReportWrapper&) = delete; // Copying not allowed
177 ReportWrapper& operator=(const ReportWrapper&) = delete; // Assignment not allowed
178
179 ReportWrapper& deactivateSpam(float seconds);
180
181 template <typename... T>
182 ReportWrapper&
183 identifier(const char* fmt, T... args)
184 {
185 m_error.identifier(fmt, args...);
186 return *this;
187 }
188
189 private:
190 Entry& m_error;
191 Reporter* m_reporter;
192 };
193
194 public:
195 ReportWrapper reportDebug(Entry& error);
196 ReportWrapper reportInfo(Entry& error);
197 ReportWrapper reportWarning(Entry& error);
198 ReportWrapper reportError(Entry& error);
199 [[noreturn]] void reportErrorAndThrow(Entry& error);
200 bool hasErrors() const;
201
202 private:
203 Reporter() = delete;
204 Reporter(Reporting* errorReporting);
205 Reporting* m_errorReporting;
206 unsigned int m_errorCount = 0;
207 };
208
209 /**
210 * @class Reporting
211 * @ingroup Namespace-error
212 * @brief Brief description of class Reporting.
213 *
214 * Detailed description of class Reporting.
215 */
216 class Reporting : virtual public Logging
217 {
218 public:
219 static Reporting& getErrorReporting();
220
223 void report(Entry&& error);
224
225 void setMaxErrorHistorySize(std::size_t maxErrorHistorySize);
226
227 std::optional<std::filesystem::path> dumpToFile(std::filesystem::path file,
228 std::uint64_t lastNSeconds = 1000000);
229
230 private:
231 Reporting(std::size_t maxErrorHistorySize = 1000);
232 ~Reporting() override;
233
234 std::vector<Entry> m_errors;
235
236 struct QueueImpl;
238
239 struct ErrorTimestamp
240 {
241 std::int64_t timestamp;
242 std::uint16_t subid;
243
244 bool
245 operator<(const ErrorTimestamp& x) const
246 {
247 return std::tie(this->timestamp, this->subid) < std::tie(x.timestamp, x.subid);
248 }
249 };
250
251 std::map<ErrorTimestamp, Entry> m_errorHistory;
252 std::size_t m_maxErrorHistorySize;
253
254 std::thread m_errorProcessingThread;
255 std::atomic_bool m_errorProcessingRunning{true};
256 void errorProcessingLoop();
257 std::condition_variable m_errorProcessingCondition;
258 std::mutex m_errorProcessingConditionMutex;
259
260 IceUtil::Time m_timestampReportingStart;
261 };
262
263} // namespace armarx::control::ethercat::reporting
264
266{
270
271
272////////////////////////////////////////////////////////////////////////////////////////////////////
273#define _detail_GENERAL_REPORT_CONSTRUCTION(...) \
274 armarx::control::ethercat::ReportingEntry() \
275 .errorType(armarx::control::ethercat::ReportingType::General) \
276 .metaInfo(__FILE__, __LINE__, ARMARX_FUNCTION) \
277 .message(__VA_ARGS__)
278
279#define GENERAL_DEBUG(...) \
280 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportDebug( \
281 _detail_GENERAL_REPORT_CONSTRUCTION(__VA_ARGS__))
282#define GENERAL_INFO(...) \
283 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportInfo( \
284 _detail_GENERAL_REPORT_CONSTRUCTION(__VA_ARGS__))
285#define GENERAL_WARNING(...) \
286 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportWarning( \
287 _detail_GENERAL_REPORT_CONSTRUCTION(__VA_ARGS__))
288#define GENERAL_ERROR(...) \
289 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportError( \
290 _detail_GENERAL_REPORT_CONSTRUCTION(__VA_ARGS__))
291
292
293////////////////////////////////////////////////////////////////////////////////////////////////////
294#define _detail_BUS_REPORT_CONSTRUCTION(bin, ...) \
295 armarx::control::ethercat::ReportingEntry() \
296 .errorType(armarx::control::ethercat::ReportingType::Bus) \
297 .metaInfo(__FILE__, __LINE__, ARMARX_FUNCTION) \
298 .message(__VA_ARGS__) \
299 .busIterationNumber(bin)
300
301#define BUS_DEBUG(bin, ...) \
302 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportDebug( \
303 _detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
304#define BUS_INFO(bin, ...) \
305 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportInfo( \
306 _detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
307#define BUS_WARNING(bin, ...) \
308 armarx::control::ethercat::reporting::Reporting::getGlobalErrorReporter().reportWarning( \
309 _detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
310#define BUS_ERROR(bin, ...) \
311 armarx::control::ethercat::reporting::Reporting::getGlobalErrorReporter().reportError( \
312 _detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
313#define BUS_FATAL_AND_THROW(bin, ...) \
314 armarx::control::ethercat::reporting::Reporting::getGlobalErrorReporter().reportErrorAndThrow( \
315 _detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
316
317#define BUS_WARNING_LOCAL(reporter, bin, ...) \
318 (reporter).reportWarning(_detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
319#define BUS_ERROR_LOCAL(reporter, bin, ...) \
320 (reporter).reportError(_detail_BUS_REPORT_CONSTRUCTION(bin, __VA_ARGS__))
321
322////////////////////////////////////////////////////////////////////////////////////////////////////
323#define _detail_SLAVE_REPORT_CONSTRUCTION(_sid, ...) \
324 armarx::control::ethercat::ReportingEntry() \
325 .errorType(armarx::control::ethercat::ReportingType::Slave) \
326 .metaInfo(__FILE__, __LINE__, ARMARX_FUNCTION) \
327 .message(__VA_ARGS__) \
328 .slaveIdentifier(_sid)
329
330#define SLAVE_DEBUG(sid, ...) \
331 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportDebug( \
332 _detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
333#define SLAVE_INFO(sid, ...) \
334 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportInfo( \
335 _detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
336#define SLAVE_WARNING(sid, ...) \
337 armarx::control::ethercat::reporting::Reporting::getGlobalErrorReporter().reportWarning( \
338 _detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
339#define SLAVE_ERROR(sid, ...) \
340 armarx::control::ethercat::reporting::Reporting::getGlobalErrorReporter().reportError( \
341 _detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
342#define SLAVE_FATAL_AND_THROW(sid, ...) \
343 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportErrorAndThrow( \
344 _detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
345
346#define SLAVE_WARNING_LOCAL(reporter, sid, ...) \
347 (reporter).reportWarning(_detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
348#define SLAVE_ERROR_LOCAL(reporter, sid, ...) \
349 (reporter).reportError(_detail_SLAVE_REPORT_CONSTRUCTION(sid, __VA_ARGS__))
350
351////////////////////////////////////////////////////////////////////////////////////////////////////
352#define _detail_DEVICE_REPORT_CONSTRUCTION(devName, ...) \
353 armarx::control::ethercat::ReportingEntry() \
354 .errorType(armarx::control::ethercat::ReportingType::Device) \
355 .metaInfo(__FILE__, __LINE__, ARMARX_FUNCTION) \
356 .message(__VA_ARGS__) \
357 .deviceName(devName)
358
359#define DEVICE_INFO(deviceName, ...) \
360 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportInfo( \
361 _detail_DEVICE_REPORT_CONSTRUCTION(deviceName, __VA_ARGS__))
362#define DEVICE_WARNING(deviceName, ...) \
363 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportWarning( \
364 _detail_DEVICE_REPORT_CONSTRUCTION(deviceName, __VA_ARGS__))
365#define DEVICE_ERROR(deviceName, ...) \
366 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportError( \
367 _detail_DEVICE_REPORT_CONSTRUCTION(deviceName, __VA_ARGS__))
368#define DEVICE_FATAL_AND_THROW(deviceName, ...) \
369 armarx::control::ethercat::Reporting::getGlobalErrorReporter().reportErrorAndThrow( \
370 _detail_DEVICE_REPORT_CONSTRUCTION(deviceName, __VA_ARGS__))
371
372#define DEVICE_WARNING_LOCAL(reporter, deviceName, ...) \
373 (reporter).reportWarning(_detail_DEVICE_REPORT_CONSTRUCTION(deviceName, __VA_ARGS__))
374#define DEVICE_ERROR_LOCAL(reporter, deviceName, ...) \
375 (reporter).reportError(_detail_DEVICE_REPORT_CONSTRUCTION(deviceName, __VA_ARGS__))
376
377
378} // namespace armarx::control::ethercat
std::string timestamp()
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
Wrapper for a pointer to propagate const to the pointed to value.
The SlaveIdentifier class is a POD-type representing a unique set of values identifying an EtherCAT s...
Brief description of class Entry.
Entry & slaveIdentifier(SlaveIdentifier sid)
Entry & message(const char *fmt, T... args)
Entry & busIterationNumber(std::uint64_t iteration)
Entry & deviceName(const char *deviceName)
Entry & metaInfo(const char *file, int line, const char *function)
Entry & identifier(const char *fmt, T... args)
Brief description of class Reporter.
Brief description of class Reporting.
std::optional< std::filesystem::path > dumpToFile(std::filesystem::path file, std::uint64_t lastNSeconds=1000000)
void setMaxErrorHistorySize(std::size_t maxErrorHistorySize)
std::ostream & operator<<(std::ostream &stream, const Type &rhs)
reporting::Reporting Reporting
reporting::Entry ReportingEntry
reporting::Type ReportingType
This file offers overloads of toIce() and fromIce() functions for STL container types.
bool operator<(const RemoteHandle< PrxTA > &fst, const RemoteHandle< PrxTB > &snd)