xref: /openbmc/dbus-sensors/src/external/ExternalSensor.cpp (revision d630b3a3904f1ad360e001937e95fb438bb8f819)
1d7be555eSGeorge Liu #include "ExternalSensor.hpp"
2d7be555eSGeorge Liu 
3d7be555eSGeorge Liu #include "SensorPaths.hpp"
4d7be555eSGeorge Liu #include "Thresholds.hpp"
5d7be555eSGeorge Liu #include "Utils.hpp"
6d7be555eSGeorge Liu #include "sensor.hpp"
7d7be555eSGeorge Liu 
8*d630b3a3SGeorge Liu #include <phosphor-logging/lg2.hpp>
9d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
10d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
11d7be555eSGeorge Liu 
12d7be555eSGeorge Liu #include <chrono>
13d7be555eSGeorge Liu #include <cstddef>
14d7be555eSGeorge Liu #include <functional>
15d7be555eSGeorge Liu #include <limits>
16d7be555eSGeorge Liu #include <memory>
17d7be555eSGeorge Liu #include <stdexcept>
18d7be555eSGeorge Liu #include <string>
19d7be555eSGeorge Liu #include <utility>
20d7be555eSGeorge Liu #include <vector>
21d7be555eSGeorge Liu 
22d7be555eSGeorge Liu static constexpr bool debug = false;
23d7be555eSGeorge Liu 
ExternalSensor(const std::string & objectType,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & conn,const std::string & sensorName,const std::string & sensorUnits,std::vector<thresholds::Threshold> && thresholdsIn,const std::string & sensorConfiguration,double maxReading,double minReading,double timeoutSecs,const PowerState & powerState)24d7be555eSGeorge Liu ExternalSensor::ExternalSensor(
25d7be555eSGeorge Liu     const std::string& objectType, sdbusplus::asio::object_server& objectServer,
26d7be555eSGeorge Liu     std::shared_ptr<sdbusplus::asio::connection>& conn,
27d7be555eSGeorge Liu     const std::string& sensorName, const std::string& sensorUnits,
28d7be555eSGeorge Liu     std::vector<thresholds::Threshold>&& thresholdsIn,
29d7be555eSGeorge Liu     const std::string& sensorConfiguration, double maxReading,
30d7be555eSGeorge Liu     double minReading, double timeoutSecs, const PowerState& powerState) :
31d7be555eSGeorge Liu     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
32d7be555eSGeorge Liu            objectType, true, true, maxReading, minReading, conn, powerState),
33d7be555eSGeorge Liu     objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
34d7be555eSGeorge Liu     writeTimeout(
35d7be555eSGeorge Liu         std::chrono::duration_cast<std::chrono::steady_clock::duration>(
36d7be555eSGeorge Liu             std::chrono::duration<double>(timeoutSecs))),
37d7be555eSGeorge Liu     writePerishable(timeoutSecs > 0.0)
38d7be555eSGeorge Liu {
39d7be555eSGeorge Liu     // The caller must specify what physical characteristic
40d7be555eSGeorge Liu     // an external sensor is expected to be measuring, such as temperature,
41d7be555eSGeorge Liu     // as, unlike others, this is not implied by device type name.
42d7be555eSGeorge Liu     std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
43d7be555eSGeorge Liu     if (dbusPath.empty())
44d7be555eSGeorge Liu     {
45d7be555eSGeorge Liu         throw std::runtime_error("Units not in allow list");
46d7be555eSGeorge Liu     }
47d7be555eSGeorge Liu     std::string objectPath = "/xyz/openbmc_project/sensors/";
48d7be555eSGeorge Liu     objectPath += dbusPath;
49d7be555eSGeorge Liu     objectPath += '/';
50d7be555eSGeorge Liu     objectPath += sensorName;
51d7be555eSGeorge Liu 
52d7be555eSGeorge Liu     sensorInterface = objectServer.add_interface(
53d7be555eSGeorge Liu         objectPath, "xyz.openbmc_project.Sensor.Value");
54d7be555eSGeorge Liu 
55d7be555eSGeorge Liu     for (const auto& threshold : thresholds)
56d7be555eSGeorge Liu     {
57d7be555eSGeorge Liu         std::string interface = thresholds::getInterface(threshold.level);
58d7be555eSGeorge Liu         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
59d7be555eSGeorge Liu             objectServer.add_interface(objectPath, interface);
60d7be555eSGeorge Liu     }
61d7be555eSGeorge Liu 
62d7be555eSGeorge Liu     association =
63d7be555eSGeorge Liu         objectServer.add_interface(objectPath, association::interface);
64d7be555eSGeorge Liu     setInitialProperties(sensorUnits);
65d7be555eSGeorge Liu 
66d7be555eSGeorge Liu     if constexpr (debug)
67d7be555eSGeorge Liu     {
68*d630b3a3SGeorge Liu         lg2::error(
69*d630b3a3SGeorge Liu             "ExternalSensor '{NAME}' constructed: path '{PATH}', type '{TYPE}', "
70*d630b3a3SGeorge Liu             "min '{MIN}', max '{MAX}', timeout '{TIMEOUT}' us",
71*d630b3a3SGeorge Liu             "NAME", name, "PATH", objectPath, "TYPE", objectType, "MIN",
72*d630b3a3SGeorge Liu             minReading, "MAX", maxReading, "TIMEOUT",
73*d630b3a3SGeorge Liu             std::chrono::duration_cast<std::chrono::microseconds>(writeTimeout)
74*d630b3a3SGeorge Liu                 .count());
75d7be555eSGeorge Liu     }
76d7be555eSGeorge Liu }
77d7be555eSGeorge Liu 
78d7be555eSGeorge Liu // Separate function from constructor, because of a gotcha: can't use the
79d7be555eSGeorge Liu // enable_shared_from_this() API until after the constructor has completed.
initWriteHook(std::function<void (std::chrono::steady_clock::time_point now)> && writeHookIn)80d7be555eSGeorge Liu void ExternalSensor::initWriteHook(
81d7be555eSGeorge Liu     std::function<void(std::chrono::steady_clock::time_point now)>&&
82d7be555eSGeorge Liu         writeHookIn)
83d7be555eSGeorge Liu {
84d7be555eSGeorge Liu     // Connect ExternalSensorMain with ExternalSensor
85d7be555eSGeorge Liu     writeHook = std::move(writeHookIn);
86d7be555eSGeorge Liu 
87d7be555eSGeorge Liu     // Connect ExternalSensor with Sensor
88d7be555eSGeorge Liu     auto weakThis = weak_from_this();
89d7be555eSGeorge Liu     externalSetHook = [weakThis]() {
90d7be555eSGeorge Liu         auto lockThis = weakThis.lock();
91d7be555eSGeorge Liu         if (lockThis)
92d7be555eSGeorge Liu         {
93d7be555eSGeorge Liu             lockThis->externalSetTrigger();
94d7be555eSGeorge Liu             return;
95d7be555eSGeorge Liu         }
96d7be555eSGeorge Liu         if constexpr (debug)
97d7be555eSGeorge Liu         {
98*d630b3a3SGeorge Liu             lg2::error("ExternalSensor receive ignored, sensor gone");
99d7be555eSGeorge Liu         }
100d7be555eSGeorge Liu     };
101d7be555eSGeorge Liu }
102d7be555eSGeorge Liu 
~ExternalSensor()103d7be555eSGeorge Liu ExternalSensor::~ExternalSensor()
104d7be555eSGeorge Liu {
105d7be555eSGeorge Liu     // Make sure the write hook does not reference this object anymore
106d7be555eSGeorge Liu     externalSetHook = nullptr;
107d7be555eSGeorge Liu 
108d7be555eSGeorge Liu     objServer.remove_interface(association);
109d7be555eSGeorge Liu     for (const auto& iface : thresholdInterfaces)
110d7be555eSGeorge Liu     {
111d7be555eSGeorge Liu         objServer.remove_interface(iface);
112d7be555eSGeorge Liu     }
113d7be555eSGeorge Liu     objServer.remove_interface(sensorInterface);
114d7be555eSGeorge Liu 
115d7be555eSGeorge Liu     if constexpr (debug)
116d7be555eSGeorge Liu     {
117*d630b3a3SGeorge Liu         lg2::error("ExternalSensor '{NAME}' destructed", "NAME", name);
118d7be555eSGeorge Liu     }
119d7be555eSGeorge Liu }
120d7be555eSGeorge Liu 
checkThresholds()121d7be555eSGeorge Liu void ExternalSensor::checkThresholds()
122d7be555eSGeorge Liu {
123d7be555eSGeorge Liu     thresholds::checkThresholds(this);
124d7be555eSGeorge Liu }
125d7be555eSGeorge Liu 
isAliveAndPerishable() const126d7be555eSGeorge Liu bool ExternalSensor::isAliveAndPerishable() const
127d7be555eSGeorge Liu {
128d7be555eSGeorge Liu     return (writeAlive && writePerishable);
129d7be555eSGeorge Liu }
130d7be555eSGeorge Liu 
isAliveAndFresh(const std::chrono::steady_clock::time_point & now) const131d7be555eSGeorge Liu bool ExternalSensor::isAliveAndFresh(
132d7be555eSGeorge Liu     const std::chrono::steady_clock::time_point& now) const
133d7be555eSGeorge Liu {
134d7be555eSGeorge Liu     // Must be alive and perishable, to have possibility of being fresh
135d7be555eSGeorge Liu     if (!isAliveAndPerishable())
136d7be555eSGeorge Liu     {
137d7be555eSGeorge Liu         return false;
138d7be555eSGeorge Liu     }
139d7be555eSGeorge Liu 
140d7be555eSGeorge Liu     // If age, as of now, is less than timeout, it is deemed fresh
141d7be555eSGeorge Liu     // NOLINTNEXTLINE
142d7be555eSGeorge Liu     return (ageElapsed(now) < writeTimeout);
143d7be555eSGeorge Liu }
144d7be555eSGeorge Liu 
writeBegin(const std::chrono::steady_clock::time_point & now)145556e04b8SPatrick Williams void ExternalSensor::writeBegin(
146556e04b8SPatrick Williams     const std::chrono::steady_clock::time_point& now)
147d7be555eSGeorge Liu {
148d7be555eSGeorge Liu     if (!writeAlive)
149d7be555eSGeorge Liu     {
150*d630b3a3SGeorge Liu         lg2::error(
151*d630b3a3SGeorge Liu             "ExternalSensor '{NAME}' online, receiving first value '{VALUE}'",
152*d630b3a3SGeorge Liu             "NAME", name, "VALUE", value);
153d7be555eSGeorge Liu     }
154d7be555eSGeorge Liu 
155d7be555eSGeorge Liu     writeLast = now;
156d7be555eSGeorge Liu     writeAlive = true;
157d7be555eSGeorge Liu }
158d7be555eSGeorge Liu 
writeInvalidate()159d7be555eSGeorge Liu void ExternalSensor::writeInvalidate()
160d7be555eSGeorge Liu {
161d7be555eSGeorge Liu     writeAlive = false;
162d7be555eSGeorge Liu 
163*d630b3a3SGeorge Liu     lg2::error("ExternalSensor '{NAME}' offline, timed out", "NAME", name);
164d7be555eSGeorge Liu 
165d7be555eSGeorge Liu     // Take back control of this sensor from the external override,
166d7be555eSGeorge Liu     // as the external source has timed out.
167d7be555eSGeorge Liu     // This allows sensor::updateValue() to work normally,
168d7be555eSGeorge Liu     // as it would do for internal sensors with values from hardware.
169d7be555eSGeorge Liu     overriddenState = false;
170d7be555eSGeorge Liu 
171d7be555eSGeorge Liu     // Invalidate the existing Value, similar to what internal sensors do,
172d7be555eSGeorge Liu     // when they encounter errors trying to read from hardware.
173d7be555eSGeorge Liu     updateValue(std::numeric_limits<double>::quiet_NaN());
174d7be555eSGeorge Liu }
175d7be555eSGeorge Liu 
ageElapsed(const std::chrono::steady_clock::time_point & now) const176d7be555eSGeorge Liu std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
177d7be555eSGeorge Liu     const std::chrono::steady_clock::time_point& now) const
178d7be555eSGeorge Liu {
179d7be555eSGeorge Liu     // Comparing 2 time_point will return duration
180d7be555eSGeorge Liu     return (now - writeLast);
181d7be555eSGeorge Liu }
182d7be555eSGeorge Liu 
ageRemaining(const std::chrono::steady_clock::time_point & now) const183d7be555eSGeorge Liu std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
184d7be555eSGeorge Liu     const std::chrono::steady_clock::time_point& now) const
185d7be555eSGeorge Liu {
186d7be555eSGeorge Liu     // Comparing duration will return another duration
187d7be555eSGeorge Liu     return (writeTimeout - ageElapsed(now));
188d7be555eSGeorge Liu }
189d7be555eSGeorge Liu 
externalSetTrigger()190d7be555eSGeorge Liu void ExternalSensor::externalSetTrigger()
191d7be555eSGeorge Liu {
192d7be555eSGeorge Liu     if constexpr (debug)
193d7be555eSGeorge Liu     {
194*d630b3a3SGeorge Liu         lg2::error("ExternalSensor '{NAME}' received '{VALUE}'", "NAME", name,
195*d630b3a3SGeorge Liu                    "VALUE", value);
196d7be555eSGeorge Liu     }
197d7be555eSGeorge Liu 
198d7be555eSGeorge Liu     auto now = std::chrono::steady_clock::now();
199d7be555eSGeorge Liu 
200d7be555eSGeorge Liu     writeBegin(now);
201d7be555eSGeorge Liu 
202d7be555eSGeorge Liu     // Tell the owner to recalculate the expiration timer
203d7be555eSGeorge Liu     writeHook(now);
204d7be555eSGeorge Liu }
205