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