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 * @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 * @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 */ 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 */ 141 inline size_t getNumRecords() const 142 { 143 return records.size(); 144 } 145 146 /** 147 * @brief Deletes all records 148 */ 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