blob: 115b8fa96630654b751fe2de0d4403e88fa9ef83 [file] [log] [blame]
James Feist8fd8a582018-11-16 11:10:46 -08001#pragma once
2
Ed Tanous8a57ec02020-10-09 12:46:52 -07003#include <Thresholds.hpp>
4#include <Utils.hpp>
James Feist38fb5982020-05-28 10:09:54 -07005#include <sdbusplus/asio/object_server.hpp>
6
Patrick Venturefd6ba732019-10-31 14:27:39 -07007#include <limits>
8#include <memory>
Patrick Venturefd6ba732019-10-31 14:27:39 -07009#include <string>
10#include <vector>
James Feist8fd8a582018-11-16 11:10:46 -080011
James Feist1169eb42018-10-31 10:08:47 -070012constexpr size_t sensorFailedPollTimeMs = 5000;
James Feista5e58722019-04-22 14:43:11 -070013
Josh Lehan3bcd8232020-10-29 00:22:12 -070014// Enable useful logging with sensor instrumentation
15// This is intentionally not DEBUG, avoid clash with usage in .cpp files
16constexpr bool enableInstrumentation = false;
17
James Feista5e58722019-04-22 14:43:11 -070018constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value";
James Feist67601bd2020-06-16 17:14:44 -070019constexpr const char* availableInterfaceName =
20 "xyz.openbmc_project.State.Decorator.Availability";
James Feist961bf092020-07-01 16:38:12 -070021constexpr const char* operationalInterfaceName =
22 "xyz.openbmc_project.State.Decorator.OperationalStatus";
23constexpr const size_t errorThreshold = 5;
24
Josh Lehan3bcd8232020-10-29 00:22:12 -070025struct SensorInstrumentation
26{
27 // These are for instrumentation for debugging
28 int numCollectsGood = 0;
29 int numCollectsMiss = 0;
30 int numStreakGreats = 0;
31 int numStreakMisses = 0;
32 double minCollected = 0.0;
33 double maxCollected = 0.0;
34};
35
James Feist8fd8a582018-11-16 11:10:46 -080036struct Sensor
37{
James Feist930fcde2019-05-28 12:58:43 -070038 Sensor(const std::string& name,
James Feistd8705872019-02-08 13:26:09 -080039 std::vector<thresholds::Threshold>&& thresholdData,
40 const std::string& configurationPath, const std::string& objectType,
James Feist961bf092020-07-01 16:38:12 -070041 const double max, const double min,
James Feiste3338522020-09-15 15:40:30 -070042 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist961bf092020-07-01 16:38:12 -070043 PowerState readState = PowerState::always) :
AppaRao Puli54ffb272020-06-02 19:09:38 +053044 name(std::regex_replace(name, std::regex("[^a-zA-Z0-9_/]+"), "_")),
James Feist930fcde2019-05-28 12:58:43 -070045 configurationPath(configurationPath), objectType(objectType),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050046 maxValue(max), minValue(min), thresholds(std::move(thresholdData)),
Josh Lehan883fb3a2020-02-27 14:41:39 -080047 hysteresisTrigger((max - min) * 0.01),
James Feiste3338522020-09-15 15:40:30 -070048 hysteresisPublish((max - min) * 0.0001), dbusConnection(conn),
Josh Lehan3bcd8232020-10-29 00:22:12 -070049 readState(readState), errCount(0),
50 instrumentation(enableInstrumentation
51 ? std::make_unique<SensorInstrumentation>()
52 : nullptr)
James Feist38fb5982020-05-28 10:09:54 -070053 {}
James Feist8fd8a582018-11-16 11:10:46 -080054 virtual ~Sensor() = default;
James Feistce3fca42018-11-21 12:58:24 -080055 virtual void checkThresholds(void) = 0;
James Feistdc6c55f2018-10-31 12:53:20 -070056 std::string name;
James Feistce3fca42018-11-21 12:58:24 -080057 std::string configurationPath;
58 std::string objectType;
59 double maxValue;
60 double minValue;
James Feist8fd8a582018-11-16 11:10:46 -080061 std::vector<thresholds::Threshold> thresholds;
62 std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
63 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceWarning;
64 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceCritical;
James Feist078f2322019-03-08 11:09:05 -080065 std::shared_ptr<sdbusplus::asio::dbus_interface> association;
James Feist67601bd2020-06-16 17:14:44 -070066 std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
James Feist961bf092020-07-01 16:38:12 -070067 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
James Feist8fd8a582018-11-16 11:10:46 -080068 double value = std::numeric_limits<double>::quiet_NaN();
Zhikui Rend3da1282020-09-11 17:02:01 -070069 double rawValue = std::numeric_limits<double>::quiet_NaN();
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053070 bool overriddenState = false;
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053071 bool internalSet = false;
Josh Lehan883fb3a2020-02-27 14:41:39 -080072 double hysteresisTrigger;
73 double hysteresisPublish;
James Feiste3338522020-09-15 15:40:30 -070074 std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
James Feist961bf092020-07-01 16:38:12 -070075 PowerState readState;
76 size_t errCount;
Josh Lehan3bcd8232020-10-29 00:22:12 -070077 std::unique_ptr<SensorInstrumentation> instrumentation;
78
79 void updateInstrumentation(double readValue)
80 {
81 // Do nothing if this feature is not enabled
82 if constexpr (!enableInstrumentation)
83 {
84 return;
85 }
86 if (!instrumentation)
87 {
88 return;
89 }
90
91 // Save some typing
92 auto& inst = *instrumentation;
93
94 // Show constants if first reading (even if unsuccessful)
95 if ((inst.numCollectsGood == 0) && (inst.numCollectsMiss == 0))
96 {
97 std::cerr << "Sensor " << name << ": Configuration min=" << minValue
98 << ", max=" << maxValue << ", type=" << objectType
99 << ", path=" << configurationPath << "\n";
100 }
101
102 // Sensors can use "nan" to indicate unavailable reading
103 if (!std::isfinite(readValue))
104 {
105 // Only show this if beginning a new streak
106 if (inst.numStreakMisses == 0)
107 {
108 std::cerr << "Sensor " << name
109 << ": Missing reading, Reading counts good="
110 << inst.numCollectsGood
111 << ", miss=" << inst.numCollectsMiss
112 << ", Prior good streak=" << inst.numStreakGreats
113 << "\n";
114 }
115
116 inst.numStreakGreats = 0;
117 ++(inst.numCollectsMiss);
118 ++(inst.numStreakMisses);
119
120 return;
121 }
122
123 // Only show this if beginning a new streak and not the first time
124 if ((inst.numStreakGreats == 0) && (inst.numCollectsGood != 0))
125 {
126 std::cerr << "Sensor " << name
127 << ": Recovered reading, Reading counts good="
128 << inst.numCollectsGood
129 << ", miss=" << inst.numCollectsMiss
130 << ", Prior miss streak=" << inst.numStreakMisses << "\n";
131 }
132
133 // Initialize min/max if the first successful reading
134 if (inst.numCollectsGood == 0)
135 {
136 std::cerr << "Sensor " << name << ": First reading=" << readValue
137 << "\n";
138
139 inst.minCollected = readValue;
140 inst.maxCollected = readValue;
141 }
142
143 inst.numStreakMisses = 0;
144 ++(inst.numCollectsGood);
145 ++(inst.numStreakGreats);
146
147 // Only provide subsequent output if new min/max established
148 if (readValue < inst.minCollected)
149 {
150 std::cerr << "Sensor " << name << ": Lowest reading=" << readValue
151 << "\n";
152
153 inst.minCollected = readValue;
154 }
155
156 if (readValue > inst.maxCollected)
157 {
158 std::cerr << "Sensor " << name << ": Highest reading=" << readValue
159 << "\n";
160
161 inst.maxCollected = readValue;
162 }
163 }
James Feistce3fca42018-11-21 12:58:24 -0800164
James Feistd8705872019-02-08 13:26:09 -0800165 int setSensorValue(const double& newValue, double& oldValue)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530166 {
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530167 if (!internalSet)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530168 {
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530169 oldValue = newValue;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530170 overriddenState = true;
171 // check thresholds for external set
172 value = newValue;
173 checkThresholds();
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530174 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530175 else if (!overriddenState)
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530176 {
177 oldValue = newValue;
178 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530179 return 1;
180 }
James Feistce3fca42018-11-21 12:58:24 -0800181
182 void
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800183 setInitialProperties(std::shared_ptr<sdbusplus::asio::connection>& conn,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700184 const std::string& label = std::string(),
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800185 size_t thresholdSize = 0)
James Feistce3fca42018-11-21 12:58:24 -0800186 {
James Feist961bf092020-07-01 16:38:12 -0700187 if (readState == PowerState::on || readState == PowerState::biosPost)
188 {
189 setupPowerMatch(conn);
190 }
191
James Feist82bac4c2019-03-11 11:16:53 -0700192 createAssociation(association, configurationPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530193
James Feistce3fca42018-11-21 12:58:24 -0800194 sensorInterface->register_property("MaxValue", maxValue);
195 sensorInterface->register_property("MinValue", minValue);
196 sensorInterface->register_property(
James Feistd8705872019-02-08 13:26:09 -0800197 "Value", value, [&](const double& newValue, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800198 return setSensorValue(newValue, oldValue);
199 });
James Feistd8705872019-02-08 13:26:09 -0800200 for (auto& threshold : thresholds)
James Feistce3fca42018-11-21 12:58:24 -0800201 {
202 std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
203 std::string level;
204 std::string alarm;
205 if (threshold.level == thresholds::Level::CRITICAL)
206 {
207 iface = thresholdInterfaceCritical;
208 if (threshold.direction == thresholds::Direction::HIGH)
209 {
210 level = "CriticalHigh";
211 alarm = "CriticalAlarmHigh";
212 }
213 else
214 {
215 level = "CriticalLow";
216 alarm = "CriticalAlarmLow";
217 }
218 }
219 else if (threshold.level == thresholds::Level::WARNING)
220 {
221 iface = thresholdInterfaceWarning;
222 if (threshold.direction == thresholds::Direction::HIGH)
223 {
224 level = "WarningHigh";
225 alarm = "WarningAlarmHigh";
226 }
227 else
228 {
229 level = "WarningLow";
230 alarm = "WarningAlarmLow";
231 }
232 }
233 else
234 {
235 std::cerr << "Unknown threshold level" << threshold.level
236 << "\n";
237 continue;
238 }
239 if (!iface)
240 {
241 std::cout << "trying to set uninitialized interface\n";
242 continue;
243 }
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800244
245 size_t thresSize =
246 label.empty() ? thresholds.size() : thresholdSize;
James Feistce3fca42018-11-21 12:58:24 -0800247 iface->register_property(
248 level, threshold.value,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800249 [&, label, thresSize](const double& request, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800250 oldValue = request; // todo, just let the config do this?
251 threshold.value = request;
252 thresholds::persistThreshold(configurationPath, objectType,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800253 threshold, conn, thresSize,
254 label);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800255 // Invalidate previously remembered value,
256 // so new thresholds will be checked during next update,
257 // even if sensor reading remains unchanged.
258 value = std::numeric_limits<double>::quiet_NaN();
259
260 // Although tempting, don't call checkThresholds() from here
261 // directly. Let the regular sensor monitor call the same
262 // using updateValue(), which can check conditions like
263 // poweron, etc., before raising any event.
James Feistce3fca42018-11-21 12:58:24 -0800264 return 1;
265 });
266 iface->register_property(alarm, false);
267 }
268 if (!sensorInterface->initialize())
269 {
270 std::cerr << "error initializing value interface\n";
271 }
272 if (thresholdInterfaceWarning &&
Yong Lif902c052020-05-07 17:13:53 +0800273 !thresholdInterfaceWarning->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800274 {
275 std::cerr << "error initializing warning threshold interface\n";
276 }
277
278 if (thresholdInterfaceCritical &&
Yong Lif902c052020-05-07 17:13:53 +0800279 !thresholdInterfaceCritical->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800280 {
281 std::cerr << "error initializing critical threshold interface\n";
282 }
James Feist67601bd2020-06-16 17:14:44 -0700283
284 if (!availableInterface)
285 {
286 availableInterface =
287 std::make_shared<sdbusplus::asio::dbus_interface>(
288 conn, sensorInterface->get_object_path(),
289 availableInterfaceName);
290 availableInterface->register_property(
291 "Available", true, [this](const bool propIn, bool& old) {
292 if (propIn == old)
293 {
294 return 1;
295 }
James Feist961bf092020-07-01 16:38:12 -0700296 old = propIn;
James Feist67601bd2020-06-16 17:14:44 -0700297 if (!propIn)
298 {
299 updateValue(std::numeric_limits<double>::quiet_NaN());
300 }
James Feist67601bd2020-06-16 17:14:44 -0700301 return 1;
302 });
303 availableInterface->initialize();
304 }
James Feist961bf092020-07-01 16:38:12 -0700305 if (!operationalInterface)
306 {
307 operationalInterface =
308 std::make_shared<sdbusplus::asio::dbus_interface>(
309 conn, sensorInterface->get_object_path(),
310 operationalInterfaceName);
311 operationalInterface->register_property("Functional", true);
312 operationalInterface->initialize();
313 }
314 }
315
316 bool readingStateGood()
317 {
318 if (readState == PowerState::on && !isPowerOn())
319 {
320 return false;
321 }
322 if (readState == PowerState::biosPost &&
323 (!hasBiosPost() || !isPowerOn()))
324 {
325 return false;
326 }
327
328 return true;
329 }
330
331 void markFunctional(bool isFunctional)
332 {
333 if (operationalInterface)
334 {
335 operationalInterface->set_property("Functional", isFunctional);
336 }
337 if (isFunctional)
338 {
339 errCount = 0;
340 }
341 else
342 {
343 updateValue(std::numeric_limits<double>::quiet_NaN());
344 }
345 }
346
347 void markAvailable(bool isAvailable)
348 {
349 if (availableInterface)
350 {
351 availableInterface->set_property("Available", isAvailable);
352 errCount = 0;
353 }
354 }
355
356 void incrementError()
357 {
358 if (!readingStateGood())
359 {
360 markAvailable(false);
361 return;
362 }
363
364 if (errCount >= errorThreshold)
365 {
366 return;
367 }
368
369 errCount++;
370 if (errCount == errorThreshold)
371 {
372 std::cerr << "Sensor " << name << " reading error!\n";
373 markFunctional(false);
374 }
James Feistce3fca42018-11-21 12:58:24 -0800375 }
376
James Feistd8705872019-02-08 13:26:09 -0800377 void updateValue(const double& newValue)
James Feistce3fca42018-11-21 12:58:24 -0800378 {
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530379 // Ignore if overriding is enabled
James Feist961bf092020-07-01 16:38:12 -0700380 if (overriddenState)
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530381 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800382 return;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530383 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800384
James Feist961bf092020-07-01 16:38:12 -0700385 if (!readingStateGood())
386 {
387 markAvailable(false);
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200388 updateValueProperty(std::numeric_limits<double>::quiet_NaN());
James Feist961bf092020-07-01 16:38:12 -0700389 return;
390 }
391
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200392 updateValueProperty(newValue);
Josh Lehan3bcd8232020-10-29 00:22:12 -0700393 updateInstrumentation(newValue);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800394
395 // Always check thresholds after changing the value,
396 // as the test against hysteresisTrigger now takes place in
397 // the thresholds::checkThresholds() method,
398 // which is called by checkThresholds() below,
399 // in all current implementations of sensors that have thresholds.
400 checkThresholds();
James Feist961bf092020-07-01 16:38:12 -0700401 if (!std::isnan(newValue))
402 {
403 markFunctional(true);
404 markAvailable(true);
405 }
James Feistce3fca42018-11-21 12:58:24 -0800406 }
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200407
408 void updateProperty(
409 std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
410 double& oldValue, const double& newValue, const char* dbusPropertyName)
411 {
412 if (requiresUpdate(oldValue, newValue))
413 {
414 oldValue = newValue;
Jae Hyun Yoo1a540b82020-07-30 23:33:18 -0700415 if (interface &&
416 !(interface->set_property(dbusPropertyName, newValue)))
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200417 {
418 std::cerr << "error setting property " << dbusPropertyName
419 << " to " << newValue << "\n";
420 }
421 }
422 }
423
424 bool requiresUpdate(const double& lVal, const double& rVal)
425 {
426 if (std::isnan(lVal) || std::isnan(rVal))
427 {
428 return true;
429 }
430 double diff = std::abs(lVal - rVal);
431 if (diff > hysteresisPublish)
432 {
433 return true;
434 }
435 return false;
436 }
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200437
438 private:
439 void updateValueProperty(const double& newValue)
440 {
441 // Indicate that it is internal set call, not an external overwrite
442 internalSet = true;
443 updateProperty(sensorInterface, value, newValue, "Value");
444 internalSet = false;
445 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530446};