xref: /openbmc/phosphor-power/power-supply/record_manager.cpp (revision dcb4b3b3b8d13d5257120a28d9427033ae631178)
1  /**
2   * Copyright © 2017 IBM Corporation
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *     http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  #include "record_manager.hpp"
17  
18  #include <math.h>
19  
20  #include <phosphor-logging/log.hpp>
21  
22  #include <chrono>
23  
24  namespace phosphor
25  {
26  namespace power
27  {
28  namespace history
29  {
30  
31  using namespace phosphor::logging;
32  
33  bool RecordManager::add(const std::vector<uint8_t>& rawRecord)
34  {
35      if (rawRecord.size() == 0)
36      {
37          // The PS has no data - either the power supply just started up,
38          // or it just got a SYNC.  Clear the history.
39          records.clear();
40          return true;
41      }
42  
43      try
44      {
45          // Peek at the ID to see if more processing is needed.
46          auto id = getRawRecordID(rawRecord);
47  
48          if (!records.empty())
49          {
50              auto previousID = std::get<recIDPos>(records.front());
51  
52              // Already have this record.  Done.
53              if (previousID == id)
54              {
55                  return false;
56              }
57  
58              // Check that the sequence ID is in order.
59              // If not, clear out current list.
60              if ((previousID + 1) != id)
61              {
62                  // If it just rolled over from 0xFF to 0x00, then no
63                  // need to clear.  If we see a 0 seemingly out of nowhere,
64                  // then it was a sync so clear the old records.
65                  auto rolledOver =
66                      (previousID == lastSequenceID) && (id == FIRST_SEQUENCE_ID);
67  
68                  if (!rolledOver)
69                  {
70                      if (id != FIRST_SEQUENCE_ID)
71                      {
72                          log<level::INFO>(
73                              "Noncontiguous INPUT_HISTORY sequence ID "
74                              "found. Clearing old entries",
75                              entry("OLD_ID=%ld", previousID),
76                              entry("NEW_ID=%ld", id));
77                      }
78                      records.clear();
79                  }
80              }
81          }
82  
83          records.push_front(std::move(createRecord(rawRecord)));
84  
85          // If no more should be stored, prune the oldest
86          if (records.size() > maxRecords)
87          {
88              records.pop_back();
89          }
90      }
91      catch (InvalidRecordException& e)
92      {
93          return false;
94      }
95  
96      return true;
97  }
98  
99  auto RecordManager::getAverageRecords() -> DBusRecordList
100  {
101      DBusRecordList list;
102  
103      for (const auto& r : records)
104      {
105          list.emplace_back(std::get<recTimePos>(r), std::get<recAvgPos>(r));
106      }
107  
108      return list;
109  }
110  
111  auto RecordManager::getMaximumRecords() -> DBusRecordList
112  {
113      DBusRecordList list;
114  
115      for (const auto& r : records)
116      {
117          list.emplace_back(std::get<recTimePos>(r), std::get<recMaxPos>(r));
118      }
119  
120      return list;
121  }
122  
123  size_t RecordManager::getRawRecordID(const std::vector<uint8_t>& data) const
124  {
125      if (data.size() != RAW_RECORD_SIZE)
126      {
127          log<level::ERR>("Invalid INPUT_HISTORY size",
128                          entry("SIZE=%d", data.size()));
129          throw InvalidRecordException{};
130      }
131  
132      return data[RAW_RECORD_ID_OFFSET];
133  }
134  
135  Record RecordManager::createRecord(const std::vector<uint8_t>& data)
136  {
137      // The raw record format is:
138      //  0xAABBCCDDEE
139      //
140      //  where:
141      //    0xAA = sequence ID
142      //    0xBBCC = average power in linear format (0xCC = MSB)
143      //    0xDDEE = maximum power in linear format (0xEE = MSB)
144      auto id = getRawRecordID(data);
145  
146      auto time = std::chrono::duration_cast<std::chrono::milliseconds>(
147                      std::chrono::system_clock::now().time_since_epoch())
148                      .count();
149  
150      auto val = static_cast<uint16_t>(data[2]) << 8 | data[1];
151      auto averagePower = linearToInteger(val);
152  
153      val = static_cast<uint16_t>(data[4]) << 8 | data[3];
154      auto maxPower = linearToInteger(val);
155  
156      return Record{id, time, averagePower, maxPower};
157  }
158  
159  int64_t RecordManager::linearToInteger(uint16_t data)
160  {
161      // The exponent is the first 5 bits, followed by 11 bits of mantissa.
162      int8_t exponent = (data & 0xF800) >> 11;
163      int16_t mantissa = (data & 0x07FF);
164  
165      // If exponent's MSB on, then it's negative.
166      // Convert from two's complement.
167      if (exponent & 0x10)
168      {
169          exponent = (~exponent) & 0x1F;
170          exponent = (exponent + 1) * -1;
171      }
172  
173      // If mantissa's MSB on, then it's negative.
174      // Convert from two's complement.
175      if (mantissa & 0x400)
176      {
177          mantissa = (~mantissa) & 0x07FF;
178          mantissa = (mantissa + 1) * -1;
179      }
180  
181      auto value = static_cast<float>(mantissa) * pow(2, exponent);
182      return value;
183  }
184  
185  } // namespace history
186  } // namespace power
187  } // namespace phosphor
188