blob: 3e69a71a059ce67f1b1ab2238df4f1f2daa7f19f [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
44 void set_mutable_vector(
45 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector) {
46 vector_ = vector;
47 }
48
49 void InvalidateBuffer() { vector_ = nullptr; }
50
51 void IncrementError(Error error) {
52 CHECK_NOTNULL(vector_);
53 DCHECK_LT(static_cast<size_t>(error), vector_->size());
54 Count *counter = vector_->GetMutableObject(static_cast<size_t>(error));
55 counter->mutate_count(counter->count() + 1);
56 }
57
58 // Sets all the error counts to zero.
59 void ResetCounts() {
60 CHECK_NOTNULL(vector_);
61 DCHECK_EQ(vector_->size(), kNumErrors) << this << " vector " << vector_;
62 for (size_t ii = 0; ii < kNumErrors; ++ii) {
63 vector_->GetMutableObject(ii)->mutate_count(0);
64 }
65 }
66
67 private:
68 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector_ = nullptr;
69};
James Kuszmaulea1e8302023-02-20 16:19:53 -080070
71// The ArrayErrorCounter serves the same purpose as the ErrorCounter class,
72// except that:
73// (a) It owns its own memory, rather than modifying a flatbuffer in-place.
74// (b) Because of this, the user has greater flexibility in choosing when to
75// reset the error counters.
76template <typename Error, typename Count>
77class ArrayErrorCounter {
78 public:
79 static constexpr size_t kNumErrors = ErrorCounter<Error, Count>::kNumErrors;
80 ArrayErrorCounter() { ResetCounts(); }
81
82 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
James Kuszmaulfb894572023-02-23 17:25:06 -080083 PopulateCounts(flatbuffers::FlatBufferBuilder *fbb) const {
James Kuszmaulea1e8302023-02-20 16:19:53 -080084 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
85 offset = ErrorCounter<Error, Count>::Initialize(fbb);
86 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector =
87 flatbuffers::GetMutableTemporaryPointer(*fbb, offset);
88 for (size_t ii = 0; ii < kNumErrors; ++ii) {
89 vector->GetMutableObject(ii)->mutate_count(error_counts_.at(ii));
90 }
91 return offset;
92 }
93
94 void IncrementError(Error error) {
95 DCHECK_LT(static_cast<size_t>(error), error_counts_.size());
96 error_counts_.at(static_cast<size_t>(error))++;
97 }
98
99 // Sets all the error counts to zero.
100 void ResetCounts() { error_counts_.fill(0); }
101
102 private:
103 std::array<size_t, kNumErrors> error_counts_;
104};
James Kuszmaulcc94ed42022-08-24 11:36:17 -0700105} // namespace aos::util
106#endif // AOS_UTIL_ERROR_COUNTER_H_