blob: 16eb8ceaac270f11b6bc5a0016697fb97adad4b9 [file] [log] [blame]
Jason M. Bills5e049d32018-10-19 12:59:38 -07001/*
2// Copyright (c) 2017 2018 Intel 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
17#pragma once
Zhikui Ren672bdfc2020-07-14 11:37:01 -070018#include <phosphor-logging/log.hpp>
19
Jason M. Bills5e049d32018-10-19 12:59:38 -070020#include <cmath>
21#include <iostream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070022
23namespace ipmi
24{
25/** @struct VariantToDoubleVisitor
26 * @brief Visitor to convert variants to doubles
27 * @details Performs a static cast on the underlying type
28 */
29struct VariantToDoubleVisitor
30{
Zhikui Ren672bdfc2020-07-14 11:37:01 -070031 template <typename T>
32 double operator()(const T& t) const
Jason M. Bills5e049d32018-10-19 12:59:38 -070033 {
34 static_assert(std::is_arithmetic_v<T>,
35 "Cannot translate type to double");
36 return static_cast<double>(t);
37 }
38};
39
40static constexpr int16_t maxInt10 = 0x1FF;
41static constexpr int16_t minInt10 = -0x200;
42static constexpr int8_t maxInt4 = 7;
43static constexpr int8_t minInt4 = -8;
44
45static inline bool getSensorAttributes(const double max, const double min,
46 int16_t& mValue, int8_t& rExp,
47 int16_t& bValue, int8_t& bExp,
48 bool& bSigned)
49{
50 // computing y = (10^rRexp) * (Mx + (B*(10^Bexp)))
51 // check for 0, assume always positive
52 double mDouble;
53 double bDouble;
54 if (max <= min)
55 {
56 phosphor::logging::log<phosphor::logging::level::DEBUG>(
57 "getSensorAttributes: Max must be greater than min");
58 return false;
59 }
60
61 mDouble = (max - min) / 0xFF;
62
63 if (min < 0)
64 {
65 bSigned = true;
66 bDouble = floor(0.5 + ((max + min) / 2));
67 }
68 else
69 {
70 bSigned = false;
71 bDouble = min;
72 }
73
74 rExp = 0;
75
76 // M too big for 10 bit variable
77 while (mDouble > maxInt10)
78 {
79 if (rExp >= maxInt4)
80 {
81 phosphor::logging::log<phosphor::logging::level::DEBUG>(
82 "rExp Too big, Max and Min range too far",
83 phosphor::logging::entry("REXP=%d", rExp));
84 return false;
85 }
86 mDouble /= 10;
87 rExp++;
88 }
89
90 // M too small, loop until we lose less than 1 eight bit count of precision
91 while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255))
92 {
93 if (rExp <= minInt4)
94 {
95 phosphor::logging::log<phosphor::logging::level::DEBUG>(
96 "rExp Too Small, Max and Min range too close");
97 return false;
98 }
99 // check to see if we reached the limit of where we can adjust back the
100 // B value
101 if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble)
102 {
103 if (mDouble < 1.0)
104 {
105 phosphor::logging::log<phosphor::logging::level::DEBUG>(
106 "Could not find mValue and B value with enough "
107 "precision.");
108 return false;
109 }
110 break;
111 }
112 // can't multiply M any more, max precision reached
113 else if (mDouble * 10 > maxInt10)
114 {
115 break;
116 }
117 mDouble *= 10;
118 rExp--;
119 }
120
121 bDouble /= std::pow(10, rExp);
122 bExp = 0;
123
124 // B too big for 10 bit variable
125 while (bDouble > maxInt10 || bDouble < minInt10)
126 {
127 if (bExp >= maxInt4)
128 {
129 phosphor::logging::log<phosphor::logging::level::DEBUG>(
130 "bExp Too Big, Max and Min range need to be adjusted");
131 return false;
132 }
133 bDouble /= 10;
134 bExp++;
135 }
136
137 while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) >
138 (1.0 / 255))
139 {
140 if (bExp <= minInt4)
141 {
142 phosphor::logging::log<phosphor::logging::level::DEBUG>(
143 "bExp Too Small, Max and Min range need to be adjusted");
144 return false;
145 }
146 bDouble *= 10;
147 bExp -= 1;
148 }
149
Jason M. Bills7d5054a2019-10-30 10:47:35 -0700150 mValue = static_cast<int16_t>(std::round(mDouble)) & maxInt10;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700151 bValue = static_cast<int16_t>(bDouble) & maxInt10;
152
153 return true;
154}
155
156static inline uint8_t
157 scaleIPMIValueFromDouble(const double value, const uint16_t mValue,
158 const int8_t rExp, const uint16_t bValue,
159 const int8_t bExp, const bool bSigned)
160{
Jason M. Bills7d5054a2019-10-30 10:47:35 -0700161 int32_t scaledValue =
Jason M. Bills5e049d32018-10-19 12:59:38 -0700162 (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) /
163 (mValue * std::pow(10, rExp));
164
Jason M. Bills5e049d32018-10-19 12:59:38 -0700165 if (bSigned)
166 {
Jason M. Bills7d5054a2019-10-30 10:47:35 -0700167 if (scaledValue > std::numeric_limits<int8_t>::max() ||
168 scaledValue < std::numeric_limits<int8_t>::lowest())
169 {
170 throw std::out_of_range("Value out of range");
171 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700172 return static_cast<int8_t>(scaledValue);
173 }
174 else
175 {
Jason M. Bills7d5054a2019-10-30 10:47:35 -0700176 if (scaledValue > std::numeric_limits<uint8_t>::max() ||
177 scaledValue < std::numeric_limits<uint8_t>::lowest())
178 {
179 throw std::out_of_range("Value out of range");
180 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700181 return static_cast<uint8_t>(scaledValue);
182 }
183}
184
185static inline uint8_t getScaledIPMIValue(const double value, const double max,
186 const double min)
187{
188 int16_t mValue = 0;
189 int8_t rExp = 0;
190 int16_t bValue = 0;
191 int8_t bExp = 0;
192 bool bSigned = 0;
193 bool result = 0;
194
195 result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned);
196 if (!result)
197 {
198 throw std::runtime_error("Illegal sensor attributes");
199 }
200 return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned);
201}
202
203} // namespace ipmi