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