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