blob: 17f5300c2e589b849d1c446f62421659f99f5a07 [file] [log] [blame]
Josh Lehan2a40e932020-09-02 11:48:14 -07001#include "ExternalSensor.hpp"
2
3#include "SensorPaths.hpp"
4
5#include <unistd.h>
6
7#include <boost/algorithm/string/predicate.hpp>
8#include <boost/algorithm/string/replace.hpp>
9#include <boost/date_time/posix_time/posix_time.hpp>
10#include <sdbusplus/asio/connection.hpp>
11#include <sdbusplus/asio/object_server.hpp>
12
Josh Lehan72432172021-03-17 13:35:43 -070013#include <chrono>
Josh Lehan2a40e932020-09-02 11:48:14 -070014#include <iostream>
15#include <istream>
16#include <limits>
17#include <memory>
18#include <string>
19#include <vector>
20
Josh Lehan72432172021-03-17 13:35:43 -070021static constexpr bool debug = false;
22
Josh Lehan2a40e932020-09-02 11:48:14 -070023ExternalSensor::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,
Jeff Lin7b7a9de2021-02-22 11:16:27 +080027 std::vector<thresholds::Threshold>&& thresholdsIn,
Josh Lehan72432172021-03-17 13:35:43 -070028 const std::string& sensorConfiguration, double maxReading,
Josh Lehan03627382021-03-17 13:35:43 -070029 double minReading, double timeoutSecs, const PowerState& powerState) :
Josh Lehan2a40e932020-09-02 11:48:14 -070030 // TODO(): When the Mutable feature is integrated,
31 // make sure all ExternalSensor instances are mutable,
32 // because that is the entire point of ExternalSensor,
33 // to accept sensor values written by an external source.
Jeff Lin7b7a9de2021-02-22 11:16:27 +080034 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
Bruce Lee1263c3d2021-06-04 15:16:33 +080035 std::move(thresholdsIn), sensorConfiguration, objectType, true,
36 maxReading, minReading, conn, powerState),
Josh Lehan72432172021-03-17 13:35:43 -070037 std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer),
38 writeLast(std::chrono::steady_clock::now()),
39 writeTimeout(
40 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
41 std::chrono::duration<double>(timeoutSecs))),
Josh Lehan03627382021-03-17 13:35:43 -070042 writeAlive(false), writePerishable(timeoutSecs > 0.0)
Josh Lehan2a40e932020-09-02 11:48:14 -070043{
44 // The caller must specify what physical characteristic
45 // an external sensor is expected to be measuring, such as temperature,
46 // as, unlike others, this is not implied by device type name.
Ed Tanous6cb732a2021-02-18 15:33:51 -080047 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
Josh Lehan2a40e932020-09-02 11:48:14 -070048 if (dbusPath.empty())
49 {
50 throw std::runtime_error("Units not in allow list");
51 }
Ed Tanous6cb732a2021-02-18 15:33:51 -080052 std::string objectPath = "/xyz/openbmc_project/sensors/";
Josh Lehan2a40e932020-09-02 11:48:14 -070053 objectPath += dbusPath;
54 objectPath += '/';
55 objectPath += sensorName;
56
57 sensorInterface = objectServer.add_interface(
58 objectPath, "xyz.openbmc_project.Sensor.Value");
59
60 if (thresholds::hasWarningInterface(thresholds))
61 {
62 thresholdInterfaceWarning = objectServer.add_interface(
63 objectPath, "xyz.openbmc_project.Sensor.Threshold.Warning");
64 }
65 if (thresholds::hasCriticalInterface(thresholds))
66 {
67 thresholdInterfaceCritical = objectServer.add_interface(
68 objectPath, "xyz.openbmc_project.Sensor.Threshold.Critical");
69 }
70
71 association =
72 objectServer.add_interface(objectPath, association::interface);
Zev Weiss6b6891c2021-04-22 02:46:21 -050073 setInitialProperties(conn, sensorUnits);
Josh Lehan72432172021-03-17 13:35:43 -070074
Josh Lehan72432172021-03-17 13:35:43 -070075 if constexpr (debug)
76 {
77 std::cerr << "ExternalSensor " << name << " constructed: path "
78 << configurationPath << ", type " << objectType << ", min "
79 << minReading << ", max " << maxReading << ", timeout "
80 << std::chrono::duration_cast<std::chrono::microseconds>(
81 writeTimeout)
82 .count()
83 << " us\n";
84 }
Josh Lehan2a40e932020-09-02 11:48:14 -070085}
86
Josh Lehan03627382021-03-17 13:35:43 -070087// Separate function from constructor, because of a gotcha: can't use the
88// enable_shared_from_this() API until after the constructor has completed.
89void ExternalSensor::initWriteHook(
90 std::function<void(std::chrono::steady_clock::time_point now)>&&
91 writeHookIn)
92{
93 // Connect ExternalSensorMain with ExternalSensor
94 writeHook = std::move(writeHookIn);
95
96 // Connect ExternalSensor with Sensor
97 auto weakThis = weak_from_this();
98 externalSetHook = std::move([weakThis]() {
99 auto lockThis = weakThis.lock();
100 if (lockThis)
101 {
102 lockThis->externalSetTrigger();
103 return;
104 }
105 if constexpr (debug)
106 {
107 std::cerr << "ExternalSensor receive ignored, sensor gone\n";
108 }
109 });
110}
111
Josh Lehan2a40e932020-09-02 11:48:14 -0700112ExternalSensor::~ExternalSensor()
113{
Josh Lehan72432172021-03-17 13:35:43 -0700114 // Make sure the write hook does not reference this object anymore
115 externalSetHook = nullptr;
116
Josh Lehan2a40e932020-09-02 11:48:14 -0700117 objServer.remove_interface(association);
118 objServer.remove_interface(thresholdInterfaceCritical);
119 objServer.remove_interface(thresholdInterfaceWarning);
120 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700121
122 if constexpr (debug)
123 {
124 std::cerr << "ExternalSensor " << name << " destructed\n";
125 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700126}
127
128void ExternalSensor::checkThresholds(void)
129{
130 thresholds::checkThresholds(this);
131}
Josh Lehan72432172021-03-17 13:35:43 -0700132
133bool ExternalSensor::isAliveAndPerishable(void) const
134{
135 return (writeAlive && writePerishable);
136}
137
138bool ExternalSensor::isAliveAndFresh(
139 const std::chrono::steady_clock::time_point& now) const
140{
141 // Must be alive and perishable, to have possibility of being fresh
142 if (!isAliveAndPerishable())
143 {
144 return false;
145 }
146
147 // If age, as of now, is less than timeout, it is deemed fresh
Andrew Jeffery92b96292021-05-27 16:41:31 +0930148 // NOLINTNEXTLINE
Josh Lehan72432172021-03-17 13:35:43 -0700149 return (ageElapsed(now) < writeTimeout);
150}
151
152void ExternalSensor::writeBegin(
153 const std::chrono::steady_clock::time_point& now)
154{
155 if (!writeAlive)
156 {
157 std::cerr << "ExternalSensor " << name
158 << " online, receiving first value " << value << "\n";
159 }
160
161 writeLast = now;
162 writeAlive = true;
163}
164
165void ExternalSensor::writeInvalidate(void)
166{
167 writeAlive = false;
168
169 std::cerr << "ExternalSensor " << name << " offline, timed out\n";
170
171 // Take back control of this sensor from the external override,
172 // as the external source has timed out.
173 // This allows sensor::updateValue() to work normally,
174 // as it would do for internal sensors with values from hardware.
175 overriddenState = false;
176
177 // Invalidate the existing Value, similar to what internal sensors do,
178 // when they encounter errors trying to read from hardware.
179 updateValue(std::numeric_limits<double>::quiet_NaN());
180}
181
182std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
183 const std::chrono::steady_clock::time_point& now) const
184{
185 // Comparing 2 time_point will return duration
186 return (now - writeLast);
187}
188
189std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
190 const std::chrono::steady_clock::time_point& now) const
191{
192 // Comparing duration will return another duration
193 return (writeTimeout - ageElapsed(now));
194}
195
196void ExternalSensor::externalSetTrigger(void)
197{
198 if constexpr (debug)
199 {
200 std::cerr << "ExternalSensor " << name << " received " << value << "\n";
201 }
202
203 auto now = std::chrono::steady_clock::now();
204
205 writeBegin(now);
206
207 // Tell the owner to recalculate the expiration timer
208 writeHook(now);
209}