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