blob: 7837f9e0a1a62de6eaee151c9f0af11b450b282b [file] [log] [blame]
James Kuszmaulcc94ed42022-08-24 11:36:17 -07001#ifndef AOS_UTIL_ERROR_COUNTER_H_
2#define AOS_UTIL_ERROR_COUNTER_H_
Stephan Pleinesb1177672024-05-27 17:48:32 -07003#include <stddef.h>
4
5#include <array>
6
7#include "flatbuffers/buffer.h"
8#include "flatbuffers/flatbuffer_builder.h"
9#include "flatbuffers/vector.h"
James Kuszmaulcc94ed42022-08-24 11:36:17 -070010#include "glog/logging.h"
11
12namespace aos::util {
13// Class to manage simple error counters for flatbuffer status message.
14// This presumes that you have a flatbuffer enum type Error which has
15// enum values that are continuous and start at zero. These are then
16// counted by a Count flatbuffer table that is of the format:
17// table Count {
18// error:Error (id: 0);
19// count:uint (id: 1);
20// }
21// And which is stored as a vector in the resulting status message,
22// where the index within the vector corresponds with the underlying
23// value of the enum.
24template <typename Error, typename Count>
25class ErrorCounter {
26 public:
27 static constexpr size_t kNumErrors =
28 static_cast<int>(Error::MAX) - static_cast<int>(Error::MIN) + 1;
29 static_assert(0 == static_cast<int>(Error::MIN),
30 "Expected Error enum values to start at zero.");
31 // TODO(james): Is there any good way to check that the values are contiguous?
32 // There's no Error::COUNT, and the method I previously used (checking the
33 // size of the return type of EnumValues*()) requires the user to pass that
34 // method as a template argument.
35 ErrorCounter() = default;
36 static flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
37 Initialize(flatbuffers::FlatBufferBuilder *fbb) {
38 std::array<flatbuffers::Offset<Count>, kNumErrors> count_offsets;
39 for (size_t ii = 0; ii < kNumErrors; ++ii) {
40 typename Count::Builder builder(*fbb);
41 builder.add_error(static_cast<Error>(ii));
42 builder.add_count(0);
43 count_offsets[ii] = builder.Finish();
44 }
45 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
46 offset = fbb->CreateVector(count_offsets.data(), count_offsets.size());
47 return offset;
48 }
49
James Kuszmaulbfb57052024-02-21 17:14:51 -080050 template <typename Static>
51 static void InitializeStaticFbs(Static *builder) {
52 CHECK(builder->reserve(kNumErrors));
53 for (size_t ii = 0; ii < kNumErrors; ++ii) {
Austin Schuh6bdcc372024-06-27 14:49:11 -070054 auto element = builder->emplace_back();
55 CHECK(element != nullptr);
James Kuszmaulbfb57052024-02-21 17:14:51 -080056 element->set_error(static_cast<Error>(ii));
57 element->set_count(0);
58 }
59 }
60
James Kuszmaulcc94ed42022-08-24 11:36:17 -070061 void set_mutable_vector(
62 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector) {
63 vector_ = vector;
64 }
65
66 void InvalidateBuffer() { vector_ = nullptr; }
67
68 void IncrementError(Error error) {
Austin Schuh6bdcc372024-06-27 14:49:11 -070069 CHECK(vector_ != nullptr);
James Kuszmaulcc94ed42022-08-24 11:36:17 -070070 DCHECK_LT(static_cast<size_t>(error), vector_->size());
71 Count *counter = vector_->GetMutableObject(static_cast<size_t>(error));
72 counter->mutate_count(counter->count() + 1);
73 }
74
75 // Sets all the error counts to zero.
76 void ResetCounts() {
Austin Schuh6bdcc372024-06-27 14:49:11 -070077 CHECK(vector_ != nullptr);
James Kuszmaulcc94ed42022-08-24 11:36:17 -070078 DCHECK_EQ(vector_->size(), kNumErrors) << this << " vector " << vector_;
79 for (size_t ii = 0; ii < kNumErrors; ++ii) {
80 vector_->GetMutableObject(ii)->mutate_count(0);
81 }
82 }
83
84 private:
85 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector_ = nullptr;
86};
James Kuszmaulea1e8302023-02-20 16:19:53 -080087
88// The ArrayErrorCounter serves the same purpose as the ErrorCounter class,
89// except that:
90// (a) It owns its own memory, rather than modifying a flatbuffer in-place.
91// (b) Because of this, the user has greater flexibility in choosing when to
92// reset the error counters.
93template <typename Error, typename Count>
94class ArrayErrorCounter {
95 public:
96 static constexpr size_t kNumErrors = ErrorCounter<Error, Count>::kNumErrors;
97 ArrayErrorCounter() { ResetCounts(); }
98
99 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
James Kuszmaulfb894572023-02-23 17:25:06 -0800100 PopulateCounts(flatbuffers::FlatBufferBuilder *fbb) const {
James Kuszmaulea1e8302023-02-20 16:19:53 -0800101 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
102 offset = ErrorCounter<Error, Count>::Initialize(fbb);
103 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector =
104 flatbuffers::GetMutableTemporaryPointer(*fbb, offset);
105 for (size_t ii = 0; ii < kNumErrors; ++ii) {
106 vector->GetMutableObject(ii)->mutate_count(error_counts_.at(ii));
107 }
108 return offset;
109 }
110
James Kuszmaulbfb57052024-02-21 17:14:51 -0800111 template <typename Static>
112 void PopulateCountsStaticFbs(Static *builder) const {
113 ErrorCounter<Error, Count>::InitializeStaticFbs(builder);
114 for (size_t ii = 0; ii < kNumErrors; ++ii) {
115 builder->at(ii).set_count(error_counts_.at(ii));
116 }
117 }
118
James Kuszmaulea1e8302023-02-20 16:19:53 -0800119 void IncrementError(Error error) {
120 DCHECK_LT(static_cast<size_t>(error), error_counts_.size());
121 error_counts_.at(static_cast<size_t>(error))++;
122 }
123
124 // Sets all the error counts to zero.
125 void ResetCounts() { error_counts_.fill(0); }
126
127 private:
128 std::array<size_t, kNumErrors> error_counts_;
129};
James Kuszmaulcc94ed42022-08-24 11:36:17 -0700130} // namespace aos::util
131#endif // AOS_UTIL_ERROR_COUNTER_H_