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