blob: e29867730215adf12f454dce23587f85a657bad9 [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"
4
Patrick Venturefd6ba732019-10-31 14:27:39 -07005#include <limits>
6#include <memory>
James Feist8fd8a582018-11-16 11:10:46 -08007#include <sdbusplus/asio/object_server.hpp>
Patrick Venturefd6ba732019-10-31 14:27:39 -07008#include <string>
9#include <vector>
James Feist8fd8a582018-11-16 11:10:46 -080010
James Feist1169eb42018-10-31 10:08:47 -070011constexpr size_t sensorFailedPollTimeMs = 5000;
James Feista5e58722019-04-22 14:43:11 -070012
13constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value";
James Feist8fd8a582018-11-16 11:10:46 -080014struct Sensor
15{
James Feist930fcde2019-05-28 12:58:43 -070016 Sensor(const std::string& name,
James Feistd8705872019-02-08 13:26:09 -080017 std::vector<thresholds::Threshold>&& thresholdData,
18 const std::string& configurationPath, const std::string& objectType,
James Feistce3fca42018-11-21 12:58:24 -080019 const double max, const double min) :
James Feistdc6c55f2018-10-31 12:53:20 -070020 name(name),
James Feist930fcde2019-05-28 12:58:43 -070021 configurationPath(configurationPath), objectType(objectType),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050022 maxValue(max), minValue(min), thresholds(std::move(thresholdData)),
Josh Lehan883fb3a2020-02-27 14:41:39 -080023 hysteresisTrigger((max - min) * 0.01),
24 hysteresisPublish((max - min) * 0.0001)
James Feistdc6c55f2018-10-31 12:53:20 -070025 {
26 }
James Feist8fd8a582018-11-16 11:10:46 -080027 virtual ~Sensor() = default;
James Feistce3fca42018-11-21 12:58:24 -080028 virtual void checkThresholds(void) = 0;
James Feistdc6c55f2018-10-31 12:53:20 -070029 std::string name;
James Feistce3fca42018-11-21 12:58:24 -080030 std::string configurationPath;
31 std::string objectType;
32 double maxValue;
33 double minValue;
James Feist8fd8a582018-11-16 11:10:46 -080034 std::vector<thresholds::Threshold> thresholds;
35 std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface;
36 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceWarning;
37 std::shared_ptr<sdbusplus::asio::dbus_interface> thresholdInterfaceCritical;
James Feist078f2322019-03-08 11:09:05 -080038 std::shared_ptr<sdbusplus::asio::dbus_interface> association;
James Feist8fd8a582018-11-16 11:10:46 -080039 double value = std::numeric_limits<double>::quiet_NaN();
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053040 bool overriddenState = false;
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053041 bool internalSet = false;
Josh Lehan883fb3a2020-02-27 14:41:39 -080042 double hysteresisTrigger;
43 double hysteresisPublish;
James Feistce3fca42018-11-21 12:58:24 -080044
James Feistd8705872019-02-08 13:26:09 -080045 int setSensorValue(const double& newValue, double& oldValue)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053046 {
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053047 if (!internalSet)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053048 {
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053049 oldValue = newValue;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053050 overriddenState = true;
51 // check thresholds for external set
52 value = newValue;
53 checkThresholds();
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053054 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +053055 else if (!overriddenState)
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053056 {
57 oldValue = newValue;
58 }
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +053059 return 1;
60 }
James Feistce3fca42018-11-21 12:58:24 -080061
62 void
James Feistd8705872019-02-08 13:26:09 -080063 setInitialProperties(std::shared_ptr<sdbusplus::asio::connection>& conn)
James Feistce3fca42018-11-21 12:58:24 -080064 {
James Feist82bac4c2019-03-11 11:16:53 -070065 createAssociation(association, configurationPath);
AppaRao Pulic82213c2020-02-27 01:24:58 +053066
James Feistce3fca42018-11-21 12:58:24 -080067 sensorInterface->register_property("MaxValue", maxValue);
68 sensorInterface->register_property("MinValue", minValue);
69 sensorInterface->register_property(
James Feistd8705872019-02-08 13:26:09 -080070 "Value", value, [&](const double& newValue, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -080071 return setSensorValue(newValue, oldValue);
72 });
James Feistd8705872019-02-08 13:26:09 -080073 for (auto& threshold : thresholds)
James Feistce3fca42018-11-21 12:58:24 -080074 {
75 std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
76 std::string level;
77 std::string alarm;
78 if (threshold.level == thresholds::Level::CRITICAL)
79 {
80 iface = thresholdInterfaceCritical;
81 if (threshold.direction == thresholds::Direction::HIGH)
82 {
83 level = "CriticalHigh";
84 alarm = "CriticalAlarmHigh";
85 }
86 else
87 {
88 level = "CriticalLow";
89 alarm = "CriticalAlarmLow";
90 }
91 }
92 else if (threshold.level == thresholds::Level::WARNING)
93 {
94 iface = thresholdInterfaceWarning;
95 if (threshold.direction == thresholds::Direction::HIGH)
96 {
97 level = "WarningHigh";
98 alarm = "WarningAlarmHigh";
99 }
100 else
101 {
102 level = "WarningLow";
103 alarm = "WarningAlarmLow";
104 }
105 }
106 else
107 {
108 std::cerr << "Unknown threshold level" << threshold.level
109 << "\n";
110 continue;
111 }
112 if (!iface)
113 {
114 std::cout << "trying to set uninitialized interface\n";
115 continue;
116 }
117 iface->register_property(
118 level, threshold.value,
James Feistd8705872019-02-08 13:26:09 -0800119 [&](const double& request, double& oldValue) {
James Feistce3fca42018-11-21 12:58:24 -0800120 oldValue = request; // todo, just let the config do this?
121 threshold.value = request;
122 thresholds::persistThreshold(configurationPath, objectType,
James Feista222ba72019-03-01 15:57:51 -0800123 threshold, conn,
124 thresholds.size());
Josh Lehan883fb3a2020-02-27 14:41:39 -0800125 // Invalidate previously remembered value,
126 // so new thresholds will be checked during next update,
127 // even if sensor reading remains unchanged.
128 value = std::numeric_limits<double>::quiet_NaN();
129
130 // Although tempting, don't call checkThresholds() from here
131 // directly. Let the regular sensor monitor call the same
132 // using updateValue(), which can check conditions like
133 // poweron, etc., before raising any event.
James Feistce3fca42018-11-21 12:58:24 -0800134 return 1;
135 });
136 iface->register_property(alarm, false);
137 }
138 if (!sensorInterface->initialize())
139 {
140 std::cerr << "error initializing value interface\n";
141 }
142 if (thresholdInterfaceWarning &&
143 !thresholdInterfaceWarning->initialize())
144 {
145 std::cerr << "error initializing warning threshold interface\n";
146 }
147
148 if (thresholdInterfaceCritical &&
149 !thresholdInterfaceCritical->initialize())
150 {
151 std::cerr << "error initializing critical threshold interface\n";
152 }
153 }
154
James Feistd8705872019-02-08 13:26:09 -0800155 void updateValue(const double& newValue)
James Feistce3fca42018-11-21 12:58:24 -0800156 {
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530157 // Ignore if overriding is enabled
Josh Lehan883fb3a2020-02-27 14:41:39 -0800158 if (overriddenState)
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530159 {
Josh Lehan883fb3a2020-02-27 14:41:39 -0800160 return;
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530161 }
Josh Lehan883fb3a2020-02-27 14:41:39 -0800162
163 bool isChanged = false;
164
165 // Avoid floating-point equality comparison,
166 // by instead comparing against a very small hysteresis range.
167 if (std::isnan(value) || std::isnan(newValue))
168 {
169 // If one or the other is NAN,
170 // either we are intentionally invalidating a sensor reading,
171 // or initializing for the very first time,
172 // either way we should always publish this.
173 isChanged = true;
174 }
175 else
176 {
177 // This essentially does "if (value != newValue)",
178 // but safely against floating-point background noise.
179 double diff = std::abs(value - newValue);
180 if (diff > hysteresisPublish)
181 {
182 isChanged = true;
183 }
184 }
185
186 // Ignore if the change is so small as to be deemed unchanged
187 if (!isChanged)
188 {
189 return;
190 }
191
192 // The value will be changed, keep track of it for next time
193 value = newValue;
194
195 // Indicate that it is internal set call
196 internalSet = true;
197 if (!(sensorInterface->set_property("Value", newValue)))
198 {
199 std::cerr << "error setting property to " << newValue << "\n";
200 }
201 internalSet = false;
202
203 // Always check thresholds after changing the value,
204 // as the test against hysteresisTrigger now takes place in
205 // the thresholds::checkThresholds() method,
206 // which is called by checkThresholds() below,
207 // in all current implementations of sensors that have thresholds.
208 checkThresholds();
James Feistce3fca42018-11-21 12:58:24 -0800209 }
Richard Marian Thomaiyarc0ca7ee2019-04-24 21:22:52 +0530210};