blob: e08c67e512c17786394a44e42653b85541da1730 [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) {
54 auto element = CHECK_NOTNULL(builder->emplace_back());
55 element->set_error(static_cast<Error>(ii));
56 element->set_count(0);
57 }
58 }
59
James Kuszmaulcc94ed42022-08-24 11:36:17 -070060 void set_mutable_vector(
61 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector) {
62 vector_ = vector;
63 }
64
65 void InvalidateBuffer() { vector_ = nullptr; }
66
67 void IncrementError(Error error) {
68 CHECK_NOTNULL(vector_);
69 DCHECK_LT(static_cast<size_t>(error), vector_->size());
70 Count *counter = vector_->GetMutableObject(static_cast<size_t>(error));
71 counter->mutate_count(counter->count() + 1);
72 }
73
74 // Sets all the error counts to zero.
75 void ResetCounts() {
76 CHECK_NOTNULL(vector_);
77 DCHECK_EQ(vector_->size(), kNumErrors) << this << " vector " << vector_;
78 for (size_t ii = 0; ii < kNumErrors; ++ii) {
79 vector_->GetMutableObject(ii)->mutate_count(0);
80 }
81 }
82
83 private:
84 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector_ = nullptr;
85};
James Kuszmaulea1e8302023-02-20 16:19:53 -080086
87// The ArrayErrorCounter serves the same purpose as the ErrorCounter class,
88// except that:
89// (a) It owns its own memory, rather than modifying a flatbuffer in-place.
90// (b) Because of this, the user has greater flexibility in choosing when to
91// reset the error counters.
92template <typename Error, typename Count>
93class ArrayErrorCounter {
94 public:
95 static constexpr size_t kNumErrors = ErrorCounter<Error, Count>::kNumErrors;
96 ArrayErrorCounter() { ResetCounts(); }
97
98 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
James Kuszmaulfb894572023-02-23 17:25:06 -080099 PopulateCounts(flatbuffers::FlatBufferBuilder *fbb) const {
James Kuszmaulea1e8302023-02-20 16:19:53 -0800100 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Count>>>
101 offset = ErrorCounter<Error, Count>::Initialize(fbb);
102 flatbuffers::Vector<flatbuffers::Offset<Count>> *vector =
103 flatbuffers::GetMutableTemporaryPointer(*fbb, offset);
104 for (size_t ii = 0; ii < kNumErrors; ++ii) {
105 vector->GetMutableObject(ii)->mutate_count(error_counts_.at(ii));
106 }
107 return offset;
108 }
109
James Kuszmaulbfb57052024-02-21 17:14:51 -0800110 template <typename Static>
111 void PopulateCountsStaticFbs(Static *builder) const {
112 ErrorCounter<Error, Count>::InitializeStaticFbs(builder);
113 for (size_t ii = 0; ii < kNumErrors; ++ii) {
114 builder->at(ii).set_count(error_counts_.at(ii));
115 }
116 }
117
James Kuszmaulea1e8302023-02-20 16:19:53 -0800118 void IncrementError(Error error) {
119 DCHECK_LT(static_cast<size_t>(error), error_counts_.size());
120 error_counts_.at(static_cast<size_t>(error))++;
121 }
122
123 // Sets all the error counts to zero.
124 void ResetCounts() { error_counts_.fill(0); }
125
126 private:
127 std::array<size_t, kNumErrors> error_counts_;
128};
James Kuszmaulcc94ed42022-08-24 11:36:17 -0700129} // namespace aos::util
130#endif // AOS_UTIL_ERROR_COUNTER_H_