blob: d38bcde1c22e36ce430a1fab7eb07b3c56a9a56e [file] [log] [blame]
James Feist8fd8a582018-11-16 11:10:46 -08001#pragma once
Bruce Lee1263c3d2021-06-04 15:16:33 +08002#include "dbus-sensor_config.h"
James Feist8fd8a582018-11-16 11:10:46 -08003
Ed Tanous6cb732a2021-02-18 15:33:51 -08004#include <SensorPaths.hpp>
Ed Tanous8a57ec02020-10-09 12:46:52 -07005#include <Thresholds.hpp>
6#include <Utils.hpp>
James Feist38fb5982020-05-28 10:09:54 -07007#include <sdbusplus/asio/object_server.hpp>
8
Patrick Venturefd6ba732019-10-31 14:27:39 -07009#include <limits>
10#include <memory>
Patrick Venturefd6ba732019-10-31 14:27:39 -070011#include <string>
12#include <vector>
James Feist8fd8a582018-11-16 11:10:46 -080013
James Feist1169eb42018-10-31 10:08:47 -070014constexpr size_t sensorFailedPollTimeMs = 5000;
James Feista5e58722019-04-22 14:43:11 -070015
Josh Lehan3bcd8232020-10-29 00:22:12 -070016// Enable useful logging with sensor instrumentation
17// This is intentionally not DEBUG, avoid clash with usage in .cpp files
18constexpr bool enableInstrumentation = false;
19
James Feista5e58722019-04-22 14:43:11 -070020constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value";
Jie Yang3291b9c2021-07-29 14:46:51 -070021constexpr const char* valueMutabilityInterfaceName =
22 "xyz.openbmc_project.Sensor.ValueMutability";
James Feist67601bd2020-06-16 17:14:44 -070023constexpr const char* availableInterfaceName =
24 "xyz.openbmc_project.State.Decorator.Availability";
James Feist961bf092020-07-01 16:38:12 -070025constexpr const char* operationalInterfaceName =
26 "xyz.openbmc_project.State.Decorator.OperationalStatus";
27constexpr const size_t errorThreshold = 5;
28
Josh Lehan3bcd8232020-10-29 00:22:12 -070029struct SensorInstrumentation
30{
31 // These are for instrumentation for debugging
32 int numCollectsGood = 0;
33 int numCollectsMiss = 0;
34 int numStreakGreats = 0;
35 int numStreakMisses = 0;
36 double minCollected = 0.0;
37 double maxCollected = 0.0;
38};
39
James Feist8fd8a582018-11-16 11:10:46 -080040struct Sensor
41{
James Feist930fcde2019-05-28 12:58:43 -070042 Sensor(const std::string& name,
James Feistd8705872019-02-08 13:26:09 -080043 std::vector<thresholds::Threshold>&& thresholdData,
44 const std::string& configurationPath, const std::string& objectType,
Jie Yang3291b9c2021-07-29 14:46:51 -070045 bool isSettable, bool isMutable, const double max, const double min,
James Feiste3338522020-09-15 15:40:30 -070046 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist961bf092020-07-01 16:38:12 -070047 PowerState readState = PowerState::always) :
Ed Tanous6cb732a2021-02-18 15:33:51 -080048 name(sensor_paths::escapePathForDbus(name)),
James Feist930fcde2019-05-28 12:58:43 -070049 configurationPath(configurationPath), objectType(objectType),
Jie Yang3291b9c2021-07-29 14:46:51 -070050 isSensorSettable(isSettable), isValueMutable(isMutable), maxValue(max),
51 minValue(min), thresholds(std::move(thresholdData)),
Josh Lehan883fb3a2020-02-27 14:41:39 -080052 hysteresisTrigger((max - min) * 0.01),
James Feiste3338522020-09-15 15:40:30 -070053 hysteresisPublish((max - min) * 0.0001), dbusConnection(conn),
Josh Lehan3bcd8232020-10-29 00:22:12 -070054 readState(readState), errCount(0),
55 instrumentation(enableInstrumentation
56 ? std::make_unique<SensorInstrumentation>()
57 : nullptr)
James Feist38fb5982020-05-28 10:09:54 -070058 {}
James Feist8fd8a582018-11-16 11:10:46 -080059 virtual ~Sensor() = default;
James Feistce3fca42018-11-21 12:58:24 -080060 virtual void checkThresholds(void) = 0;
James Feistdc6c55f2018-10-31 12:53:20 -070061 std::string name;
James Feistce3fca42018-11-21 12:58:24 -080062 std::string configurationPath;
63 std::string objectType;
Bruce Lee1263c3d2021-06-04 15:16:33 +080064 bool isSensorSettable;
Jie Yang3291b9c2021-07-29 14:46:51 -070065
66 /* A flag indicates if properties of xyz.openbmc_project.Sensor.Value
67 * interface are mutable. If mutable, then
68 * xyz.openbmc_project.Sensor.ValueMutability interface will be
69 * instantiated.
70 */
71 bool isValueMutable;
James Feistce3fca42018-11-21 12:58:24 -080072 double maxValue;
73 double minValue;
James Feist8fd8a582018-11-16 11:10:46 -080074 std::vector<thresholds::Threshold> thresholds;
75 std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
76 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceWarning;
77 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceCritical;
James Feist078f2322019-03-08 11:09:05 -080078 std::shared_ptr<sdbusplus::asio::dbus_interface> association;
James Feist67601bd2020-06-16 17:14:44 -070079 std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
James Feist961bf092020-07-01 16:38:12 -070080 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
Jie Yang3291b9c2021-07-29 14:46:51 -070081 std::shared_ptr<sdbusplus::asio::dbus_interface> valueMutabilityInterface;
James Feist8fd8a582018-11-16 11:10:46 -080082 double value = std::numeric_limits<double>::quiet_NaN();
Zhikui Rend3da1282020-09-11 17:02:01 -070083 double rawValue = std::numeric_limits<double>::quiet_NaN();
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053084 bool overriddenState = false;
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053085 bool internalSet = false;
Josh Lehan883fb3a2020-02-27 14:41:39 -080086 double hysteresisTrigger;
87 double hysteresisPublish;
James Feiste3338522020-09-15 15:40:30 -070088 std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
James Feist961bf092020-07-01 16:38:12 -070089 PowerState readState;
90 size_t errCount;
Josh Lehan3bcd8232020-10-29 00:22:12 -070091 std::unique_ptr<SensorInstrumentation> instrumentation;
92
Josh Lehanffe18342021-03-17 13:29:51 -070093 // This member variable provides a hook that can be used to receive
94 // notification whenever this Sensor's value is externally set via D-Bus.
95 // If interested, assign your own lambda to this variable, during
96 // construction of your Sensor subclass. See ExternalSensor for example.
97 std::function<void()> externalSetHook;
98
Josh Lehan3bcd8232020-10-29 00:22:12 -070099 void updateInstrumentation(double readValue)
100 {
101 // Do nothing if this feature is not enabled
102 if constexpr (!enableInstrumentation)
103 {
104 return;
105 }
106 if (!instrumentation)
107 {
108 return;
109 }
110
111 // Save some typing
112 auto& inst = *instrumentation;
113
114 // Show constants if first reading (even if unsuccessful)
115 if ((inst.numCollectsGood == 0) && (inst.numCollectsMiss == 0))
116 {
117 std::cerr << "Sensor " << name << ": Configuration min=" << minValue
118 << ", max=" << maxValue << ", type=" << objectType
119 << ", path=" << configurationPath << "\n";
120 }
121
122 // Sensors can use "nan" to indicate unavailable reading
123 if (!std::isfinite(readValue))
124 {
125 // Only show this if beginning a new streak
126 if (inst.numStreakMisses == 0)
127 {
128 std::cerr << "Sensor " << name
129 << ": Missing reading, Reading counts good="
130 << inst.numCollectsGood
131 << ", miss=" << inst.numCollectsMiss
132 << ", Prior good streak=" << inst.numStreakGreats
133 << "\n";
134 }
135
136 inst.numStreakGreats = 0;
137 ++(inst.numCollectsMiss);
138 ++(inst.numStreakMisses);
139
140 return;
141 }
142
143 // Only show this if beginning a new streak and not the first time
144 if ((inst.numStreakGreats == 0) && (inst.numCollectsGood != 0))
145 {
146 std::cerr << "Sensor " << name
147 << ": Recovered reading, Reading counts good="
148 << inst.numCollectsGood
149 << ", miss=" << inst.numCollectsMiss
150 << ", Prior miss streak=" << inst.numStreakMisses << "\n";
151 }
152
153 // Initialize min/max if the first successful reading
154 if (inst.numCollectsGood == 0)
155 {
156 std::cerr << "Sensor " << name << ": First reading=" << readValue
157 << "\n";
158
159 inst.minCollected = readValue;
160 inst.maxCollected = readValue;
161 }
162
163 inst.numStreakMisses = 0;
164 ++(inst.numCollectsGood);
165 ++(inst.numStreakGreats);
166
167 // Only provide subsequent output if new min/max established
168 if (readValue < inst.minCollected)
169 {
170 std::cerr << "Sensor " << name << ": Lowest reading=" << readValue
171 << "\n";
172
173 inst.minCollected = readValue;
174 }
175
176 if (readValue > inst.maxCollected)
177 {
178 std::cerr << "Sensor " << name << ": Highest reading=" << readValue
179 << "\n";
180
181 inst.maxCollected = readValue;
182 }
183 }
James Feistce3fca42018-11-21 12:58:24 -0800184
James Feistd8705872019-02-08 13:26:09 -0800185 int setSensorValue(const double& newValue, double& oldValue)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530186 {
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530187 if (!internalSet)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530188 {
Bruce Lee1263c3d2021-06-04 15:16:33 +0800189 if (insecureSensorOverride == false)
190 { // insecure sesnor override.
191 if (isSensorSettable == false)
192 { // sensor is not settable.
193 if (getManufacturingMode() == false)
194 { // manufacture mode is not enable.
Patrick Williams379b1132021-09-02 05:42:25 -0500195 std::cerr << "Sensor " << name
196 << ": Not allowed to set property value.\n";
197 return -EACCES;
Bruce Lee1263c3d2021-06-04 15:16:33 +0800198 }
199 }
200 }
201
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530202 oldValue = newValue;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530203 overriddenState = true;
204 // check thresholds for external set
205 value = newValue;
206 checkThresholds();
Josh Lehanffe18342021-03-17 13:29:51 -0700207
208 // Trigger the hook, as an external set has just happened
209 if (externalSetHook)
210 {
211 externalSetHook();
212 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530213 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530214 else if (!overriddenState)
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530215 {
216 oldValue = newValue;
217 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530218 return 1;
219 }
James Feistce3fca42018-11-21 12:58:24 -0800220
221 void
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800222 setInitialProperties(std::shared_ptr<sdbusplus::asio::connection>& conn,
Zev Weiss6b6891c2021-04-22 02:46:21 -0500223 const std::string& unit,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700224 const std::string& label = std::string(),
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800225 size_t thresholdSize = 0)
James Feistce3fca42018-11-21 12:58:24 -0800226 {
James Feist961bf092020-07-01 16:38:12 -0700227 if (readState == PowerState::on || readState == PowerState::biosPost)
228 {
229 setupPowerMatch(conn);
230 }
231
James Feist82bac4c2019-03-11 11:16:53 -0700232 createAssociation(association, configurationPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530233
Zev Weiss6b6891c2021-04-22 02:46:21 -0500234 sensorInterface->register_property("Unit", unit);
James Feistce3fca42018-11-21 12:58:24 -0800235 sensorInterface->register_property("MaxValue", maxValue);
236 sensorInterface->register_property("MinValue", minValue);
237 sensorInterface->register_property(
James Feistd8705872019-02-08 13:26:09 -0800238 "Value", value, [&](const double& newValue, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800239 return setSensorValue(newValue, oldValue);
240 });
James Feistd8705872019-02-08 13:26:09 -0800241 for (auto& threshold : thresholds)
James Feistce3fca42018-11-21 12:58:24 -0800242 {
Rashmica Gupta1e34cec2021-08-31 16:47:39 +1000243 if (std::isnan(threshold.hysteresis))
244 {
245 threshold.hysteresis = hysteresisTrigger;
246 }
James Feistce3fca42018-11-21 12:58:24 -0800247 std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
248 std::string level;
249 std::string alarm;
250 if (threshold.level == thresholds::Level::CRITICAL)
251 {
252 iface = thresholdInterfaceCritical;
253 if (threshold.direction == thresholds::Direction::HIGH)
254 {
255 level = "CriticalHigh";
256 alarm = "CriticalAlarmHigh";
257 }
258 else
259 {
260 level = "CriticalLow";
261 alarm = "CriticalAlarmLow";
262 }
263 }
264 else if (threshold.level == thresholds::Level::WARNING)
265 {
266 iface = thresholdInterfaceWarning;
267 if (threshold.direction == thresholds::Direction::HIGH)
268 {
269 level = "WarningHigh";
270 alarm = "WarningAlarmHigh";
271 }
272 else
273 {
274 level = "WarningLow";
275 alarm = "WarningAlarmLow";
276 }
277 }
278 else
279 {
280 std::cerr << "Unknown threshold level" << threshold.level
281 << "\n";
282 continue;
283 }
284 if (!iface)
285 {
286 std::cout << "trying to set uninitialized interface\n";
287 continue;
288 }
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800289
290 size_t thresSize =
291 label.empty() ? thresholds.size() : thresholdSize;
James Feistce3fca42018-11-21 12:58:24 -0800292 iface->register_property(
293 level, threshold.value,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800294 [&, label, thresSize](const double& request, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800295 oldValue = request; // todo, just let the config do this?
296 threshold.value = request;
297 thresholds::persistThreshold(configurationPath, objectType,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800298 threshold, conn, thresSize,
299 label);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800300 // Invalidate previously remembered value,
301 // so new thresholds will be checked during next update,
302 // even if sensor reading remains unchanged.
303 value = std::numeric_limits<double>::quiet_NaN();
304
305 // Although tempting, don't call checkThresholds() from here
306 // directly. Let the regular sensor monitor call the same
307 // using updateValue(), which can check conditions like
308 // poweron, etc., before raising any event.
James Feistce3fca42018-11-21 12:58:24 -0800309 return 1;
310 });
311 iface->register_property(alarm, false);
312 }
313 if (!sensorInterface->initialize())
314 {
315 std::cerr << "error initializing value interface\n";
316 }
317 if (thresholdInterfaceWarning &&
Yong Lif902c052020-05-07 17:13:53 +0800318 !thresholdInterfaceWarning->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800319 {
320 std::cerr << "error initializing warning threshold interface\n";
321 }
322
323 if (thresholdInterfaceCritical &&
Yong Lif902c052020-05-07 17:13:53 +0800324 !thresholdInterfaceCritical->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800325 {
326 std::cerr << "error initializing critical threshold interface\n";
327 }
James Feist67601bd2020-06-16 17:14:44 -0700328
Jie Yang3291b9c2021-07-29 14:46:51 -0700329 if (isValueMutable)
330 {
331 valueMutabilityInterface =
332 std::make_shared<sdbusplus::asio::dbus_interface>(
333 conn, sensorInterface->get_object_path(),
334 valueMutabilityInterfaceName);
335 valueMutabilityInterface->register_property("Mutable", true);
336 if (!valueMutabilityInterface->initialize())
337 {
338 std::cerr
339 << "error initializing sensor value mutability interface\n";
340 valueMutabilityInterface = nullptr;
341 }
342 }
343
James Feist67601bd2020-06-16 17:14:44 -0700344 if (!availableInterface)
345 {
346 availableInterface =
347 std::make_shared<sdbusplus::asio::dbus_interface>(
348 conn, sensorInterface->get_object_path(),
349 availableInterfaceName);
350 availableInterface->register_property(
351 "Available", true, [this](const bool propIn, bool& old) {
352 if (propIn == old)
353 {
354 return 1;
355 }
James Feist961bf092020-07-01 16:38:12 -0700356 old = propIn;
James Feist67601bd2020-06-16 17:14:44 -0700357 if (!propIn)
358 {
359 updateValue(std::numeric_limits<double>::quiet_NaN());
360 }
James Feist67601bd2020-06-16 17:14:44 -0700361 return 1;
362 });
363 availableInterface->initialize();
364 }
James Feist961bf092020-07-01 16:38:12 -0700365 if (!operationalInterface)
366 {
367 operationalInterface =
368 std::make_shared<sdbusplus::asio::dbus_interface>(
369 conn, sensorInterface->get_object_path(),
370 operationalInterfaceName);
371 operationalInterface->register_property("Functional", true);
372 operationalInterface->initialize();
373 }
374 }
375
376 bool readingStateGood()
377 {
378 if (readState == PowerState::on && !isPowerOn())
379 {
380 return false;
381 }
382 if (readState == PowerState::biosPost &&
383 (!hasBiosPost() || !isPowerOn()))
384 {
385 return false;
386 }
387
388 return true;
389 }
390
391 void markFunctional(bool isFunctional)
392 {
393 if (operationalInterface)
394 {
395 operationalInterface->set_property("Functional", isFunctional);
396 }
397 if (isFunctional)
398 {
399 errCount = 0;
400 }
401 else
402 {
403 updateValue(std::numeric_limits<double>::quiet_NaN());
404 }
405 }
406
407 void markAvailable(bool isAvailable)
408 {
409 if (availableInterface)
410 {
411 availableInterface->set_property("Available", isAvailable);
412 errCount = 0;
413 }
414 }
415
416 void incrementError()
417 {
418 if (!readingStateGood())
419 {
420 markAvailable(false);
421 return;
422 }
423
424 if (errCount >= errorThreshold)
425 {
426 return;
427 }
428
429 errCount++;
430 if (errCount == errorThreshold)
431 {
432 std::cerr << "Sensor " << name << " reading error!\n";
433 markFunctional(false);
434 }
James Feistce3fca42018-11-21 12:58:24 -0800435 }
436
James Feistd8705872019-02-08 13:26:09 -0800437 void updateValue(const double& newValue)
James Feistce3fca42018-11-21 12:58:24 -0800438 {
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530439 // Ignore if overriding is enabled
James Feist961bf092020-07-01 16:38:12 -0700440 if (overriddenState)
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530441 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800442 return;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530443 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800444
James Feist961bf092020-07-01 16:38:12 -0700445 if (!readingStateGood())
446 {
447 markAvailable(false);
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200448 updateValueProperty(std::numeric_limits<double>::quiet_NaN());
James Feist961bf092020-07-01 16:38:12 -0700449 return;
450 }
451
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200452 updateValueProperty(newValue);
Josh Lehan3bcd8232020-10-29 00:22:12 -0700453 updateInstrumentation(newValue);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800454
455 // Always check thresholds after changing the value,
456 // as the test against hysteresisTrigger now takes place in
457 // the thresholds::checkThresholds() method,
458 // which is called by checkThresholds() below,
459 // in all current implementations of sensors that have thresholds.
460 checkThresholds();
James Feist961bf092020-07-01 16:38:12 -0700461 if (!std::isnan(newValue))
462 {
463 markFunctional(true);
464 markAvailable(true);
465 }
James Feistce3fca42018-11-21 12:58:24 -0800466 }
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200467
468 void updateProperty(
469 std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
470 double& oldValue, const double& newValue, const char* dbusPropertyName)
471 {
472 if (requiresUpdate(oldValue, newValue))
473 {
474 oldValue = newValue;
Jae Hyun Yoo1a540b82020-07-30 23:33:18 -0700475 if (interface &&
476 !(interface->set_property(dbusPropertyName, newValue)))
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200477 {
478 std::cerr << "error setting property " << dbusPropertyName
479 << " to " << newValue << "\n";
480 }
481 }
482 }
483
484 bool requiresUpdate(const double& lVal, const double& rVal)
485 {
486 if (std::isnan(lVal) || std::isnan(rVal))
487 {
488 return true;
489 }
490 double diff = std::abs(lVal - rVal);
491 if (diff > hysteresisPublish)
492 {
493 return true;
494 }
495 return false;
496 }
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200497
498 private:
499 void updateValueProperty(const double& newValue)
500 {
501 // Indicate that it is internal set call, not an external overwrite
502 internalSet = true;
503 updateProperty(sensorInterface, value, newValue, "Value");
504 internalSet = false;
505 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530506};