blob: 3df71183a647a62a66e6fe541ca8604dbd185383 [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";
James Feist67601bd2020-06-16 17:14:44 -070021constexpr const char* availableInterfaceName =
22 "xyz.openbmc_project.State.Decorator.Availability";
James Feist961bf092020-07-01 16:38:12 -070023constexpr const char* operationalInterfaceName =
24 "xyz.openbmc_project.State.Decorator.OperationalStatus";
25constexpr const size_t errorThreshold = 5;
26
Josh Lehan3bcd8232020-10-29 00:22:12 -070027struct SensorInstrumentation
28{
29 // These are for instrumentation for debugging
30 int numCollectsGood = 0;
31 int numCollectsMiss = 0;
32 int numStreakGreats = 0;
33 int numStreakMisses = 0;
34 double minCollected = 0.0;
35 double maxCollected = 0.0;
36};
37
James Feist8fd8a582018-11-16 11:10:46 -080038struct Sensor
39{
James Feist930fcde2019-05-28 12:58:43 -070040 Sensor(const std::string& name,
James Feistd8705872019-02-08 13:26:09 -080041 std::vector<thresholds::Threshold>&& thresholdData,
42 const std::string& configurationPath, const std::string& objectType,
Bruce Lee1263c3d2021-06-04 15:16:33 +080043 bool isSettable, const double max, const double min,
James Feiste3338522020-09-15 15:40:30 -070044 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist961bf092020-07-01 16:38:12 -070045 PowerState readState = PowerState::always) :
Ed Tanous6cb732a2021-02-18 15:33:51 -080046 name(sensor_paths::escapePathForDbus(name)),
James Feist930fcde2019-05-28 12:58:43 -070047 configurationPath(configurationPath), objectType(objectType),
Bruce Lee1263c3d2021-06-04 15:16:33 +080048 isSensorSettable(isSettable), maxValue(max), minValue(min),
49 thresholds(std::move(thresholdData)),
Josh Lehan883fb3a2020-02-27 14:41:39 -080050 hysteresisTrigger((max - min) * 0.01),
James Feiste3338522020-09-15 15:40:30 -070051 hysteresisPublish((max - min) * 0.0001), dbusConnection(conn),
Josh Lehan3bcd8232020-10-29 00:22:12 -070052 readState(readState), errCount(0),
53 instrumentation(enableInstrumentation
54 ? std::make_unique<SensorInstrumentation>()
55 : nullptr)
James Feist38fb5982020-05-28 10:09:54 -070056 {}
James Feist8fd8a582018-11-16 11:10:46 -080057 virtual ~Sensor() = default;
James Feistce3fca42018-11-21 12:58:24 -080058 virtual void checkThresholds(void) = 0;
James Feistdc6c55f2018-10-31 12:53:20 -070059 std::string name;
James Feistce3fca42018-11-21 12:58:24 -080060 std::string configurationPath;
61 std::string objectType;
Bruce Lee1263c3d2021-06-04 15:16:33 +080062 bool isSensorSettable;
James Feistce3fca42018-11-21 12:58:24 -080063 double maxValue;
64 double minValue;
James Feist8fd8a582018-11-16 11:10:46 -080065 std::vector<thresholds::Threshold> thresholds;
66 std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
67 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceWarning;
68 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceCritical;
James Feist078f2322019-03-08 11:09:05 -080069 std::shared_ptr<sdbusplus::asio::dbus_interface> association;
James Feist67601bd2020-06-16 17:14:44 -070070 std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
James Feist961bf092020-07-01 16:38:12 -070071 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
James Feist8fd8a582018-11-16 11:10:46 -080072 double value = std::numeric_limits<double>::quiet_NaN();
Zhikui Rend3da1282020-09-11 17:02:01 -070073 double rawValue = std::numeric_limits<double>::quiet_NaN();
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053074 bool overriddenState = false;
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053075 bool internalSet = false;
Josh Lehan883fb3a2020-02-27 14:41:39 -080076 double hysteresisTrigger;
77 double hysteresisPublish;
James Feiste3338522020-09-15 15:40:30 -070078 std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
James Feist961bf092020-07-01 16:38:12 -070079 PowerState readState;
80 size_t errCount;
Josh Lehan3bcd8232020-10-29 00:22:12 -070081 std::unique_ptr<SensorInstrumentation> instrumentation;
82
Josh Lehanffe18342021-03-17 13:29:51 -070083 // This member variable provides a hook that can be used to receive
84 // notification whenever this Sensor's value is externally set via D-Bus.
85 // If interested, assign your own lambda to this variable, during
86 // construction of your Sensor subclass. See ExternalSensor for example.
87 std::function<void()> externalSetHook;
88
Josh Lehan3bcd8232020-10-29 00:22:12 -070089 void updateInstrumentation(double readValue)
90 {
91 // Do nothing if this feature is not enabled
92 if constexpr (!enableInstrumentation)
93 {
94 return;
95 }
96 if (!instrumentation)
97 {
98 return;
99 }
100
101 // Save some typing
102 auto& inst = *instrumentation;
103
104 // Show constants if first reading (even if unsuccessful)
105 if ((inst.numCollectsGood == 0) && (inst.numCollectsMiss == 0))
106 {
107 std::cerr << "Sensor " << name << ": Configuration min=" << minValue
108 << ", max=" << maxValue << ", type=" << objectType
109 << ", path=" << configurationPath << "\n";
110 }
111
112 // Sensors can use "nan" to indicate unavailable reading
113 if (!std::isfinite(readValue))
114 {
115 // Only show this if beginning a new streak
116 if (inst.numStreakMisses == 0)
117 {
118 std::cerr << "Sensor " << name
119 << ": Missing reading, Reading counts good="
120 << inst.numCollectsGood
121 << ", miss=" << inst.numCollectsMiss
122 << ", Prior good streak=" << inst.numStreakGreats
123 << "\n";
124 }
125
126 inst.numStreakGreats = 0;
127 ++(inst.numCollectsMiss);
128 ++(inst.numStreakMisses);
129
130 return;
131 }
132
133 // Only show this if beginning a new streak and not the first time
134 if ((inst.numStreakGreats == 0) && (inst.numCollectsGood != 0))
135 {
136 std::cerr << "Sensor " << name
137 << ": Recovered reading, Reading counts good="
138 << inst.numCollectsGood
139 << ", miss=" << inst.numCollectsMiss
140 << ", Prior miss streak=" << inst.numStreakMisses << "\n";
141 }
142
143 // Initialize min/max if the first successful reading
144 if (inst.numCollectsGood == 0)
145 {
146 std::cerr << "Sensor " << name << ": First reading=" << readValue
147 << "\n";
148
149 inst.minCollected = readValue;
150 inst.maxCollected = readValue;
151 }
152
153 inst.numStreakMisses = 0;
154 ++(inst.numCollectsGood);
155 ++(inst.numStreakGreats);
156
157 // Only provide subsequent output if new min/max established
158 if (readValue < inst.minCollected)
159 {
160 std::cerr << "Sensor " << name << ": Lowest reading=" << readValue
161 << "\n";
162
163 inst.minCollected = readValue;
164 }
165
166 if (readValue > inst.maxCollected)
167 {
168 std::cerr << "Sensor " << name << ": Highest reading=" << readValue
169 << "\n";
170
171 inst.maxCollected = readValue;
172 }
173 }
James Feistce3fca42018-11-21 12:58:24 -0800174
James Feistd8705872019-02-08 13:26:09 -0800175 int setSensorValue(const double& newValue, double& oldValue)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530176 {
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530177 if (!internalSet)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530178 {
Bruce Lee1263c3d2021-06-04 15:16:33 +0800179 if (insecureSensorOverride == false)
180 { // insecure sesnor override.
181 if (isSensorSettable == false)
182 { // sensor is not settable.
183 if (getManufacturingMode() == false)
184 { // manufacture mode is not enable.
Patrick Williams379b1132021-09-02 05:42:25 -0500185 std::cerr << "Sensor " << name
186 << ": Not allowed to set property value.\n";
187 return -EACCES;
Bruce Lee1263c3d2021-06-04 15:16:33 +0800188 }
189 }
190 }
191
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530192 oldValue = newValue;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530193 overriddenState = true;
194 // check thresholds for external set
195 value = newValue;
196 checkThresholds();
Josh Lehanffe18342021-03-17 13:29:51 -0700197
198 // Trigger the hook, as an external set has just happened
199 if (externalSetHook)
200 {
201 externalSetHook();
202 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530203 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530204 else if (!overriddenState)
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530205 {
206 oldValue = newValue;
207 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530208 return 1;
209 }
James Feistce3fca42018-11-21 12:58:24 -0800210
211 void
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800212 setInitialProperties(std::shared_ptr<sdbusplus::asio::connection>& conn,
Zev Weiss6b6891c2021-04-22 02:46:21 -0500213 const std::string& unit,
Ed Tanous8a57ec02020-10-09 12:46:52 -0700214 const std::string& label = std::string(),
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800215 size_t thresholdSize = 0)
James Feistce3fca42018-11-21 12:58:24 -0800216 {
James Feist961bf092020-07-01 16:38:12 -0700217 if (readState == PowerState::on || readState == PowerState::biosPost)
218 {
219 setupPowerMatch(conn);
220 }
221
James Feist82bac4c2019-03-11 11:16:53 -0700222 createAssociation(association, configurationPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +0530223
Zev Weiss6b6891c2021-04-22 02:46:21 -0500224 sensorInterface->register_property("Unit", unit);
James Feistce3fca42018-11-21 12:58:24 -0800225 sensorInterface->register_property("MaxValue", maxValue);
226 sensorInterface->register_property("MinValue", minValue);
227 sensorInterface->register_property(
James Feistd8705872019-02-08 13:26:09 -0800228 "Value", value, [&](const double& newValue, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800229 return setSensorValue(newValue, oldValue);
230 });
James Feistd8705872019-02-08 13:26:09 -0800231 for (auto& threshold : thresholds)
James Feistce3fca42018-11-21 12:58:24 -0800232 {
Rashmica Gupta1e34cec2021-08-31 16:47:39 +1000233 if (std::isnan(threshold.hysteresis))
234 {
235 threshold.hysteresis = hysteresisTrigger;
236 }
James Feistce3fca42018-11-21 12:58:24 -0800237 std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
238 std::string level;
239 std::string alarm;
240 if (threshold.level == thresholds::Level::CRITICAL)
241 {
242 iface = thresholdInterfaceCritical;
243 if (threshold.direction == thresholds::Direction::HIGH)
244 {
245 level = "CriticalHigh";
246 alarm = "CriticalAlarmHigh";
247 }
248 else
249 {
250 level = "CriticalLow";
251 alarm = "CriticalAlarmLow";
252 }
253 }
254 else if (threshold.level == thresholds::Level::WARNING)
255 {
256 iface = thresholdInterfaceWarning;
257 if (threshold.direction == thresholds::Direction::HIGH)
258 {
259 level = "WarningHigh";
260 alarm = "WarningAlarmHigh";
261 }
262 else
263 {
264 level = "WarningLow";
265 alarm = "WarningAlarmLow";
266 }
267 }
268 else
269 {
270 std::cerr << "Unknown threshold level" << threshold.level
271 << "\n";
272 continue;
273 }
274 if (!iface)
275 {
276 std::cout << "trying to set uninitialized interface\n";
277 continue;
278 }
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800279
280 size_t thresSize =
281 label.empty() ? thresholds.size() : thresholdSize;
James Feistce3fca42018-11-21 12:58:24 -0800282 iface->register_property(
283 level, threshold.value,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800284 [&, label, thresSize](const double& request, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800285 oldValue = request; // todo, just let the config do this?
286 threshold.value = request;
287 thresholds::persistThreshold(configurationPath, objectType,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800288 threshold, conn, thresSize,
289 label);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800290 // Invalidate previously remembered value,
291 // so new thresholds will be checked during next update,
292 // even if sensor reading remains unchanged.
293 value = std::numeric_limits<double>::quiet_NaN();
294
295 // Although tempting, don't call checkThresholds() from here
296 // directly. Let the regular sensor monitor call the same
297 // using updateValue(), which can check conditions like
298 // poweron, etc., before raising any event.
James Feistce3fca42018-11-21 12:58:24 -0800299 return 1;
300 });
301 iface->register_property(alarm, false);
302 }
303 if (!sensorInterface->initialize())
304 {
305 std::cerr << "error initializing value interface\n";
306 }
307 if (thresholdInterfaceWarning &&
Yong Lif902c052020-05-07 17:13:53 +0800308 !thresholdInterfaceWarning->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800309 {
310 std::cerr << "error initializing warning threshold interface\n";
311 }
312
313 if (thresholdInterfaceCritical &&
Yong Lif902c052020-05-07 17:13:53 +0800314 !thresholdInterfaceCritical->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800315 {
316 std::cerr << "error initializing critical threshold interface\n";
317 }
James Feist67601bd2020-06-16 17:14:44 -0700318
319 if (!availableInterface)
320 {
321 availableInterface =
322 std::make_shared<sdbusplus::asio::dbus_interface>(
323 conn, sensorInterface->get_object_path(),
324 availableInterfaceName);
325 availableInterface->register_property(
326 "Available", true, [this](const bool propIn, bool& old) {
327 if (propIn == old)
328 {
329 return 1;
330 }
James Feist961bf092020-07-01 16:38:12 -0700331 old = propIn;
James Feist67601bd2020-06-16 17:14:44 -0700332 if (!propIn)
333 {
334 updateValue(std::numeric_limits<double>::quiet_NaN());
335 }
James Feist67601bd2020-06-16 17:14:44 -0700336 return 1;
337 });
338 availableInterface->initialize();
339 }
James Feist961bf092020-07-01 16:38:12 -0700340 if (!operationalInterface)
341 {
342 operationalInterface =
343 std::make_shared<sdbusplus::asio::dbus_interface>(
344 conn, sensorInterface->get_object_path(),
345 operationalInterfaceName);
346 operationalInterface->register_property("Functional", true);
347 operationalInterface->initialize();
348 }
349 }
350
351 bool readingStateGood()
352 {
353 if (readState == PowerState::on && !isPowerOn())
354 {
355 return false;
356 }
357 if (readState == PowerState::biosPost &&
358 (!hasBiosPost() || !isPowerOn()))
359 {
360 return false;
361 }
362
363 return true;
364 }
365
366 void markFunctional(bool isFunctional)
367 {
368 if (operationalInterface)
369 {
370 operationalInterface->set_property("Functional", isFunctional);
371 }
372 if (isFunctional)
373 {
374 errCount = 0;
375 }
376 else
377 {
378 updateValue(std::numeric_limits<double>::quiet_NaN());
379 }
380 }
381
382 void markAvailable(bool isAvailable)
383 {
384 if (availableInterface)
385 {
386 availableInterface->set_property("Available", isAvailable);
387 errCount = 0;
388 }
389 }
390
391 void incrementError()
392 {
393 if (!readingStateGood())
394 {
395 markAvailable(false);
396 return;
397 }
398
399 if (errCount >= errorThreshold)
400 {
401 return;
402 }
403
404 errCount++;
405 if (errCount == errorThreshold)
406 {
407 std::cerr << "Sensor " << name << " reading error!\n";
408 markFunctional(false);
409 }
James Feistce3fca42018-11-21 12:58:24 -0800410 }
411
James Feistd8705872019-02-08 13:26:09 -0800412 void updateValue(const double& newValue)
James Feistce3fca42018-11-21 12:58:24 -0800413 {
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530414 // Ignore if overriding is enabled
James Feist961bf092020-07-01 16:38:12 -0700415 if (overriddenState)
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530416 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800417 return;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530418 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800419
James Feist961bf092020-07-01 16:38:12 -0700420 if (!readingStateGood())
421 {
422 markAvailable(false);
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200423 updateValueProperty(std::numeric_limits<double>::quiet_NaN());
James Feist961bf092020-07-01 16:38:12 -0700424 return;
425 }
426
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200427 updateValueProperty(newValue);
Josh Lehan3bcd8232020-10-29 00:22:12 -0700428 updateInstrumentation(newValue);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800429
430 // Always check thresholds after changing the value,
431 // as the test against hysteresisTrigger now takes place in
432 // the thresholds::checkThresholds() method,
433 // which is called by checkThresholds() below,
434 // in all current implementations of sensors that have thresholds.
435 checkThresholds();
James Feist961bf092020-07-01 16:38:12 -0700436 if (!std::isnan(newValue))
437 {
438 markFunctional(true);
439 markAvailable(true);
440 }
James Feistce3fca42018-11-21 12:58:24 -0800441 }
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200442
443 void updateProperty(
444 std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
445 double& oldValue, const double& newValue, const char* dbusPropertyName)
446 {
447 if (requiresUpdate(oldValue, newValue))
448 {
449 oldValue = newValue;
Jae Hyun Yoo1a540b82020-07-30 23:33:18 -0700450 if (interface &&
451 !(interface->set_property(dbusPropertyName, newValue)))
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200452 {
453 std::cerr << "error setting property " << dbusPropertyName
454 << " to " << newValue << "\n";
455 }
456 }
457 }
458
459 bool requiresUpdate(const double& lVal, const double& rVal)
460 {
461 if (std::isnan(lVal) || std::isnan(rVal))
462 {
463 return true;
464 }
465 double diff = std::abs(lVal - rVal);
466 if (diff > hysteresisPublish)
467 {
468 return true;
469 }
470 return false;
471 }
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200472
473 private:
474 void updateValueProperty(const double& newValue)
475 {
476 // Indicate that it is internal set call, not an external overwrite
477 internalSet = true;
478 updateProperty(sensorInterface, value, newValue, "Value");
479 internalSet = false;
480 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530481};