/** * Copyright © 2017 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "record_manager.hpp" #include #include #include namespace witherspoon { namespace power { namespace history { using namespace phosphor::logging; bool RecordManager::add(const std::vector& rawRecord) { if (rawRecord.size() == 0) { // The PS has no data - either the power supply just started up, // or it just got a SYNC. Clear the history. records.clear(); return true; } try { // Peek at the ID to see if more processing is needed. auto id = getRawRecordID(rawRecord); if (!records.empty()) { auto previousID = std::get(records.front()); // Already have this record. Done. if (previousID == id) { return false; } // Check that the sequence ID is in order. // If not, clear out current list. if ((previousID + 1) != id) { // If it just rolled over from 0xFF to 0x00, then no // need to clear. If we see a 0 seemingly out of nowhere, // then it was a sync so clear the old records. auto rolledOver = (previousID == lastSequenceID) && (id == FIRST_SEQUENCE_ID); if (!rolledOver) { if (id != FIRST_SEQUENCE_ID) { log( "Noncontiguous INPUT_HISTORY sequence ID " "found. Clearing old entries", entry("OLD_ID=%ld", previousID), entry("NEW_ID=%ld", id)); } records.clear(); } } } records.push_front(std::move(createRecord(rawRecord))); // If no more should be stored, prune the oldest if (records.size() > maxRecords) { records.pop_back(); } } catch (InvalidRecordException& e) { return false; } return true; } auto RecordManager::getAverageRecords() -> DBusRecordList { DBusRecordList list; for (const auto& r : records) { list.emplace_back(std::get(r), std::get(r)); } return list; } auto RecordManager::getMaximumRecords() -> DBusRecordList { DBusRecordList list; for (const auto& r : records) { list.emplace_back(std::get(r), std::get(r)); } return list; } size_t RecordManager::getRawRecordID(const std::vector& data) const { if (data.size() != RAW_RECORD_SIZE) { log("Invalid INPUT_HISTORY size", entry("SIZE=%d", data.size())); throw InvalidRecordException{}; } return data[RAW_RECORD_ID_OFFSET]; } Record RecordManager::createRecord(const std::vector& data) { // The raw record format is: // 0xAABBCCDDEE // // where: // 0xAA = sequence ID // 0xBBCC = average power in linear format (0xCC = MSB) // 0xDDEE = maximum power in linear format (0xEE = MSB) auto id = getRawRecordID(data); auto time = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); auto val = static_cast(data[2]) << 8 | data[1]; auto averagePower = linearToInteger(val); val = static_cast(data[4]) << 8 | data[3]; auto maxPower = linearToInteger(val); return Record{id, time, averagePower, maxPower}; } int64_t RecordManager::linearToInteger(uint16_t data) { // The exponent is the first 5 bits, followed by 11 bits of mantissa. int8_t exponent = (data & 0xF800) >> 11; int16_t mantissa = (data & 0x07FF); // If exponent's MSB on, then it's negative. // Convert from two's complement. if (exponent & 0x10) { exponent = (~exponent) & 0x1F; exponent = (exponent + 1) * -1; } // If mantissa's MSB on, then it's negative. // Convert from two's complement. if (mantissa & 0x400) { mantissa = (~mantissa) & 0x07FF; mantissa = (mantissa + 1) * -1; } auto value = static_cast(mantissa) * pow(2, exponent); return value; } } // namespace history } // namespace power } // namespace witherspoon