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