blob: 6fd7e43f082bdb830c34032bf870b056ccf60768 [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,
James Feiste3338522020-09-15 15:40:30 -070028 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist961bf092020-07-01 16:38:12 -070029 PowerState readState = PowerState::always) :
AppaRao Puli54ffb272020-06-02 19:09:38 +053030 name(std::regex_replace(name, std::regex("[^a-zA-Z0-9_/]+"), "_")),
James Feist930fcde2019-05-28 12:58:43 -070031 configurationPath(configurationPath), objectType(objectType),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050032 maxValue(max), minValue(min), thresholds(std::move(thresholdData)),
Josh Lehan883fb3a2020-02-27 14:41:39 -080033 hysteresisTrigger((max - min) * 0.01),
James Feiste3338522020-09-15 15:40:30 -070034 hysteresisPublish((max - min) * 0.0001), dbusConnection(conn),
35 readState(readState), errCount(0)
James Feist38fb5982020-05-28 10:09:54 -070036 {}
James Feist8fd8a582018-11-16 11:10:46 -080037 virtual ~Sensor() = default;
James Feistce3fca42018-11-21 12:58:24 -080038 virtual void checkThresholds(void) = 0;
James Feistdc6c55f2018-10-31 12:53:20 -070039 std::string name;
James Feistce3fca42018-11-21 12:58:24 -080040 std::string configurationPath;
41 std::string objectType;
42 double maxValue;
43 double minValue;
James Feist8fd8a582018-11-16 11:10:46 -080044 std::vector<thresholds::Threshold> thresholds;
45 std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
46 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceWarning;
47 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceCritical;
James Feist078f2322019-03-08 11:09:05 -080048 std::shared_ptr<sdbusplus::asio::dbus_interface> association;
James Feist67601bd2020-06-16 17:14:44 -070049 std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
James Feist961bf092020-07-01 16:38:12 -070050 std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
James Feist8fd8a582018-11-16 11:10:46 -080051 double value = std::numeric_limits<double>::quiet_NaN();
Zhikui Rend3da1282020-09-11 17:02:01 -070052 double rawValue = std::numeric_limits<double>::quiet_NaN();
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053053 bool overriddenState = false;
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053054 bool internalSet = false;
Josh Lehan883fb3a2020-02-27 14:41:39 -080055 double hysteresisTrigger;
56 double hysteresisPublish;
James Feiste3338522020-09-15 15:40:30 -070057 std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
James Feist961bf092020-07-01 16:38:12 -070058 PowerState readState;
59 size_t errCount;
James Feistce3fca42018-11-21 12:58:24 -080060
James Feistd8705872019-02-08 13:26:09 -080061 int setSensorValue(const double& newValue, double& oldValue)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053062 {
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053063 if (!internalSet)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053064 {
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053065 oldValue = newValue;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053066 overriddenState = true;
67 // check thresholds for external set
68 value = newValue;
69 checkThresholds();
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053070 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053071 else if (!overriddenState)
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053072 {
73 oldValue = newValue;
74 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053075 return 1;
76 }
James Feistce3fca42018-11-21 12:58:24 -080077
78 void
Cheng C Yang6b1247a2020-03-09 23:48:39 +080079 setInitialProperties(std::shared_ptr<sdbusplus::asio::connection>& conn,
80 const std::string label = std::string(),
81 size_t thresholdSize = 0)
James Feistce3fca42018-11-21 12:58:24 -080082 {
James Feist961bf092020-07-01 16:38:12 -070083 if (readState == PowerState::on || readState == PowerState::biosPost)
84 {
85 setupPowerMatch(conn);
86 }
87
James Feist82bac4c2019-03-11 11:16:53 -070088 createAssociation(association, configurationPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +053089
James Feistce3fca42018-11-21 12:58:24 -080090 sensorInterface->register_property("MaxValue", maxValue);
91 sensorInterface->register_property("MinValue", minValue);
92 sensorInterface->register_property(
James Feistd8705872019-02-08 13:26:09 -080093 "Value", value, [&](const double& newValue, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -080094 return setSensorValue(newValue, oldValue);
95 });
James Feistd8705872019-02-08 13:26:09 -080096 for (auto& threshold : thresholds)
James Feistce3fca42018-11-21 12:58:24 -080097 {
98 std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
99 std::string level;
100 std::string alarm;
101 if (threshold.level == thresholds::Level::CRITICAL)
102 {
103 iface = thresholdInterfaceCritical;
104 if (threshold.direction == thresholds::Direction::HIGH)
105 {
106 level = "CriticalHigh";
107 alarm = "CriticalAlarmHigh";
108 }
109 else
110 {
111 level = "CriticalLow";
112 alarm = "CriticalAlarmLow";
113 }
114 }
115 else if (threshold.level == thresholds::Level::WARNING)
116 {
117 iface = thresholdInterfaceWarning;
118 if (threshold.direction == thresholds::Direction::HIGH)
119 {
120 level = "WarningHigh";
121 alarm = "WarningAlarmHigh";
122 }
123 else
124 {
125 level = "WarningLow";
126 alarm = "WarningAlarmLow";
127 }
128 }
129 else
130 {
131 std::cerr << "Unknown threshold level" << threshold.level
132 << "\n";
133 continue;
134 }
135 if (!iface)
136 {
137 std::cout << "trying to set uninitialized interface\n";
138 continue;
139 }
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800140
141 size_t thresSize =
142 label.empty() ? thresholds.size() : thresholdSize;
James Feistce3fca42018-11-21 12:58:24 -0800143 iface->register_property(
144 level, threshold.value,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800145 [&, label, thresSize](const double& request, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800146 oldValue = request; // todo, just let the config do this?
147 threshold.value = request;
148 thresholds::persistThreshold(configurationPath, objectType,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800149 threshold, conn, thresSize,
150 label);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800151 // Invalidate previously remembered value,
152 // so new thresholds will be checked during next update,
153 // even if sensor reading remains unchanged.
154 value = std::numeric_limits<double>::quiet_NaN();
155
156 // Although tempting, don't call checkThresholds() from here
157 // directly. Let the regular sensor monitor call the same
158 // using updateValue(), which can check conditions like
159 // poweron, etc., before raising any event.
James Feistce3fca42018-11-21 12:58:24 -0800160 return 1;
161 });
162 iface->register_property(alarm, false);
163 }
164 if (!sensorInterface->initialize())
165 {
166 std::cerr << "error initializing value interface\n";
167 }
168 if (thresholdInterfaceWarning &&
Yong Lif902c052020-05-07 17:13:53 +0800169 !thresholdInterfaceWarning->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800170 {
171 std::cerr << "error initializing warning threshold interface\n";
172 }
173
174 if (thresholdInterfaceCritical &&
Yong Lif902c052020-05-07 17:13:53 +0800175 !thresholdInterfaceCritical->initialize(true))
James Feistce3fca42018-11-21 12:58:24 -0800176 {
177 std::cerr << "error initializing critical threshold interface\n";
178 }
James Feist67601bd2020-06-16 17:14:44 -0700179
180 if (!availableInterface)
181 {
182 availableInterface =
183 std::make_shared<sdbusplus::asio::dbus_interface>(
184 conn, sensorInterface->get_object_path(),
185 availableInterfaceName);
186 availableInterface->register_property(
187 "Available", true, [this](const bool propIn, bool& old) {
188 if (propIn == old)
189 {
190 return 1;
191 }
James Feist961bf092020-07-01 16:38:12 -0700192 old = propIn;
James Feist67601bd2020-06-16 17:14:44 -0700193 if (!propIn)
194 {
195 updateValue(std::numeric_limits<double>::quiet_NaN());
196 }
James Feist67601bd2020-06-16 17:14:44 -0700197 return 1;
198 });
199 availableInterface->initialize();
200 }
James Feist961bf092020-07-01 16:38:12 -0700201 if (!operationalInterface)
202 {
203 operationalInterface =
204 std::make_shared<sdbusplus::asio::dbus_interface>(
205 conn, sensorInterface->get_object_path(),
206 operationalInterfaceName);
207 operationalInterface->register_property("Functional", true);
208 operationalInterface->initialize();
209 }
210 }
211
212 bool readingStateGood()
213 {
214 if (readState == PowerState::on && !isPowerOn())
215 {
216 return false;
217 }
218 if (readState == PowerState::biosPost &&
219 (!hasBiosPost() || !isPowerOn()))
220 {
221 return false;
222 }
223
224 return true;
225 }
226
227 void markFunctional(bool isFunctional)
228 {
229 if (operationalInterface)
230 {
231 operationalInterface->set_property("Functional", isFunctional);
232 }
233 if (isFunctional)
234 {
235 errCount = 0;
236 }
237 else
238 {
239 updateValue(std::numeric_limits<double>::quiet_NaN());
240 }
241 }
242
243 void markAvailable(bool isAvailable)
244 {
245 if (availableInterface)
246 {
247 availableInterface->set_property("Available", isAvailable);
248 errCount = 0;
249 }
250 }
251
252 void incrementError()
253 {
254 if (!readingStateGood())
255 {
256 markAvailable(false);
257 return;
258 }
259
260 if (errCount >= errorThreshold)
261 {
262 return;
263 }
264
265 errCount++;
266 if (errCount == errorThreshold)
267 {
268 std::cerr << "Sensor " << name << " reading error!\n";
269 markFunctional(false);
270 }
James Feistce3fca42018-11-21 12:58:24 -0800271 }
272
James Feistd8705872019-02-08 13:26:09 -0800273 void updateValue(const double& newValue)
James Feistce3fca42018-11-21 12:58:24 -0800274 {
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530275 // Ignore if overriding is enabled
James Feist961bf092020-07-01 16:38:12 -0700276 if (overriddenState)
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530277 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800278 return;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530279 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800280
James Feist961bf092020-07-01 16:38:12 -0700281 if (!readingStateGood())
282 {
283 markAvailable(false);
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200284 updateValueProperty(std::numeric_limits<double>::quiet_NaN());
James Feist961bf092020-07-01 16:38:12 -0700285 return;
286 }
287
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200288 updateValueProperty(newValue);
Josh Lehan883fb3a2020-02-27 14:41:39 -0800289
290 // Always check thresholds after changing the value,
291 // as the test against hysteresisTrigger now takes place in
292 // the thresholds::checkThresholds() method,
293 // which is called by checkThresholds() below,
294 // in all current implementations of sensors that have thresholds.
295 checkThresholds();
James Feist961bf092020-07-01 16:38:12 -0700296 if (!std::isnan(newValue))
297 {
298 markFunctional(true);
299 markAvailable(true);
300 }
James Feistce3fca42018-11-21 12:58:24 -0800301 }
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200302
303 void updateProperty(
304 std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
305 double& oldValue, const double& newValue, const char* dbusPropertyName)
306 {
307 if (requiresUpdate(oldValue, newValue))
308 {
309 oldValue = newValue;
Jae Hyun Yoo1a540b82020-07-30 23:33:18 -0700310 if (interface &&
311 !(interface->set_property(dbusPropertyName, newValue)))
Zbigniew Kurzynski4f45e422020-06-09 12:42:15 +0200312 {
313 std::cerr << "error setting property " << dbusPropertyName
314 << " to " << newValue << "\n";
315 }
316 }
317 }
318
319 bool requiresUpdate(const double& lVal, const double& rVal)
320 {
321 if (std::isnan(lVal) || std::isnan(rVal))
322 {
323 return true;
324 }
325 double diff = std::abs(lVal - rVal);
326 if (diff > hysteresisPublish)
327 {
328 return true;
329 }
330 return false;
331 }
Adrian Ambrożewicz623723b2020-07-29 12:53:54 +0200332
333 private:
334 void updateValueProperty(const double& newValue)
335 {
336 // Indicate that it is internal set call, not an external overwrite
337 internalSet = true;
338 updateProperty(sensorInterface, value, newValue, "Value");
339 internalSet = false;
340 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530341};