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