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