xref: /openbmc/dbus-sensors/src/sensor.hpp (revision 556e04b8f374a9eb8cf32bf0e36ac46c14873eba)
1e73bd0a1SAndrew Jeffery #pragma once
2e73bd0a1SAndrew Jeffery 
3e73bd0a1SAndrew Jeffery #include "dbus-sensor_config.h"
4e73bd0a1SAndrew Jeffery 
5e73bd0a1SAndrew Jeffery #include "SensorPaths.hpp"
6e73bd0a1SAndrew Jeffery #include "Thresholds.hpp"
7e73bd0a1SAndrew Jeffery #include "Utils.hpp"
8e73bd0a1SAndrew Jeffery 
918b6186eSEd Tanous #include <sdbusplus/asio/connection.hpp>
10e73bd0a1SAndrew Jeffery #include <sdbusplus/asio/object_server.hpp>
11e73bd0a1SAndrew Jeffery #include <sdbusplus/exception.hpp>
12e73bd0a1SAndrew Jeffery 
1318b6186eSEd Tanous #include <array>
1418b6186eSEd Tanous #include <cerrno>
1518b6186eSEd Tanous #include <cmath>
1618b6186eSEd Tanous #include <cstddef>
1718b6186eSEd Tanous #include <cstdlib>
1818b6186eSEd Tanous #include <functional>
1918b6186eSEd Tanous #include <iostream>
20e73bd0a1SAndrew Jeffery #include <limits>
21e73bd0a1SAndrew Jeffery #include <memory>
22e73bd0a1SAndrew Jeffery #include <string>
2318b6186eSEd Tanous #include <utility>
24e73bd0a1SAndrew Jeffery #include <vector>
25e73bd0a1SAndrew Jeffery 
26e73bd0a1SAndrew Jeffery constexpr size_t sensorFailedPollTimeMs = 5000;
27e73bd0a1SAndrew Jeffery 
28e73bd0a1SAndrew Jeffery // Enable useful logging with sensor instrumentation
29e73bd0a1SAndrew Jeffery // This is intentionally not DEBUG, avoid clash with usage in .cpp files
30e73bd0a1SAndrew Jeffery constexpr bool enableInstrumentation = false;
31e73bd0a1SAndrew Jeffery 
32e73bd0a1SAndrew Jeffery constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value";
33e73bd0a1SAndrew Jeffery constexpr const char* valueMutabilityInterfaceName =
34e73bd0a1SAndrew Jeffery     "xyz.openbmc_project.Sensor.ValueMutability";
35e73bd0a1SAndrew Jeffery constexpr const char* availableInterfaceName =
36e73bd0a1SAndrew Jeffery     "xyz.openbmc_project.State.Decorator.Availability";
37e73bd0a1SAndrew Jeffery constexpr const char* operationalInterfaceName =
38e73bd0a1SAndrew Jeffery     "xyz.openbmc_project.State.Decorator.OperationalStatus";
39e73bd0a1SAndrew Jeffery constexpr const size_t errorThreshold = 5;
40e73bd0a1SAndrew Jeffery 
41e73bd0a1SAndrew Jeffery struct SensorInstrumentation
42e73bd0a1SAndrew Jeffery {
43e73bd0a1SAndrew Jeffery     // These are for instrumentation for debugging
44e73bd0a1SAndrew Jeffery     int numCollectsGood = 0;
45e73bd0a1SAndrew Jeffery     int numCollectsMiss = 0;
46e73bd0a1SAndrew Jeffery     int numStreakGreats = 0;
47e73bd0a1SAndrew Jeffery     int numStreakMisses = 0;
48e73bd0a1SAndrew Jeffery     double minCollected = 0.0;
49e73bd0a1SAndrew Jeffery     double maxCollected = 0.0;
50e73bd0a1SAndrew Jeffery };
51e73bd0a1SAndrew Jeffery 
52e73bd0a1SAndrew Jeffery struct SetSensorError : sdbusplus::exception_t
53e73bd0a1SAndrew Jeffery {
nameSetSensorError54e73bd0a1SAndrew Jeffery     const char* name() const noexcept override
55e73bd0a1SAndrew Jeffery     {
56e73bd0a1SAndrew Jeffery         return "xyz.openbmc_project.Common.Errors.NotAllowed";
57e73bd0a1SAndrew Jeffery     }
descriptionSetSensorError58e73bd0a1SAndrew Jeffery     const char* description() const noexcept override
59e73bd0a1SAndrew Jeffery     {
60e73bd0a1SAndrew Jeffery         return "Not allowed to set property value.";
61e73bd0a1SAndrew Jeffery     }
get_errnoSetSensorError62e73bd0a1SAndrew Jeffery     int get_errno() const noexcept override
63e73bd0a1SAndrew Jeffery     {
64e73bd0a1SAndrew Jeffery         return EACCES;
65e73bd0a1SAndrew Jeffery     }
66e73bd0a1SAndrew Jeffery };
67e73bd0a1SAndrew Jeffery 
68e73bd0a1SAndrew Jeffery struct Sensor
69e73bd0a1SAndrew Jeffery {
SensorSensor70e73bd0a1SAndrew Jeffery     Sensor(const std::string& name,
71e73bd0a1SAndrew Jeffery            std::vector<thresholds::Threshold>&& thresholdData,
72e73bd0a1SAndrew Jeffery            const std::string& configurationPath, const std::string& objectType,
73e73bd0a1SAndrew Jeffery            bool isSettable, bool isMutable, const double max, const double min,
74e73bd0a1SAndrew Jeffery            std::shared_ptr<sdbusplus::asio::connection>& conn,
75e73bd0a1SAndrew Jeffery            PowerState readState = PowerState::always) :
76e73bd0a1SAndrew Jeffery         name(sensor_paths::escapePathForDbus(name)),
77e73bd0a1SAndrew Jeffery         configurationPath(configurationPath),
7855832f37SMatt Spinler         configInterface(configInterfaceName(objectType)),
79e73bd0a1SAndrew Jeffery         isSensorSettable(isSettable), isValueMutable(isMutable), maxValue(max),
80e73bd0a1SAndrew Jeffery         minValue(min), thresholds(std::move(thresholdData)),
81e73bd0a1SAndrew Jeffery         hysteresisTrigger((max - min) * 0.01),
82e73bd0a1SAndrew Jeffery         hysteresisPublish((max - min) * 0.0001), dbusConnection(conn),
83e73bd0a1SAndrew Jeffery         readState(readState),
84e73bd0a1SAndrew Jeffery         instrumentation(enableInstrumentation
85e73bd0a1SAndrew Jeffery                             ? std::make_unique<SensorInstrumentation>()
86e73bd0a1SAndrew Jeffery                             : nullptr)
87e73bd0a1SAndrew Jeffery     {}
88e73bd0a1SAndrew Jeffery     virtual ~Sensor() = default;
89201a1015SEd Tanous     virtual void checkThresholds() = 0;
90e73bd0a1SAndrew Jeffery     std::string name;
91e73bd0a1SAndrew Jeffery     std::string configurationPath;
9255832f37SMatt Spinler     std::string configInterface;
93e73bd0a1SAndrew Jeffery     bool isSensorSettable;
94e73bd0a1SAndrew Jeffery 
95e73bd0a1SAndrew Jeffery     /* A flag indicates if properties of xyz.openbmc_project.Sensor.Value
96e73bd0a1SAndrew Jeffery      * interface are mutable. If mutable, then
97e73bd0a1SAndrew Jeffery      * xyz.openbmc_project.Sensor.ValueMutability interface will be
98e73bd0a1SAndrew Jeffery      * instantiated.
99e73bd0a1SAndrew Jeffery      */
100e73bd0a1SAndrew Jeffery     bool isValueMutable;
101e73bd0a1SAndrew Jeffery     double maxValue;
102e73bd0a1SAndrew Jeffery     double minValue;
103e73bd0a1SAndrew Jeffery     std::vector<thresholds::Threshold> thresholds;
104e73bd0a1SAndrew Jeffery     std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
105e73bd0a1SAndrew Jeffery     std::shared_ptr<sdbusplus::asio::dbus_interface> association;
106e73bd0a1SAndrew Jeffery     std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface;
107e73bd0a1SAndrew Jeffery     std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface;
108e73bd0a1SAndrew Jeffery     std::shared_ptr<sdbusplus::asio::dbus_interface> valueMutabilityInterface;
109e73bd0a1SAndrew Jeffery     double value = std::numeric_limits<double>::quiet_NaN();
110e73bd0a1SAndrew Jeffery     double rawValue = std::numeric_limits<double>::quiet_NaN();
111e73bd0a1SAndrew Jeffery     bool overriddenState = false;
112e73bd0a1SAndrew Jeffery     bool internalSet = false;
113e73bd0a1SAndrew Jeffery     double hysteresisTrigger;
114e73bd0a1SAndrew Jeffery     double hysteresisPublish;
115e73bd0a1SAndrew Jeffery     std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
116e73bd0a1SAndrew Jeffery     PowerState readState;
117e73bd0a1SAndrew Jeffery     size_t errCount{0};
118e73bd0a1SAndrew Jeffery     std::unique_ptr<SensorInstrumentation> instrumentation;
119e73bd0a1SAndrew Jeffery 
120e73bd0a1SAndrew Jeffery     // This member variable provides a hook that can be used to receive
121e73bd0a1SAndrew Jeffery     // notification whenever this Sensor's value is externally set via D-Bus.
122e73bd0a1SAndrew Jeffery     // If interested, assign your own lambda to this variable, during
123e73bd0a1SAndrew Jeffery     // construction of your Sensor subclass. See ExternalSensor for example.
124e73bd0a1SAndrew Jeffery     std::function<void()> externalSetHook;
125e73bd0a1SAndrew Jeffery 
126e73bd0a1SAndrew Jeffery     using Level = thresholds::Level;
127e73bd0a1SAndrew Jeffery     using Direction = thresholds::Direction;
128e73bd0a1SAndrew Jeffery 
129e73bd0a1SAndrew Jeffery     std::array<std::shared_ptr<sdbusplus::asio::dbus_interface>,
130e73bd0a1SAndrew Jeffery                thresholds::thresProp.size()>
131e73bd0a1SAndrew Jeffery         thresholdInterfaces;
132e73bd0a1SAndrew Jeffery 
getThresholdInterfaceSensor133*556e04b8SPatrick Williams     std::shared_ptr<sdbusplus::asio::dbus_interface> getThresholdInterface(
134*556e04b8SPatrick Williams         Level lev)
135e73bd0a1SAndrew Jeffery     {
136e73bd0a1SAndrew Jeffery         size_t index = static_cast<size_t>(lev);
137e73bd0a1SAndrew Jeffery         if (index >= thresholdInterfaces.size())
138e73bd0a1SAndrew Jeffery         {
139e73bd0a1SAndrew Jeffery             std::cout << "Unknown threshold level \n";
140e73bd0a1SAndrew Jeffery             return nullptr;
141e73bd0a1SAndrew Jeffery         }
142e73bd0a1SAndrew Jeffery         std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
143e73bd0a1SAndrew Jeffery             thresholdInterfaces[index];
144e73bd0a1SAndrew Jeffery         return interface;
145e73bd0a1SAndrew Jeffery     }
146e73bd0a1SAndrew Jeffery 
updateInstrumentationSensor147e73bd0a1SAndrew Jeffery     void updateInstrumentation(double readValue) const
148e73bd0a1SAndrew Jeffery     {
149e73bd0a1SAndrew Jeffery         // Do nothing if this feature is not enabled
150e73bd0a1SAndrew Jeffery         if constexpr (!enableInstrumentation)
151e73bd0a1SAndrew Jeffery         {
152e73bd0a1SAndrew Jeffery             return;
153e73bd0a1SAndrew Jeffery         }
154e73bd0a1SAndrew Jeffery         if (!instrumentation)
155e73bd0a1SAndrew Jeffery         {
156e73bd0a1SAndrew Jeffery             return;
157e73bd0a1SAndrew Jeffery         }
158e73bd0a1SAndrew Jeffery 
159e73bd0a1SAndrew Jeffery         // Save some typing
160e73bd0a1SAndrew Jeffery         auto& inst = *instrumentation;
161e73bd0a1SAndrew Jeffery 
162e73bd0a1SAndrew Jeffery         // Show constants if first reading (even if unsuccessful)
163e73bd0a1SAndrew Jeffery         if ((inst.numCollectsGood == 0) && (inst.numCollectsMiss == 0))
164e73bd0a1SAndrew Jeffery         {
165e73bd0a1SAndrew Jeffery             std::cerr << "Sensor " << name << ": Configuration min=" << minValue
16655832f37SMatt Spinler                       << ", max=" << maxValue << ", type=" << configInterface
167e73bd0a1SAndrew Jeffery                       << ", path=" << configurationPath << "\n";
168e73bd0a1SAndrew Jeffery         }
169e73bd0a1SAndrew Jeffery 
170e73bd0a1SAndrew Jeffery         // Sensors can use "nan" to indicate unavailable reading
171e73bd0a1SAndrew Jeffery         if (!std::isfinite(readValue))
172e73bd0a1SAndrew Jeffery         {
173e73bd0a1SAndrew Jeffery             // Only show this if beginning a new streak
174e73bd0a1SAndrew Jeffery             if (inst.numStreakMisses == 0)
175e73bd0a1SAndrew Jeffery             {
176e73bd0a1SAndrew Jeffery                 std::cerr << "Sensor " << name
177e73bd0a1SAndrew Jeffery                           << ": Missing reading, Reading counts good="
178e73bd0a1SAndrew Jeffery                           << inst.numCollectsGood
179e73bd0a1SAndrew Jeffery                           << ", miss=" << inst.numCollectsMiss
180e73bd0a1SAndrew Jeffery                           << ", Prior good streak=" << inst.numStreakGreats
181e73bd0a1SAndrew Jeffery                           << "\n";
182e73bd0a1SAndrew Jeffery             }
183e73bd0a1SAndrew Jeffery 
184e73bd0a1SAndrew Jeffery             inst.numStreakGreats = 0;
185e73bd0a1SAndrew Jeffery             ++(inst.numCollectsMiss);
186e73bd0a1SAndrew Jeffery             ++(inst.numStreakMisses);
187e73bd0a1SAndrew Jeffery 
188e73bd0a1SAndrew Jeffery             return;
189e73bd0a1SAndrew Jeffery         }
190e73bd0a1SAndrew Jeffery 
191e73bd0a1SAndrew Jeffery         // Only show this if beginning a new streak and not the first time
192e73bd0a1SAndrew Jeffery         if ((inst.numStreakGreats == 0) && (inst.numCollectsGood != 0))
193e73bd0a1SAndrew Jeffery         {
194e73bd0a1SAndrew Jeffery             std::cerr << "Sensor " << name
195e73bd0a1SAndrew Jeffery                       << ": Recovered reading, Reading counts good="
196e73bd0a1SAndrew Jeffery                       << inst.numCollectsGood
197e73bd0a1SAndrew Jeffery                       << ", miss=" << inst.numCollectsMiss
198e73bd0a1SAndrew Jeffery                       << ", Prior miss streak=" << inst.numStreakMisses << "\n";
199e73bd0a1SAndrew Jeffery         }
200e73bd0a1SAndrew Jeffery 
201e73bd0a1SAndrew Jeffery         // Initialize min/max if the first successful reading
202e73bd0a1SAndrew Jeffery         if (inst.numCollectsGood == 0)
203e73bd0a1SAndrew Jeffery         {
204e73bd0a1SAndrew Jeffery             std::cerr << "Sensor " << name << ": First reading=" << readValue
205e73bd0a1SAndrew Jeffery                       << "\n";
206e73bd0a1SAndrew Jeffery 
207e73bd0a1SAndrew Jeffery             inst.minCollected = readValue;
208e73bd0a1SAndrew Jeffery             inst.maxCollected = readValue;
209e73bd0a1SAndrew Jeffery         }
210e73bd0a1SAndrew Jeffery 
211e73bd0a1SAndrew Jeffery         inst.numStreakMisses = 0;
212e73bd0a1SAndrew Jeffery         ++(inst.numCollectsGood);
213e73bd0a1SAndrew Jeffery         ++(inst.numStreakGreats);
214e73bd0a1SAndrew Jeffery 
215e73bd0a1SAndrew Jeffery         // Only provide subsequent output if new min/max established
216e73bd0a1SAndrew Jeffery         if (readValue < inst.minCollected)
217e73bd0a1SAndrew Jeffery         {
218e73bd0a1SAndrew Jeffery             std::cerr << "Sensor " << name << ": Lowest reading=" << readValue
219e73bd0a1SAndrew Jeffery                       << "\n";
220e73bd0a1SAndrew Jeffery 
221e73bd0a1SAndrew Jeffery             inst.minCollected = readValue;
222e73bd0a1SAndrew Jeffery         }
223e73bd0a1SAndrew Jeffery 
224e73bd0a1SAndrew Jeffery         if (readValue > inst.maxCollected)
225e73bd0a1SAndrew Jeffery         {
226e73bd0a1SAndrew Jeffery             std::cerr << "Sensor " << name << ": Highest reading=" << readValue
227e73bd0a1SAndrew Jeffery                       << "\n";
228e73bd0a1SAndrew Jeffery 
229e73bd0a1SAndrew Jeffery             inst.maxCollected = readValue;
230e73bd0a1SAndrew Jeffery         }
231e73bd0a1SAndrew Jeffery     }
232e73bd0a1SAndrew Jeffery 
setSensorValueSensor233e73bd0a1SAndrew Jeffery     int setSensorValue(const double& newValue, double& oldValue)
234e73bd0a1SAndrew Jeffery     {
235e73bd0a1SAndrew Jeffery         if (!internalSet)
236e73bd0a1SAndrew Jeffery         {
237e73bd0a1SAndrew Jeffery             if (insecureSensorOverride == 0 && !isSensorSettable &&
238e73bd0a1SAndrew Jeffery                 !getManufacturingMode())
239e73bd0a1SAndrew Jeffery             {
240e73bd0a1SAndrew Jeffery                 throw SetSensorError();
241e73bd0a1SAndrew Jeffery             }
242e73bd0a1SAndrew Jeffery 
243e73bd0a1SAndrew Jeffery             oldValue = newValue;
244e73bd0a1SAndrew Jeffery             overriddenState = true;
245e73bd0a1SAndrew Jeffery             // check thresholds for external set
246e73bd0a1SAndrew Jeffery             value = newValue;
247e73bd0a1SAndrew Jeffery             checkThresholds();
248e73bd0a1SAndrew Jeffery 
249e73bd0a1SAndrew Jeffery             // Trigger the hook, as an external set has just happened
250e73bd0a1SAndrew Jeffery             if (externalSetHook)
251e73bd0a1SAndrew Jeffery             {
252e73bd0a1SAndrew Jeffery                 externalSetHook();
253e73bd0a1SAndrew Jeffery             }
254e73bd0a1SAndrew Jeffery         }
255e73bd0a1SAndrew Jeffery         else if (!overriddenState)
256e73bd0a1SAndrew Jeffery         {
257e73bd0a1SAndrew Jeffery             oldValue = newValue;
258e73bd0a1SAndrew Jeffery         }
259e73bd0a1SAndrew Jeffery         return 1;
260e73bd0a1SAndrew Jeffery     }
261e73bd0a1SAndrew Jeffery 
setInitialPropertiesSensor262e73bd0a1SAndrew Jeffery     void setInitialProperties(const std::string& unit,
263e73bd0a1SAndrew Jeffery                               const std::string& label = std::string(),
264e73bd0a1SAndrew Jeffery                               size_t thresholdSize = 0)
265e73bd0a1SAndrew Jeffery     {
266e73bd0a1SAndrew Jeffery         if (readState == PowerState::on || readState == PowerState::biosPost ||
267e73bd0a1SAndrew Jeffery             readState == PowerState::chassisOn)
268e73bd0a1SAndrew Jeffery         {
269e73bd0a1SAndrew Jeffery             setupPowerMatch(dbusConnection);
270e73bd0a1SAndrew Jeffery         }
271e73bd0a1SAndrew Jeffery 
272e73bd0a1SAndrew Jeffery         createAssociation(association, configurationPath);
273e73bd0a1SAndrew Jeffery 
274e73bd0a1SAndrew Jeffery         sensorInterface->register_property("Unit", unit);
275e73bd0a1SAndrew Jeffery         sensorInterface->register_property("MaxValue", maxValue);
276e73bd0a1SAndrew Jeffery         sensorInterface->register_property("MinValue", minValue);
277e73bd0a1SAndrew Jeffery         sensorInterface->register_property(
278e73bd0a1SAndrew Jeffery             "Value", value, [this](const double& newValue, double& oldValue) {
279e73bd0a1SAndrew Jeffery                 return setSensorValue(newValue, oldValue);
280e73bd0a1SAndrew Jeffery             });
281e73bd0a1SAndrew Jeffery 
282e73bd0a1SAndrew Jeffery         fillMissingThresholds();
283e73bd0a1SAndrew Jeffery 
284e73bd0a1SAndrew Jeffery         for (auto& threshold : thresholds)
285e73bd0a1SAndrew Jeffery         {
286e73bd0a1SAndrew Jeffery             if (std::isnan(threshold.hysteresis))
287e73bd0a1SAndrew Jeffery             {
288e73bd0a1SAndrew Jeffery                 threshold.hysteresis = hysteresisTrigger;
289e73bd0a1SAndrew Jeffery             }
290e73bd0a1SAndrew Jeffery 
291e73bd0a1SAndrew Jeffery             std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
292e73bd0a1SAndrew Jeffery                 getThresholdInterface(threshold.level);
293e73bd0a1SAndrew Jeffery 
294e73bd0a1SAndrew Jeffery             if (!iface)
295e73bd0a1SAndrew Jeffery             {
296e73bd0a1SAndrew Jeffery                 std::cout << "trying to set uninitialized interface\n";
297e73bd0a1SAndrew Jeffery                 continue;
298e73bd0a1SAndrew Jeffery             }
299e73bd0a1SAndrew Jeffery 
3002aaf7175SPatrick Williams             std::string level =
3012aaf7175SPatrick Williams                 propertyLevel(threshold.level, threshold.direction);
3022aaf7175SPatrick Williams             std::string alarm =
3032aaf7175SPatrick Williams                 propertyAlarm(threshold.level, threshold.direction);
304e73bd0a1SAndrew Jeffery 
305e73bd0a1SAndrew Jeffery             if ((level.empty()) || (alarm.empty()))
306e73bd0a1SAndrew Jeffery             {
307e73bd0a1SAndrew Jeffery                 continue;
308e73bd0a1SAndrew Jeffery             }
3092aaf7175SPatrick Williams             size_t thresSize =
3102aaf7175SPatrick Williams                 label.empty() ? thresholds.size() : thresholdSize;
311e73bd0a1SAndrew Jeffery             iface->register_property(
312e73bd0a1SAndrew Jeffery                 level, threshold.value,
313e73bd0a1SAndrew Jeffery                 [&, label, thresSize](const double& request, double& oldValue) {
314e73bd0a1SAndrew Jeffery                     oldValue = request; // todo, just let the config do this?
315e73bd0a1SAndrew Jeffery                     threshold.value = request;
3162aaf7175SPatrick Williams                     thresholds::persistThreshold(
3172aaf7175SPatrick Williams                         configurationPath, configInterface, threshold,
3182aaf7175SPatrick Williams                         dbusConnection, thresSize, label);
319e73bd0a1SAndrew Jeffery                     // Invalidate previously remembered value,
320e73bd0a1SAndrew Jeffery                     // so new thresholds will be checked during next update,
321e73bd0a1SAndrew Jeffery                     // even if sensor reading remains unchanged.
322e73bd0a1SAndrew Jeffery                     value = std::numeric_limits<double>::quiet_NaN();
323e73bd0a1SAndrew Jeffery 
324e73bd0a1SAndrew Jeffery                     // Although tempting, don't call checkThresholds() from here
325e73bd0a1SAndrew Jeffery                     // directly. Let the regular sensor monitor call the same
326e73bd0a1SAndrew Jeffery                     // using updateValue(), which can check conditions like
327e73bd0a1SAndrew Jeffery                     // poweron, etc., before raising any event.
328e73bd0a1SAndrew Jeffery                     return 1;
329e73bd0a1SAndrew Jeffery                 });
330e73bd0a1SAndrew Jeffery             iface->register_property(alarm, false);
331e73bd0a1SAndrew Jeffery         }
332e73bd0a1SAndrew Jeffery         if (!sensorInterface->initialize())
333e73bd0a1SAndrew Jeffery         {
334e73bd0a1SAndrew Jeffery             std::cerr << "error initializing value interface\n";
335e73bd0a1SAndrew Jeffery         }
336e73bd0a1SAndrew Jeffery 
337e73bd0a1SAndrew Jeffery         for (auto& thresIface : thresholdInterfaces)
338e73bd0a1SAndrew Jeffery         {
339e73bd0a1SAndrew Jeffery             if (thresIface)
340e73bd0a1SAndrew Jeffery             {
341e73bd0a1SAndrew Jeffery                 if (!thresIface->initialize(true))
342e73bd0a1SAndrew Jeffery                 {
343e73bd0a1SAndrew Jeffery                     std::cerr << "Error initializing threshold interface \n";
344e73bd0a1SAndrew Jeffery                 }
345e73bd0a1SAndrew Jeffery             }
346e73bd0a1SAndrew Jeffery         }
347e73bd0a1SAndrew Jeffery 
348e73bd0a1SAndrew Jeffery         if (isValueMutable)
349e73bd0a1SAndrew Jeffery         {
350e73bd0a1SAndrew Jeffery             valueMutabilityInterface =
351e73bd0a1SAndrew Jeffery                 std::make_shared<sdbusplus::asio::dbus_interface>(
352e73bd0a1SAndrew Jeffery                     dbusConnection, sensorInterface->get_object_path(),
353e73bd0a1SAndrew Jeffery                     valueMutabilityInterfaceName);
354e73bd0a1SAndrew Jeffery             valueMutabilityInterface->register_property("Mutable", true);
355e73bd0a1SAndrew Jeffery             if (!valueMutabilityInterface->initialize())
356e73bd0a1SAndrew Jeffery             {
357e73bd0a1SAndrew Jeffery                 std::cerr
358e73bd0a1SAndrew Jeffery                     << "error initializing sensor value mutability interface\n";
359e73bd0a1SAndrew Jeffery                 valueMutabilityInterface = nullptr;
360e73bd0a1SAndrew Jeffery             }
361e73bd0a1SAndrew Jeffery         }
362e73bd0a1SAndrew Jeffery 
363e73bd0a1SAndrew Jeffery         if (!availableInterface)
364e73bd0a1SAndrew Jeffery         {
365e73bd0a1SAndrew Jeffery             availableInterface =
366e73bd0a1SAndrew Jeffery                 std::make_shared<sdbusplus::asio::dbus_interface>(
367e73bd0a1SAndrew Jeffery                     dbusConnection, sensorInterface->get_object_path(),
368e73bd0a1SAndrew Jeffery                     availableInterfaceName);
369e73bd0a1SAndrew Jeffery             availableInterface->register_property(
370e73bd0a1SAndrew Jeffery                 "Available", true, [this](const bool propIn, bool& old) {
371e73bd0a1SAndrew Jeffery                     if (propIn == old)
372e73bd0a1SAndrew Jeffery                     {
373e73bd0a1SAndrew Jeffery                         return 1;
374e73bd0a1SAndrew Jeffery                     }
375e73bd0a1SAndrew Jeffery                     old = propIn;
376e73bd0a1SAndrew Jeffery                     if (!propIn)
377e73bd0a1SAndrew Jeffery                     {
378e73bd0a1SAndrew Jeffery                         updateValue(std::numeric_limits<double>::quiet_NaN());
379e73bd0a1SAndrew Jeffery                     }
380e73bd0a1SAndrew Jeffery                     return 1;
381e73bd0a1SAndrew Jeffery                 });
382e73bd0a1SAndrew Jeffery             availableInterface->initialize();
383e73bd0a1SAndrew Jeffery         }
384e73bd0a1SAndrew Jeffery         if (!operationalInterface)
385e73bd0a1SAndrew Jeffery         {
386e73bd0a1SAndrew Jeffery             operationalInterface =
387e73bd0a1SAndrew Jeffery                 std::make_shared<sdbusplus::asio::dbus_interface>(
388e73bd0a1SAndrew Jeffery                     dbusConnection, sensorInterface->get_object_path(),
389e73bd0a1SAndrew Jeffery                     operationalInterfaceName);
390e73bd0a1SAndrew Jeffery             operationalInterface->register_property("Functional", true);
391e73bd0a1SAndrew Jeffery             operationalInterface->initialize();
392e73bd0a1SAndrew Jeffery         }
393e73bd0a1SAndrew Jeffery     }
394e73bd0a1SAndrew Jeffery 
propertyLevelSensor395e73bd0a1SAndrew Jeffery     static std::string propertyLevel(const Level lev, const Direction dir)
396e73bd0a1SAndrew Jeffery     {
397e73bd0a1SAndrew Jeffery         for (const thresholds::ThresholdDefinition& prop :
398e73bd0a1SAndrew Jeffery              thresholds::thresProp)
399e73bd0a1SAndrew Jeffery         {
400e73bd0a1SAndrew Jeffery             if (prop.level == lev)
401e73bd0a1SAndrew Jeffery             {
402e73bd0a1SAndrew Jeffery                 if (dir == Direction::HIGH)
403e73bd0a1SAndrew Jeffery                 {
404e73bd0a1SAndrew Jeffery                     return std::string(prop.levelName) + "High";
405e73bd0a1SAndrew Jeffery                 }
406e73bd0a1SAndrew Jeffery                 if (dir == Direction::LOW)
407e73bd0a1SAndrew Jeffery                 {
408e73bd0a1SAndrew Jeffery                     return std::string(prop.levelName) + "Low";
409e73bd0a1SAndrew Jeffery                 }
410e73bd0a1SAndrew Jeffery             }
411e73bd0a1SAndrew Jeffery         }
412e73bd0a1SAndrew Jeffery         return "";
413e73bd0a1SAndrew Jeffery     }
414e73bd0a1SAndrew Jeffery 
propertyAlarmSensor415e73bd0a1SAndrew Jeffery     static std::string propertyAlarm(const Level lev, const Direction dir)
416e73bd0a1SAndrew Jeffery     {
417e73bd0a1SAndrew Jeffery         for (const thresholds::ThresholdDefinition& prop :
418e73bd0a1SAndrew Jeffery              thresholds::thresProp)
419e73bd0a1SAndrew Jeffery         {
420e73bd0a1SAndrew Jeffery             if (prop.level == lev)
421e73bd0a1SAndrew Jeffery             {
422e73bd0a1SAndrew Jeffery                 if (dir == Direction::HIGH)
423e73bd0a1SAndrew Jeffery                 {
424e73bd0a1SAndrew Jeffery                     return std::string(prop.levelName) + "AlarmHigh";
425e73bd0a1SAndrew Jeffery                 }
426e73bd0a1SAndrew Jeffery                 if (dir == Direction::LOW)
427e73bd0a1SAndrew Jeffery                 {
428e73bd0a1SAndrew Jeffery                     return std::string(prop.levelName) + "AlarmLow";
429e73bd0a1SAndrew Jeffery                 }
430e73bd0a1SAndrew Jeffery             }
431e73bd0a1SAndrew Jeffery         }
432e73bd0a1SAndrew Jeffery         return "";
433e73bd0a1SAndrew Jeffery     }
434e73bd0a1SAndrew Jeffery 
readingStateGoodSensor435e73bd0a1SAndrew Jeffery     bool readingStateGood() const
436e73bd0a1SAndrew Jeffery     {
437e73bd0a1SAndrew Jeffery         return ::readingStateGood(readState);
438e73bd0a1SAndrew Jeffery     }
439e73bd0a1SAndrew Jeffery 
markFunctionalSensor440e73bd0a1SAndrew Jeffery     void markFunctional(bool isFunctional)
441e73bd0a1SAndrew Jeffery     {
442e73bd0a1SAndrew Jeffery         if (operationalInterface)
443e73bd0a1SAndrew Jeffery         {
444e73bd0a1SAndrew Jeffery             operationalInterface->set_property("Functional", isFunctional);
445e73bd0a1SAndrew Jeffery         }
446e73bd0a1SAndrew Jeffery         if (isFunctional)
447e73bd0a1SAndrew Jeffery         {
448e73bd0a1SAndrew Jeffery             errCount = 0;
449e73bd0a1SAndrew Jeffery         }
450e73bd0a1SAndrew Jeffery         else
451e73bd0a1SAndrew Jeffery         {
452e73bd0a1SAndrew Jeffery             updateValue(std::numeric_limits<double>::quiet_NaN());
453e73bd0a1SAndrew Jeffery         }
454e73bd0a1SAndrew Jeffery     }
455e73bd0a1SAndrew Jeffery 
markAvailableSensor456e73bd0a1SAndrew Jeffery     void markAvailable(bool isAvailable)
457e73bd0a1SAndrew Jeffery     {
458e73bd0a1SAndrew Jeffery         if (availableInterface)
459e73bd0a1SAndrew Jeffery         {
460e73bd0a1SAndrew Jeffery             availableInterface->set_property("Available", isAvailable);
461e73bd0a1SAndrew Jeffery             errCount = 0;
462e73bd0a1SAndrew Jeffery         }
463e73bd0a1SAndrew Jeffery     }
464e73bd0a1SAndrew Jeffery 
incrementErrorSensor465e73bd0a1SAndrew Jeffery     void incrementError()
466e73bd0a1SAndrew Jeffery     {
467e73bd0a1SAndrew Jeffery         if (!readingStateGood())
468e73bd0a1SAndrew Jeffery         {
469e73bd0a1SAndrew Jeffery             markAvailable(false);
470e73bd0a1SAndrew Jeffery             return;
471e73bd0a1SAndrew Jeffery         }
472e73bd0a1SAndrew Jeffery 
473e73bd0a1SAndrew Jeffery         if (errCount >= errorThreshold)
474e73bd0a1SAndrew Jeffery         {
475e73bd0a1SAndrew Jeffery             return;
476e73bd0a1SAndrew Jeffery         }
477e73bd0a1SAndrew Jeffery 
478e73bd0a1SAndrew Jeffery         errCount++;
479e73bd0a1SAndrew Jeffery         if (errCount == errorThreshold)
480e73bd0a1SAndrew Jeffery         {
481e73bd0a1SAndrew Jeffery             std::cerr << "Sensor " << name << " reading error!\n";
482e73bd0a1SAndrew Jeffery             markFunctional(false);
483e73bd0a1SAndrew Jeffery         }
484e73bd0a1SAndrew Jeffery     }
485e73bd0a1SAndrew Jeffery 
inErrorSensor486e73bd0a1SAndrew Jeffery     bool inError() const
487e73bd0a1SAndrew Jeffery     {
488e73bd0a1SAndrew Jeffery         return errCount >= errorThreshold;
489e73bd0a1SAndrew Jeffery     }
490e73bd0a1SAndrew Jeffery 
updateValueSensor491e73bd0a1SAndrew Jeffery     void updateValue(const double& newValue)
492e73bd0a1SAndrew Jeffery     {
493e73bd0a1SAndrew Jeffery         // Ignore if overriding is enabled
494e73bd0a1SAndrew Jeffery         if (overriddenState)
495e73bd0a1SAndrew Jeffery         {
496e73bd0a1SAndrew Jeffery             return;
497e73bd0a1SAndrew Jeffery         }
498e73bd0a1SAndrew Jeffery 
499e73bd0a1SAndrew Jeffery         if (!readingStateGood())
500e73bd0a1SAndrew Jeffery         {
501e73bd0a1SAndrew Jeffery             markAvailable(false);
502e456925bSKonstantin Aladyshev             for (auto& threshold : thresholds)
503e456925bSKonstantin Aladyshev             {
504e456925bSKonstantin Aladyshev                 assertThresholds(this, value, threshold.level,
505e456925bSKonstantin Aladyshev                                  threshold.direction, false);
506e456925bSKonstantin Aladyshev             }
507e73bd0a1SAndrew Jeffery             updateValueProperty(std::numeric_limits<double>::quiet_NaN());
508e73bd0a1SAndrew Jeffery             return;
509e73bd0a1SAndrew Jeffery         }
510e73bd0a1SAndrew Jeffery 
511e73bd0a1SAndrew Jeffery         updateValueProperty(newValue);
512e73bd0a1SAndrew Jeffery         updateInstrumentation(newValue);
513e73bd0a1SAndrew Jeffery 
514e73bd0a1SAndrew Jeffery         // Always check thresholds after changing the value,
515e73bd0a1SAndrew Jeffery         // as the test against hysteresisTrigger now takes place in
516e73bd0a1SAndrew Jeffery         // the thresholds::checkThresholds() method,
517e73bd0a1SAndrew Jeffery         // which is called by checkThresholds() below,
518e73bd0a1SAndrew Jeffery         // in all current implementations of sensors that have thresholds.
519e73bd0a1SAndrew Jeffery         checkThresholds();
520e73bd0a1SAndrew Jeffery         if (!std::isnan(newValue))
521e73bd0a1SAndrew Jeffery         {
522e73bd0a1SAndrew Jeffery             markFunctional(true);
523e73bd0a1SAndrew Jeffery             markAvailable(true);
524e73bd0a1SAndrew Jeffery         }
525e73bd0a1SAndrew Jeffery     }
526e73bd0a1SAndrew Jeffery 
updatePropertySensor527e73bd0a1SAndrew Jeffery     void updateProperty(
528e73bd0a1SAndrew Jeffery         std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
529e73bd0a1SAndrew Jeffery         double& oldValue, const double& newValue,
530e73bd0a1SAndrew Jeffery         const char* dbusPropertyName) const
531e73bd0a1SAndrew Jeffery     {
532e73bd0a1SAndrew Jeffery         if (requiresUpdate(oldValue, newValue))
533e73bd0a1SAndrew Jeffery         {
534e73bd0a1SAndrew Jeffery             oldValue = newValue;
535e73bd0a1SAndrew Jeffery             if (interface &&
536e73bd0a1SAndrew Jeffery                 !(interface->set_property(dbusPropertyName, newValue)))
537e73bd0a1SAndrew Jeffery             {
538e73bd0a1SAndrew Jeffery                 std::cerr << "error setting property " << dbusPropertyName
539e73bd0a1SAndrew Jeffery                           << " to " << newValue << "\n";
540e73bd0a1SAndrew Jeffery             }
541e73bd0a1SAndrew Jeffery         }
542e73bd0a1SAndrew Jeffery     }
543e73bd0a1SAndrew Jeffery 
requiresUpdateSensor544e73bd0a1SAndrew Jeffery     bool requiresUpdate(const double& lVal, const double& rVal) const
545e73bd0a1SAndrew Jeffery     {
546e73bd0a1SAndrew Jeffery         const auto lNan = std::isnan(lVal);
547e73bd0a1SAndrew Jeffery         const auto rNan = std::isnan(rVal);
548e73bd0a1SAndrew Jeffery         if (lNan || rNan)
549e73bd0a1SAndrew Jeffery         {
550e73bd0a1SAndrew Jeffery             return (lNan != rNan);
551e73bd0a1SAndrew Jeffery         }
552e73bd0a1SAndrew Jeffery         return std::abs(lVal - rVal) > hysteresisPublish;
553e73bd0a1SAndrew Jeffery     }
554e73bd0a1SAndrew Jeffery 
555e73bd0a1SAndrew Jeffery   private:
556e73bd0a1SAndrew Jeffery     // If one of the thresholds for a dbus interface is provided
557e73bd0a1SAndrew Jeffery     // we have to set the other one as dbus properties are never
558e73bd0a1SAndrew Jeffery     // optional.
fillMissingThresholdsSensor559e73bd0a1SAndrew Jeffery     void fillMissingThresholds()
560e73bd0a1SAndrew Jeffery     {
5612d5ee50bSVikash Chandola         const std::size_t thresholdsLen = thresholds.size();
5622d5ee50bSVikash Chandola         for (std::size_t index = 0; index < thresholdsLen; ++index)
563e73bd0a1SAndrew Jeffery         {
5642d5ee50bSVikash Chandola             const thresholds::Threshold& thisThreshold = thresholds[index];
565e73bd0a1SAndrew Jeffery             bool foundOpposite = false;
566e73bd0a1SAndrew Jeffery             thresholds::Direction opposite = thresholds::Direction::HIGH;
567e73bd0a1SAndrew Jeffery             if (thisThreshold.direction == thresholds::Direction::HIGH)
568e73bd0a1SAndrew Jeffery             {
569e73bd0a1SAndrew Jeffery                 opposite = thresholds::Direction::LOW;
570e73bd0a1SAndrew Jeffery             }
571e73bd0a1SAndrew Jeffery             for (thresholds::Threshold& otherThreshold : thresholds)
572e73bd0a1SAndrew Jeffery             {
573e73bd0a1SAndrew Jeffery                 if (thisThreshold.level != otherThreshold.level)
574e73bd0a1SAndrew Jeffery                 {
575e73bd0a1SAndrew Jeffery                     continue;
576e73bd0a1SAndrew Jeffery                 }
577e73bd0a1SAndrew Jeffery                 if (otherThreshold.direction != opposite)
578e73bd0a1SAndrew Jeffery                 {
579e73bd0a1SAndrew Jeffery                     continue;
580e73bd0a1SAndrew Jeffery                 }
581e73bd0a1SAndrew Jeffery                 foundOpposite = true;
582e73bd0a1SAndrew Jeffery                 break;
583e73bd0a1SAndrew Jeffery             }
584e73bd0a1SAndrew Jeffery             if (foundOpposite)
585e73bd0a1SAndrew Jeffery             {
586e73bd0a1SAndrew Jeffery                 continue;
587e73bd0a1SAndrew Jeffery             }
588e73bd0a1SAndrew Jeffery             thresholds.emplace_back(thisThreshold.level, opposite,
589e73bd0a1SAndrew Jeffery                                     std::numeric_limits<double>::quiet_NaN());
590e73bd0a1SAndrew Jeffery         }
591e73bd0a1SAndrew Jeffery     }
592e73bd0a1SAndrew Jeffery 
updateValuePropertySensor593e73bd0a1SAndrew Jeffery     void updateValueProperty(const double& newValue)
594e73bd0a1SAndrew Jeffery     {
595e73bd0a1SAndrew Jeffery         // Indicate that it is internal set call, not an external overwrite
596e73bd0a1SAndrew Jeffery         internalSet = true;
597e73bd0a1SAndrew Jeffery         updateProperty(sensorInterface, value, newValue, "Value");
598e73bd0a1SAndrew Jeffery         internalSet = false;
599e73bd0a1SAndrew Jeffery     }
600e73bd0a1SAndrew Jeffery };
601