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