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