blob: 98382c7499db2ab1c742ef1cffff4305194de989 [file] [log] [blame]
#pragma once
#include <cstdint>
#include <deque>
#include <stdexcept>
#include <tuple>
#include <vector>
namespace witherspoon
{
namespace power
{
namespace history
{
static constexpr auto recIDPos = 0;
static constexpr auto recTimePos = 1;
static constexpr auto recAvgPos = 2;
static constexpr auto recMaxPos = 3;
using Record = std::tuple<size_t, int64_t, int64_t, int64_t>;
/**
* @class InvalidRecordException
*
* The exception that is thrown when a raw history record
* cannot be parsed.
*/
class InvalidRecordException : public std::runtime_error
{
public:
InvalidRecordException() : std::runtime_error("Invalid history record")
{}
};
/**
* @class RecordManager
*
* This class manages the records for the input power history of
* a power supply.
*
* The history is the average and maximum power values across 30s
* intervals. Every 30s, a new record will be available from the
* PS. This class takes that raw PS data and converts it into
* something usable by D-Bus. It ensures the readings are always
* sorted newest to oldest, and prunes out the oldest entries when
* necessary. If there is a problem with the ordering IDs coming
* from the PS, it will clear out the old records and start over.
*/
class RecordManager
{
public:
static constexpr auto RAW_RECORD_SIZE = 5;
static constexpr auto RAW_RECORD_ID_OFFSET = 0;
static constexpr auto FIRST_SEQUENCE_ID = 0;
static constexpr auto LAST_SEQUENCE_ID = 0xFF;
using DBusRecord = std::tuple<uint64_t, int64_t>;
using DBusRecordList = std::vector<DBusRecord>;
RecordManager() = delete;
~RecordManager() = default;
RecordManager(const RecordManager&) = default;
RecordManager& operator=(const RecordManager&) = default;
RecordManager(RecordManager&&) = default;
RecordManager& operator=(RecordManager&&) = default;
/**
* @brief Constructor
*
* @param[in] maxRec - the maximum number of history
* records to keep at a time
*/
RecordManager(size_t maxRec) : RecordManager(maxRec, LAST_SEQUENCE_ID)
{}
/**
* @brief Constructor
*
* @param[in] maxRec - the maximum number of history
* records to keep at a time
* @param[in] lastSequenceID - the last sequence ID the power supply
* will use before starting over
*/
RecordManager(size_t maxRec, size_t lastSequenceID) :
maxRecords(maxRec), lastSequenceID(lastSequenceID)
{}
/**
* @brief Adds a new entry to the history
*
* Also checks to see if the old history should be
* cleared, such as when there is an invalid record
* sequence ID or if there was no data from the PS.
*
* @param[in] rawRecord - the record data straight
* from the power supply
*
* @return bool - If there has been a change to the
* history records that needs to be
* reflected in D-Bus.
*/
bool add(const std::vector<uint8_t>& rawRecord);
/**
* @brief Returns the history of average input power
* in a representation used by D-Bus.
*
* @return DBusRecordList - A list of averages with
* a timestamp for each entry.
*/
DBusRecordList getAverageRecords();
/**
* @brief Returns the history of maximum input power
* in a representation used by D-Bus.
*
* @return DBusRecordList - A list of maximums with
* a timestamp for each entry.
*/
DBusRecordList getMaximumRecords();
/**
* @brief Converts a Linear Format power number to an integer
*
* The PMBus spec describes a 2 byte Linear Format
* number that is composed of an exponent and mantissa
* in two's complement notation.
*
* Value = Mantissa * 2**Exponent
*
* @return int64_t the converted value
*/
static int64_t linearToInteger(uint16_t data);
/**
* @brief Returns the number of records
*
* @return size_t - the number of records
*
*/
inline size_t getNumRecords() const
{
return records.size();
}
/**
* @brief Deletes all records
*/
inline void clear()
{
records.clear();
}
private:
/**
* @brief returns the sequence ID from a raw history record
*
* Throws InvalidRecordException if the data is the wrong length.
*
* @param[in] data - the raw record data as the PS returns it
*
* @return size_t - the ID from byte 0
*/
size_t getRawRecordID(const std::vector<uint8_t>& data) const;
/**
* @brief Creates an instance of a Record from the raw PS data
*
* @param[in] data - the raw record data as the PS returns it
*
* @return Record - A filled in Record instance
*/
Record createRecord(const std::vector<uint8_t>& data);
/**
* @brief The maximum number of entries to keep in the history.
*
* When a new record is added, the oldest one will be removed.
*/
const size_t maxRecords;
/**
* @brief The last ID the power supply returns before rolling over
* back to the first ID of 0.
*/
const size_t lastSequenceID;
/**
* @brief The list of timestamp/average/maximum records.
* Newer records are added to the front, and older ones
* removed from the back.
*/
std::deque<Record> records;
};
} // namespace history
} // namespace power
} // namespace witherspoon