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