blob: 7fa9300076ca1d43023f40e35c6f162198287b65 [file] [log] [blame]
James Feist8fd8a582018-11-16 11:10:46 -08001#pragma once
2
Patrick Ventureca44b2f2019-10-31 11:02:26 -07003#include "Thresholds.hpp"
James Feist961bf092020-07-01 16:38:12 -07004#include "Utils.hpp"
Patrick Ventureca44b2f2019-10-31 11:02:26 -07005
James Feist38fb5982020-05-28 10:09:54 -07006#include <sdbusplus/asio/object_server.hpp>
7
Patrick Venturefd6ba732019-10-31 14:27:39 -07008#include <limits>
9#include <memory>
Patrick Venturefd6ba732019-10-31 14:27:39 -070010#include <string>
11#include <vector>
James Feist8fd8a582018-11-16 11:10:46 -080012
James Feist1169eb42018-10-31 10:08:47 -070013constexpr size_t sensorFailedPollTimeMs = 5000;
James Feista5e58722019-04-22 14:43:11 -070014
15constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value";
James Feist67601bd2020-06-16 17:14:44 -070016constexpr const char* availableInterfaceName =
17 "xyz.openbmc_project.State.Decorator.Availability";
James Feist961bf092020-07-01 16:38:12 -070018constexpr const char* operationalInterfaceName =
19 "xyz.openbmc_project.State.Decorator.OperationalStatus";
20constexpr const size_t errorThreshold = 5;
21
James Feist8fd8a582018-11-16 11:10:46 -080022struct Sensor
23{
James Feist930fcde2019-05-28 12:58:43 -070024 Sensor(const std::string& name,
James Feistd8705872019-02-08 13:26:09 -080025 std::vector<thresholds::Threshold>&& thresholdData,
26 const std::string& configurationPath, const std::string& objectType,
James Feist961bf092020-07-01 16:38:12 -070027 const double max, const double min,
28 PowerState readState = PowerState::always) :
AppaRao Puli54ffb272020-06-02 19:09:38 +053029 name(std::regex_replace(name, std::regex("[^a-zA-Z0-9_/]+"), "_")),
James Feist930fcde2019-05-28 12:58:43 -070030 configurationPath(configurationPath), objectType(objectType),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050031 maxValue(max), minValue(min), thresholds(std::move(thresholdData)),
Josh Lehan883fb3a2020-02-27 14:41:39 -080032 hysteresisTrigger((max - min) * 0.01),
James Feist961bf092020-07-01 16:38:12 -070033 hysteresisPublish((max - min) * 0.0001), readState(readState),
34 errCount(0)
James Feist38fb5982020-05-28 10:09:54 -070035 {}
James Feist8fd8a582018-11-16 11:10:46 -080036 virtual ~Sensor() = default;
James Feistce3fca42018-11-21 12:58:24 -080037 virtual void checkThresholds(void) = 0;
James Feistdc6c55f2018-10-31 12:53:20 -070038 std::string name;
James Feistce3fca42018-11-21 12:58:24 -080039 std::string configurationPath;
40 std::string objectType;
41 double maxValue;
42 double minValue;
James Feist8fd8a582018-11-16 11:10:46 -080043 std::vector<thresholds::Threshold> thresholds;
44 std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
45 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceWarning;
46 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceCritical;
James Feist078f2322019-03-08 11:09:05 -080047 std::shared_ptr<sdbusplus::asio::dbus_interface> association;
James Feist67601bd2020-06-16 17:14:44 -070048 std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
James Feist961bf092020-07-01 16:38:12 -070049 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
James Feist8fd8a582018-11-16 11:10:46 -080050 double value = std::numeric_limits<double>::quiet_NaN();
Zhikui Rend3da1282020-09-11 17:02:01 -070051 double rawValue = std::numeric_limits<double>::quiet_NaN();
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053052 bool overriddenState = false;
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053053 bool internalSet = false;
Josh Lehan883fb3a2020-02-27 14:41:39 -080054 double hysteresisTrigger;
55 double hysteresisPublish;
James Feist961bf092020-07-01 16:38:12 -070056 PowerState readState;
57 size_t errCount;
James Feistce3fca42018-11-21 12:58:24 -080058
James Feistd8705872019-02-08 13:26:09 -080059 int setSensorValue(const double& newValue, double& oldValue)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053060 {
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053061 if (!internalSet)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053062 {
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053063 oldValue = newValue;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053064 overriddenState = true;
65 // check thresholds for external set
66 value = newValue;
67 checkThresholds();
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053068 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053069 else if (!overriddenState)
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053070 {
71 oldValue = newValue;
72 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053073 return 1;
74 }
James Feistce3fca42018-11-21 12:58:24 -080075
76 void
Cheng C Yang6b1247a2020-03-09 23:48:39 +080077 setInitialProperties(std::shared_ptr<sdbusplus::asio::connection>& conn,
78 const std::string label = std::string(),
79 size_t thresholdSize = 0)
James Feistce3fca42018-11-21 12:58:24 -080080 {
James Feist961bf092020-07-01 16:38:12 -070081 if (readState == PowerState::on || readState == PowerState::biosPost)
82 {
83 setupPowerMatch(conn);
84 }
85
James Feist82bac4c2019-03-11 11:16:53 -070086 createAssociation(association, configurationPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +053087
James Feistce3fca42018-11-21 12:58:24 -080088 sensorInterface->register_property("MaxValue", maxValue);
89 sensorInterface->register_property("MinValue", minValue);
90 sensorInterface->register_property(
James Feistd8705872019-02-08 13:26:09 -080091 "Value", value, [&](const double& newValue, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -080092 return setSensorValue(newValue, oldValue);
93 });
James Feistd8705872019-02-08 13:26:09 -080094 for (auto& threshold : thresholds)
James Feistce3fca42018-11-21 12:58:24 -080095 {
96 std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
97 std::string level;
98 std::string alarm;
99 if (threshold.level == thresholds::Level::CRITICAL)
100 {
101 iface = thresholdInterfaceCritical;
102 if (threshold.direction == thresholds::Direction::HIGH)
103 {
104 level = "CriticalHigh";
105 alarm = "CriticalAlarmHigh";
106 }
107 else
108 {
109 level = "CriticalLow";
110 alarm = "CriticalAlarmLow";
111 }
112 }
113 else if (threshold.level == thresholds::Level::WARNING)
114 {
115 iface = thresholdInterfaceWarning;
116 if (threshold.direction == thresholds::Direction::HIGH)
117 {
118 level = "WarningHigh";
119 alarm = "WarningAlarmHigh";
120 }
121 else
122 {
123 level = "WarningLow";
124 alarm = "WarningAlarmLow";
125 }
126 }
127 else
128 {
129 std::cerr << "Unknown threshold level" << threshold.level
130 << "\n";
131 continue;
132 }
133 if (!iface)
134 {
135 std::cout << "trying to set uninitialized interface\n";
136 continue;
137 }
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800138
139 size_t thresSize =
140 label.empty() ? thresholds.size() : thresholdSize;
James Feistce3fca42018-11-21 12:58:24 -0800141 iface->register_property(
142 level, threshold.value,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800143 [&, label, thresSize](const double& request, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800144 oldValue = request; // todo, just let the config do this?
145 threshold.value = request;
146 thresholds::persistThreshold(configurationPath, objectType,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800147 threshold, conn, thresSize,
148 label);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800149 // Invalidate previously remembered value,
150 // so new thresholds will be checked during next update,
151 // even if sensor reading remains unchanged.
152 value = std::numeric_limits<double>::quiet_NaN();
153
154 // Although tempting, don't call checkThresholds() from here
155 // directly. Let the regular sensor monitor call the same
156 // using updateValue(), which can check conditions like
157 // poweron, etc., before raising any event.
James Feistce3fca42018-11-21 12:58:24 -0800158 return 1;
159 });
160 iface->register_property(alarm, false);
161 }
162 if (!sensorInterface->initialize())
163 {
164 std::cerr << "error initializing value interface\n";
165 }
166 if (thresholdInterfaceWarning &&
Yong Lif902c052020-05-07 17:13:53 +0800167 !thresholdInterfaceWarning->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800168 {
169 std::cerr << "error initializing warning threshold interface\n";
170 }
171
172 if (thresholdInterfaceCritical &&
Yong Lif902c052020-05-07 17:13:53 +0800173 !thresholdInterfaceCritical->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800174 {
175 std::cerr << "error initializing critical threshold interface\n";
176 }
James Feist67601bd2020-06-16 17:14:44 -0700177
178 if (!availableInterface)
179 {
180 availableInterface =
181 std::make_shared<sdbusplus::asio::dbus_interface>(
182 conn, sensorInterface->get_object_path(),
183 availableInterfaceName);
184 availableInterface->register_property(
185 "Available", true, [this](const bool propIn, bool& old) {
186 if (propIn == old)
187 {
188 return 1;
189 }
James Feist961bf092020-07-01 16:38:12 -0700190 old = propIn;
James Feist67601bd2020-06-16 17:14:44 -0700191 if (!propIn)
192 {
193 updateValue(std::numeric_limits<double>::quiet_NaN());
194 }
James Feist67601bd2020-06-16 17:14:44 -0700195 return 1;
196 });
197 availableInterface->initialize();
198 }
James Feist961bf092020-07-01 16:38:12 -0700199 if (!operationalInterface)
200 {
201 operationalInterface =
202 std::make_shared<sdbusplus::asio::dbus_interface>(
203 conn, sensorInterface->get_object_path(),
204 operationalInterfaceName);
205 operationalInterface->register_property("Functional", true);
206 operationalInterface->initialize();
207 }
208 }
209
210 bool readingStateGood()
211 {
212 if (readState == PowerState::on && !isPowerOn())
213 {
214 return false;
215 }
216 if (readState == PowerState::biosPost &&
217 (!hasBiosPost() || !isPowerOn()))
218 {
219 return false;
220 }
221
222 return true;
223 }
224
225 void markFunctional(bool isFunctional)
226 {
227 if (operationalInterface)
228 {
229 operationalInterface->set_property("Functional", isFunctional);
230 }
231 if (isFunctional)
232 {
233 errCount = 0;
234 }
235 else
236 {
237 updateValue(std::numeric_limits<double>::quiet_NaN());
238 }
239 }
240
241 void markAvailable(bool isAvailable)
242 {
243 if (availableInterface)
244 {
245 availableInterface->set_property("Available", isAvailable);
246 errCount = 0;
247 }
248 }
249
250 void incrementError()
251 {
252 if (!readingStateGood())
253 {
254 markAvailable(false);
255 return;
256 }
257
258 if (errCount >= errorThreshold)
259 {
260 return;
261 }
262
263 errCount++;
264 if (errCount == errorThreshold)
265 {
266 std::cerr << "Sensor " << name << " reading error!\n";
267 markFunctional(false);
268 }
James Feistce3fca42018-11-21 12:58:24 -0800269 }
270
James Feistd8705872019-02-08 13:26:09 -0800271 void updateValue(const double& newValue)
James Feistce3fca42018-11-21 12:58:24 -0800272 {
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530273 // Ignore if overriding is enabled
James Feist961bf092020-07-01 16:38:12 -0700274 if (overriddenState)
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530275 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800276 return;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530277 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800278
James Feist961bf092020-07-01 16:38:12 -0700279 if (!readingStateGood())
280 {
281 markAvailable(false);
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200282 updateValueProperty(std::numeric_limits<double>::quiet_NaN());
James Feist961bf092020-07-01 16:38:12 -0700283 return;
284 }
285
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200286 updateValueProperty(newValue);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800287
288 // Always check thresholds after changing the value,
289 // as the test against hysteresisTrigger now takes place in
290 // the thresholds::checkThresholds() method,
291 // which is called by checkThresholds() below,
292 // in all current implementations of sensors that have thresholds.
293 checkThresholds();
James Feist961bf092020-07-01 16:38:12 -0700294 if (!std::isnan(newValue))
295 {
296 markFunctional(true);
297 markAvailable(true);
298 }
James Feistce3fca42018-11-21 12:58:24 -0800299 }
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200300
301 void updateProperty(
302 std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
303 double& oldValue, const double& newValue, const char* dbusPropertyName)
304 {
305 if (requiresUpdate(oldValue, newValue))
306 {
307 oldValue = newValue;
Jae Hyun Yoo1a540b82020-07-30 23:33:18 -0700308 if (interface &&
309 !(interface->set_property(dbusPropertyName, newValue)))
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200310 {
311 std::cerr << "error setting property " << dbusPropertyName
312 << " to " << newValue << "\n";
313 }
314 }
315 }
316
317 bool requiresUpdate(const double& lVal, const double& rVal)
318 {
319 if (std::isnan(lVal) || std::isnan(rVal))
320 {
321 return true;
322 }
323 double diff = std::abs(lVal - rVal);
324 if (diff > hysteresisPublish)
325 {
326 return true;
327 }
328 return false;
329 }
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200330
331 private:
332 void updateValueProperty(const double& newValue)
333 {
334 // Indicate that it is internal set call, not an external overwrite
335 internalSet = true;
336 updateProperty(sensorInterface, value, newValue, "Value");
337 internalSet = false;
338 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530339};