blob: 6b42a15728064c09b9c895e6720747ad79df708a [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) :
Jeff Lin7b7a9de2021-02-22 11:16:27 +080030 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
Jie Yang3291b9c2021-07-29 14:46:51 -070031 std::move(thresholdsIn), sensorConfiguration, objectType, true, true,
Bruce Lee1263c3d2021-06-04 15:16:33 +080032 maxReading, minReading, conn, powerState),
Josh Lehan72432172021-03-17 13:35:43 -070033 std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer),
34 writeLast(std::chrono::steady_clock::now()),
35 writeTimeout(
36 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
37 std::chrono::duration<double>(timeoutSecs))),
Josh Lehan03627382021-03-17 13:35:43 -070038 writeAlive(false), writePerishable(timeoutSecs > 0.0)
Josh Lehan2a40e932020-09-02 11:48:14 -070039{
40 // The caller must specify what physical characteristic
41 // an external sensor is expected to be measuring, such as temperature,
42 // as, unlike others, this is not implied by device type name.
Ed Tanous6cb732a2021-02-18 15:33:51 -080043 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
Josh Lehan2a40e932020-09-02 11:48:14 -070044 if (dbusPath.empty())
45 {
46 throw std::runtime_error("Units not in allow list");
47 }
Ed Tanous6cb732a2021-02-18 15:33:51 -080048 std::string objectPath = "/xyz/openbmc_project/sensors/";
Josh Lehan2a40e932020-09-02 11:48:14 -070049 objectPath += dbusPath;
50 objectPath += '/';
51 objectPath += sensorName;
52
53 sensorInterface = objectServer.add_interface(
54 objectPath, "xyz.openbmc_project.Sensor.Value");
55
56 if (thresholds::hasWarningInterface(thresholds))
57 {
58 thresholdInterfaceWarning = objectServer.add_interface(
59 objectPath, "xyz.openbmc_project.Sensor.Threshold.Warning");
60 }
61 if (thresholds::hasCriticalInterface(thresholds))
62 {
63 thresholdInterfaceCritical = objectServer.add_interface(
64 objectPath, "xyz.openbmc_project.Sensor.Threshold.Critical");
65 }
66
67 association =
68 objectServer.add_interface(objectPath, association::interface);
Zev Weiss6b6891c2021-04-22 02:46:21 -050069 setInitialProperties(conn, sensorUnits);
Josh Lehan72432172021-03-17 13:35:43 -070070
Josh Lehan72432172021-03-17 13:35:43 -070071 if constexpr (debug)
72 {
73 std::cerr << "ExternalSensor " << name << " constructed: path "
74 << configurationPath << ", type " << objectType << ", min "
75 << minReading << ", max " << maxReading << ", timeout "
76 << std::chrono::duration_cast<std::chrono::microseconds>(
77 writeTimeout)
78 .count()
79 << " us\n";
80 }
Josh Lehan2a40e932020-09-02 11:48:14 -070081}
82
Josh Lehan03627382021-03-17 13:35:43 -070083// Separate function from constructor, because of a gotcha: can't use the
84// enable_shared_from_this() API until after the constructor has completed.
85void ExternalSensor::initWriteHook(
86 std::function<void(std::chrono::steady_clock::time_point now)>&&
87 writeHookIn)
88{
89 // Connect ExternalSensorMain with ExternalSensor
90 writeHook = std::move(writeHookIn);
91
92 // Connect ExternalSensor with Sensor
93 auto weakThis = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -070094 externalSetHook = [weakThis]() {
Josh Lehan03627382021-03-17 13:35:43 -070095 auto lockThis = weakThis.lock();
96 if (lockThis)
97 {
98 lockThis->externalSetTrigger();
99 return;
100 }
101 if constexpr (debug)
102 {
103 std::cerr << "ExternalSensor receive ignored, sensor gone\n";
104 }
Ed Tanous8a17c302021-09-02 15:07:11 -0700105 };
Josh Lehan03627382021-03-17 13:35:43 -0700106}
107
Josh Lehan2a40e932020-09-02 11:48:14 -0700108ExternalSensor::~ExternalSensor()
109{
Josh Lehan72432172021-03-17 13:35:43 -0700110 // Make sure the write hook does not reference this object anymore
111 externalSetHook = nullptr;
112
Josh Lehan2a40e932020-09-02 11:48:14 -0700113 objServer.remove_interface(association);
114 objServer.remove_interface(thresholdInterfaceCritical);
115 objServer.remove_interface(thresholdInterfaceWarning);
116 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700117
118 if constexpr (debug)
119 {
120 std::cerr << "ExternalSensor " << name << " destructed\n";
121 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700122}
123
124void ExternalSensor::checkThresholds(void)
125{
126 thresholds::checkThresholds(this);
127}
Josh Lehan72432172021-03-17 13:35:43 -0700128
129bool ExternalSensor::isAliveAndPerishable(void) const
130{
131 return (writeAlive && writePerishable);
132}
133
134bool ExternalSensor::isAliveAndFresh(
135 const std::chrono::steady_clock::time_point& now) const
136{
137 // Must be alive and perishable, to have possibility of being fresh
138 if (!isAliveAndPerishable())
139 {
140 return false;
141 }
142
143 // If age, as of now, is less than timeout, it is deemed fresh
Andrew Jeffery92b96292021-05-27 16:41:31 +0930144 // NOLINTNEXTLINE
Josh Lehan72432172021-03-17 13:35:43 -0700145 return (ageElapsed(now) < writeTimeout);
146}
147
148void ExternalSensor::writeBegin(
149 const std::chrono::steady_clock::time_point& now)
150{
151 if (!writeAlive)
152 {
153 std::cerr << "ExternalSensor " << name
154 << " online, receiving first value " << value << "\n";
155 }
156
157 writeLast = now;
158 writeAlive = true;
159}
160
161void ExternalSensor::writeInvalidate(void)
162{
163 writeAlive = false;
164
165 std::cerr << "ExternalSensor " << name << " offline, timed out\n";
166
167 // Take back control of this sensor from the external override,
168 // as the external source has timed out.
169 // This allows sensor::updateValue() to work normally,
170 // as it would do for internal sensors with values from hardware.
171 overriddenState = false;
172
173 // Invalidate the existing Value, similar to what internal sensors do,
174 // when they encounter errors trying to read from hardware.
175 updateValue(std::numeric_limits<double>::quiet_NaN());
176}
177
178std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
179 const std::chrono::steady_clock::time_point& now) const
180{
181 // Comparing 2 time_point will return duration
182 return (now - writeLast);
183}
184
185std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
186 const std::chrono::steady_clock::time_point& now) const
187{
188 // Comparing duration will return another duration
189 return (writeTimeout - ageElapsed(now));
190}
191
192void ExternalSensor::externalSetTrigger(void)
193{
194 if constexpr (debug)
195 {
196 std::cerr << "ExternalSensor " << name << " received " << value << "\n";
197 }
198
199 auto now = std::chrono::steady_clock::now();
200
201 writeBegin(now);
202
203 // Tell the owner to recalculate the expiration timer
204 writeHook(now);
205}