blob: e1a2cfcab9062f3f0aec4cc3a32aa774b9dff14f [file] [log] [blame]
Matt Spinlerd7abf362018-01-18 12:40:02 -06001#pragma once
2
3#include <deque>
4#include <tuple>
5#include <vector>
6
7namespace witherspoon
8{
9namespace power
10{
11namespace history
12{
13
14static constexpr auto recIDPos = 0;
15static constexpr auto recTimePos = 1;
16static constexpr auto recAvgPos = 2;
17static constexpr auto recMaxPos = 3;
18using Record = std::tuple<size_t, int64_t, int64_t, int64_t>;
19
20/**
Matt Spinler28fa1b62018-01-18 13:15:02 -060021 * @class InvalidRecordException
22 *
23 * The exception that is thrown when a raw history record
24 * cannot be parsed.
25 */
26class InvalidRecordException : public std::runtime_error
27{
28 public:
29
30 InvalidRecordException() :
31 std::runtime_error("Invalid history record")
32 {
33 }
34};
35
36/**
Matt Spinlerd7abf362018-01-18 12:40:02 -060037 * @class RecordManager
38 *
39 * This class manages the records for the input power history of
40 * a power supply.
41 *
42 * The history is the average and maximum power values across 30s
43 * intervals. Every 30s, a new record will be available from the
44 * PS. This class takes that raw PS data and converts it into
45 * something useable by D-Bus. It ensures the readings are always
46 * sorted newest to oldest, and prunes out the oldest entries when
47 * necessary. If there is a problem with the ordering IDs coming
48 * from the PS, it will clear out the old records and start over.
49 */
50class RecordManager
51{
52 public:
53
Matt Spinler28fa1b62018-01-18 13:15:02 -060054 static constexpr auto RAW_RECORD_SIZE = 5;
55 static constexpr auto RAW_RECORD_ID_OFFSET = 0;
Matt Spinler8168d282018-01-18 13:24:06 -060056 static constexpr auto FIRST_SEQUENCE_ID = 0;
Matt Spinlerd7abf362018-01-18 12:40:02 -060057 static constexpr auto LAST_SEQUENCE_ID = 0xFF;
58
59 using DBusRecord = std::tuple<uint64_t, int64_t>;
60 using DBusRecordList = std::vector<DBusRecord>;
61
62 RecordManager() = delete;
63 ~RecordManager() = default;
64 RecordManager(const RecordManager&) = default;
65 RecordManager& operator=(const RecordManager&) = default;
66 RecordManager(RecordManager&&) = default;
67 RecordManager& operator=(RecordManager&&) = default;
68
69 /**
70 * @brief Constructor
71 *
72 * @param[in] maxRec - the maximum number of history
73 * records to keep at a time
74 */
75 RecordManager(size_t maxRec) :
76 RecordManager(maxRec, LAST_SEQUENCE_ID)
77 {
78 }
79
80 /**
81 * @brief Constructor
82 *
83 * @param[in] maxRec - the maximum number of history
84 * records to keep at a time
85 * @param[in] lastSequenceID - the last sequence ID the power supply
86 * will use before starting over
87 */
88 RecordManager(size_t maxRec, size_t lastSequenceID) :
89 maxRecords(maxRec),
90 lastSequenceID(lastSequenceID)
91 {
92 }
93
94 /**
Matt Spinler8168d282018-01-18 13:24:06 -060095 * @brief Adds a new entry to the history
96 *
97 * Also checks to see if the old history should be
98 * cleared, such as when there is an invalid record
99 * sequence ID or if there was no data from the PS.
100 *
101 * @param[in] rawRecord - the record data straight
102 * from the power supply
103 *
104 * @return bool - If there has been a change to the
105 * history records that needs to be
106 * reflected in D-Bus.
107 */
108 bool add(const std::vector<uint8_t>& rawRecord);
109
110 /**
Matt Spinlere710d182018-01-18 13:06:17 -0600111 * @brief Converts a Linear Format power number to an integer
112 *
113 * The PMBus spec describes a 2 byte Linear Format
114 * number that is composed of an exponent and mantissa
115 * in two's complement notation.
116 *
117 * Value = Mantissa * 2**Exponent
118 *
119 * @return int64_t the converted value
120 */
121 static int64_t linearToInteger(uint16_t data);
122
123 /**
Matt Spinlerd7abf362018-01-18 12:40:02 -0600124 * @brief Returns the number of records
125 *
126 * @return size_t - the number of records
127 *
128 */
129 inline size_t getNumRecords() const
130 {
131 return records.size();
132 }
133
134 /**
135 * @brief Deletes all records
136 */
137 inline void clear()
138 {
139 records.clear();
140 }
141
142 private:
143
144 /**
Matt Spinler28fa1b62018-01-18 13:15:02 -0600145 * @brief returns the sequence ID from a raw history record
146 *
147 * Throws InvalidRecordException if the data is the wrong length.
148 *
149 * @param[in] data - the raw record data as the PS returns it
150 *
151 * @return size_t - the ID from byte 0
152 */
153 size_t getRawRecordID(const std::vector<uint8_t>& data) const;
154
155 /**
156 * @brief Creates an instance of a Record from the raw PS data
157 *
158 * @param[in] data - the raw record data as the PS returns it
159 *
160 * @return Record - A filled in Record instance
161 */
162 Record createRecord(const std::vector<uint8_t>& data);
163
164 /**
Matt Spinlerd7abf362018-01-18 12:40:02 -0600165 * @brief The maximum number of entries to keep in the history.
166 *
167 * When a new record is added, the oldest one will be removed.
168 */
169 const size_t maxRecords;
170
171 /**
172 * @brief The last ID the power supply returns before rolling over
173 * back to the first ID of 0.
174 */
175 const size_t lastSequenceID;
176
177 /**
178 * @brief The list of timestamp/average/maximum records.
179 * Newer records are added to the front, and older ones
180 * removed from the back.
181 */
182 std::deque<Record> records;
183};
184
185}
186}
187}