1d7abf367SMatt Spinler /**
2d7abf367SMatt Spinler  * Copyright © 2017 IBM Corporation
3d7abf367SMatt Spinler  *
4d7abf367SMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
5d7abf367SMatt Spinler  * you may not use this file except in compliance with the License.
6d7abf367SMatt Spinler  * You may obtain a copy of the License at
7d7abf367SMatt Spinler  *
8d7abf367SMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
9d7abf367SMatt Spinler  *
10d7abf367SMatt Spinler  * Unless required by applicable law or agreed to in writing, software
11d7abf367SMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
12d7abf367SMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7abf367SMatt Spinler  * See the License for the specific language governing permissions and
14d7abf367SMatt Spinler  * limitations under the License.
15d7abf367SMatt Spinler  */
16d7abf367SMatt Spinler #include "record_manager.hpp"
17d7abf367SMatt Spinler 
18f0f02b9aSMatt Spinler #include <math.h>
19f0f02b9aSMatt Spinler 
20f0f02b9aSMatt Spinler #include <phosphor-logging/log.hpp>
21f0f02b9aSMatt Spinler 
222c4fbc4cSPatrick Williams #include <chrono>
232c4fbc4cSPatrick Williams 
24d7abf367SMatt Spinler namespace witherspoon
25d7abf367SMatt Spinler {
26d7abf367SMatt Spinler namespace power
27d7abf367SMatt Spinler {
28d7abf367SMatt Spinler namespace history
29d7abf367SMatt Spinler {
30d7abf367SMatt Spinler 
3128fa1b6bSMatt Spinler using namespace phosphor::logging;
3228fa1b6bSMatt Spinler 
add(const std::vector<uint8_t> & rawRecord)338168d280SMatt Spinler bool RecordManager::add(const std::vector<uint8_t>& rawRecord)
348168d280SMatt Spinler {
358168d280SMatt Spinler     if (rawRecord.size() == 0)
368168d280SMatt Spinler     {
378168d280SMatt Spinler         // The PS has no data - either the power supply just started up,
388168d280SMatt Spinler         // or it just got a SYNC.  Clear the history.
398168d280SMatt Spinler         records.clear();
408168d280SMatt Spinler         return true;
418168d280SMatt Spinler     }
428168d280SMatt Spinler 
438168d280SMatt Spinler     try
448168d280SMatt Spinler     {
458168d280SMatt Spinler         // Peek at the ID to see if more processing is needed.
468168d280SMatt Spinler         auto id = getRawRecordID(rawRecord);
478168d280SMatt Spinler 
488168d280SMatt Spinler         if (!records.empty())
498168d280SMatt Spinler         {
508168d280SMatt Spinler             auto previousID = std::get<recIDPos>(records.front());
518168d280SMatt Spinler 
528168d280SMatt Spinler             // Already have this record.  Done.
538168d280SMatt Spinler             if (previousID == id)
548168d280SMatt Spinler             {
558168d280SMatt Spinler                 return false;
568168d280SMatt Spinler             }
578168d280SMatt Spinler 
588168d280SMatt Spinler             // Check that the sequence ID is in order.
598168d280SMatt Spinler             // If not, clear out current list.
608168d280SMatt Spinler             if ((previousID + 1) != id)
618168d280SMatt Spinler             {
628168d280SMatt Spinler                 // If it just rolled over from 0xFF to 0x00, then no
638168d280SMatt Spinler                 // need to clear.  If we see a 0 seemingly out of nowhere,
648168d280SMatt Spinler                 // then it was a sync so clear the old records.
65*b7ed5773SPatrick Williams                 auto rolledOver = (previousID == lastSequenceID) &&
66*b7ed5773SPatrick Williams                                   (id == FIRST_SEQUENCE_ID);
678168d280SMatt Spinler 
688168d280SMatt Spinler                 if (!rolledOver)
698168d280SMatt Spinler                 {
708168d280SMatt Spinler                     if (id != FIRST_SEQUENCE_ID)
718168d280SMatt Spinler                     {
728168d280SMatt Spinler                         log<level::INFO>(
738168d280SMatt Spinler                             "Noncontiguous INPUT_HISTORY sequence ID "
748168d280SMatt Spinler                             "found. Clearing old entries",
758168d280SMatt Spinler                             entry("OLD_ID=%ld", previousID),
768168d280SMatt Spinler                             entry("NEW_ID=%ld", id));
778168d280SMatt Spinler                     }
788168d280SMatt Spinler                     records.clear();
798168d280SMatt Spinler                 }
808168d280SMatt Spinler             }
818168d280SMatt Spinler         }
828168d280SMatt Spinler 
838168d280SMatt Spinler         records.push_front(std::move(createRecord(rawRecord)));
848168d280SMatt Spinler 
858168d280SMatt Spinler         // If no more should be stored, prune the oldest
868168d280SMatt Spinler         if (records.size() > maxRecords)
878168d280SMatt Spinler         {
888168d280SMatt Spinler             records.pop_back();
898168d280SMatt Spinler         }
908168d280SMatt Spinler     }
918168d280SMatt Spinler     catch (InvalidRecordException& e)
928168d280SMatt Spinler     {
938168d280SMatt Spinler         return false;
948168d280SMatt Spinler     }
958168d280SMatt Spinler 
968168d280SMatt Spinler     return true;
978168d280SMatt Spinler }
988168d280SMatt Spinler 
getAverageRecords()99c3414388SMatt Spinler auto RecordManager::getAverageRecords() -> DBusRecordList
100c3414388SMatt Spinler {
101c3414388SMatt Spinler     DBusRecordList list;
102c3414388SMatt Spinler 
103c3414388SMatt Spinler     for (const auto& r : records)
104c3414388SMatt Spinler     {
105f0f02b9aSMatt Spinler         list.emplace_back(std::get<recTimePos>(r), std::get<recAvgPos>(r));
106c3414388SMatt Spinler     }
107c3414388SMatt Spinler 
108c3414388SMatt Spinler     return list;
109c3414388SMatt Spinler }
110c3414388SMatt Spinler 
getMaximumRecords()111c3414388SMatt Spinler auto RecordManager::getMaximumRecords() -> DBusRecordList
112c3414388SMatt Spinler {
113c3414388SMatt Spinler     DBusRecordList list;
114c3414388SMatt Spinler 
115c3414388SMatt Spinler     for (const auto& r : records)
116c3414388SMatt Spinler     {
117f0f02b9aSMatt Spinler         list.emplace_back(std::get<recTimePos>(r), std::get<recMaxPos>(r));
118c3414388SMatt Spinler     }
119c3414388SMatt Spinler 
120c3414388SMatt Spinler     return list;
121c3414388SMatt Spinler }
122c3414388SMatt Spinler 
getRawRecordID(const std::vector<uint8_t> & data) const123f0f02b9aSMatt Spinler size_t RecordManager::getRawRecordID(const std::vector<uint8_t>& data) const
12428fa1b6bSMatt Spinler {
12528fa1b6bSMatt Spinler     if (data.size() != RAW_RECORD_SIZE)
12628fa1b6bSMatt Spinler     {
12728fa1b6bSMatt Spinler         log<level::ERR>("Invalid INPUT_HISTORY size",
12828fa1b6bSMatt Spinler                         entry("SIZE=%d", data.size()));
12928fa1b6bSMatt Spinler         throw InvalidRecordException{};
13028fa1b6bSMatt Spinler     }
13128fa1b6bSMatt Spinler 
13228fa1b6bSMatt Spinler     return data[RAW_RECORD_ID_OFFSET];
13328fa1b6bSMatt Spinler }
13428fa1b6bSMatt Spinler 
createRecord(const std::vector<uint8_t> & data)13528fa1b6bSMatt Spinler Record RecordManager::createRecord(const std::vector<uint8_t>& data)
13628fa1b6bSMatt Spinler {
13728fa1b6bSMatt Spinler     // The raw record format is:
13828fa1b6bSMatt Spinler     //  0xAABBCCDDEE
13928fa1b6bSMatt Spinler     //
14028fa1b6bSMatt Spinler     //  where:
14128fa1b6bSMatt Spinler     //    0xAA = sequence ID
14228fa1b6bSMatt Spinler     //    0xBBCC = average power in linear format (0xCC = MSB)
14328fa1b6bSMatt Spinler     //    0xDDEE = maximum power in linear format (0xEE = MSB)
14428fa1b6bSMatt Spinler     auto id = getRawRecordID(data);
14528fa1b6bSMatt Spinler 
14628fa1b6bSMatt Spinler     auto time = std::chrono::duration_cast<std::chrono::milliseconds>(
147f0f02b9aSMatt Spinler                     std::chrono::system_clock::now().time_since_epoch())
148f0f02b9aSMatt Spinler                     .count();
14928fa1b6bSMatt Spinler 
15028fa1b6bSMatt Spinler     auto val = static_cast<uint16_t>(data[2]) << 8 | data[1];
15128fa1b6bSMatt Spinler     auto averagePower = linearToInteger(val);
15228fa1b6bSMatt Spinler 
15328fa1b6bSMatt Spinler     val = static_cast<uint16_t>(data[4]) << 8 | data[3];
15428fa1b6bSMatt Spinler     auto maxPower = linearToInteger(val);
15528fa1b6bSMatt Spinler 
15628fa1b6bSMatt Spinler     return Record{id, time, averagePower, maxPower};
15728fa1b6bSMatt Spinler }
15828fa1b6bSMatt Spinler 
linearToInteger(uint16_t data)159e710d189SMatt Spinler int64_t RecordManager::linearToInteger(uint16_t data)
160e710d189SMatt Spinler {
161e710d189SMatt Spinler     // The exponent is the first 5 bits, followed by 11 bits of mantissa.
162e710d189SMatt Spinler     int8_t exponent = (data & 0xF800) >> 11;
163e710d189SMatt Spinler     int16_t mantissa = (data & 0x07FF);
164e710d189SMatt Spinler 
165e710d189SMatt Spinler     // If exponent's MSB on, then it's negative.
166e710d189SMatt Spinler     // Convert from two's complement.
167e710d189SMatt Spinler     if (exponent & 0x10)
168e710d189SMatt Spinler     {
169e710d189SMatt Spinler         exponent = (~exponent) & 0x1F;
170e710d189SMatt Spinler         exponent = (exponent + 1) * -1;
171e710d189SMatt Spinler     }
172e710d189SMatt Spinler 
173e710d189SMatt Spinler     // If mantissa's MSB on, then it's negative.
174e710d189SMatt Spinler     // Convert from two's complement.
175e710d189SMatt Spinler     if (mantissa & 0x400)
176e710d189SMatt Spinler     {
177e710d189SMatt Spinler         mantissa = (~mantissa) & 0x07FF;
178e710d189SMatt Spinler         mantissa = (mantissa + 1) * -1;
179e710d189SMatt Spinler     }
180e710d189SMatt Spinler 
181e710d189SMatt Spinler     auto value = static_cast<float>(mantissa) * pow(2, exponent);
182e710d189SMatt Spinler     return value;
183e710d189SMatt Spinler }
184d7abf367SMatt Spinler 
185f0f02b9aSMatt Spinler } // namespace history
186f0f02b9aSMatt Spinler } // namespace power
187f0f02b9aSMatt Spinler } // namespace witherspoon
188