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