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