blob: 13082e3c161197934ae47f1c38590ce865a3ba23 [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,
29 double minReading, double timeoutSecs, const PowerState& powerState,
30 std::function<void(std::chrono::steady_clock::time_point now)>&&
31 writeHookIn) :
Josh Lehan2a40e932020-09-02 11:48:14 -070032 // TODO(): When the Mutable feature is integrated,
33 // make sure all ExternalSensor instances are mutable,
34 // because that is the entire point of ExternalSensor,
35 // to accept sensor values written by an external source.
Jeff Lin7b7a9de2021-02-22 11:16:27 +080036 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
37 std::move(thresholdsIn), sensorConfiguration, objectType, maxReading,
38 minReading, conn, powerState),
Josh Lehan72432172021-03-17 13:35:43 -070039 std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer),
40 writeLast(std::chrono::steady_clock::now()),
41 writeTimeout(
42 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
43 std::chrono::duration<double>(timeoutSecs))),
44 writeAlive(false), writePerishable(timeoutSecs > 0.0),
45 writeHook(std::move(writeHookIn))
Josh Lehan2a40e932020-09-02 11:48:14 -070046{
47 // The caller must specify what physical characteristic
48 // an external sensor is expected to be measuring, such as temperature,
49 // as, unlike others, this is not implied by device type name.
Ed Tanous6cb732a2021-02-18 15:33:51 -080050 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
Josh Lehan2a40e932020-09-02 11:48:14 -070051 if (dbusPath.empty())
52 {
53 throw std::runtime_error("Units not in allow list");
54 }
Ed Tanous6cb732a2021-02-18 15:33:51 -080055 std::string objectPath = "/xyz/openbmc_project/sensors/";
Josh Lehan2a40e932020-09-02 11:48:14 -070056 objectPath += dbusPath;
57 objectPath += '/';
58 objectPath += sensorName;
59
60 sensorInterface = objectServer.add_interface(
61 objectPath, "xyz.openbmc_project.Sensor.Value");
62
63 if (thresholds::hasWarningInterface(thresholds))
64 {
65 thresholdInterfaceWarning = objectServer.add_interface(
66 objectPath, "xyz.openbmc_project.Sensor.Threshold.Warning");
67 }
68 if (thresholds::hasCriticalInterface(thresholds))
69 {
70 thresholdInterfaceCritical = objectServer.add_interface(
71 objectPath, "xyz.openbmc_project.Sensor.Threshold.Critical");
72 }
73
74 association =
75 objectServer.add_interface(objectPath, association::interface);
76 setInitialProperties(conn);
Josh Lehan72432172021-03-17 13:35:43 -070077
78 externalSetHook = [weakThis = weak_from_this()]() {
79 auto lockThis = weakThis.lock();
80 if (lockThis)
81 {
82 lockThis->externalSetTrigger();
83 }
84 };
85
86 if constexpr (debug)
87 {
88 std::cerr << "ExternalSensor " << name << " constructed: path "
89 << configurationPath << ", type " << objectType << ", min "
90 << minReading << ", max " << maxReading << ", timeout "
91 << std::chrono::duration_cast<std::chrono::microseconds>(
92 writeTimeout)
93 .count()
94 << " us\n";
95 }
Josh Lehan2a40e932020-09-02 11:48:14 -070096}
97
98ExternalSensor::~ExternalSensor()
99{
Josh Lehan72432172021-03-17 13:35:43 -0700100 // Make sure the write hook does not reference this object anymore
101 externalSetHook = nullptr;
102
Josh Lehan2a40e932020-09-02 11:48:14 -0700103 objServer.remove_interface(association);
104 objServer.remove_interface(thresholdInterfaceCritical);
105 objServer.remove_interface(thresholdInterfaceWarning);
106 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700107
108 if constexpr (debug)
109 {
110 std::cerr << "ExternalSensor " << name << " destructed\n";
111 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700112}
113
114void ExternalSensor::checkThresholds(void)
115{
116 thresholds::checkThresholds(this);
117}
Josh Lehan72432172021-03-17 13:35:43 -0700118
119bool ExternalSensor::isAliveAndPerishable(void) const
120{
121 return (writeAlive && writePerishable);
122}
123
124bool ExternalSensor::isAliveAndFresh(
125 const std::chrono::steady_clock::time_point& now) const
126{
127 // Must be alive and perishable, to have possibility of being fresh
128 if (!isAliveAndPerishable())
129 {
130 return false;
131 }
132
133 // If age, as of now, is less than timeout, it is deemed fresh
134 return (ageElapsed(now) < writeTimeout);
135}
136
137void ExternalSensor::writeBegin(
138 const std::chrono::steady_clock::time_point& now)
139{
140 if (!writeAlive)
141 {
142 std::cerr << "ExternalSensor " << name
143 << " online, receiving first value " << value << "\n";
144 }
145
146 writeLast = now;
147 writeAlive = true;
148}
149
150void ExternalSensor::writeInvalidate(void)
151{
152 writeAlive = false;
153
154 std::cerr << "ExternalSensor " << name << " offline, timed out\n";
155
156 // Take back control of this sensor from the external override,
157 // as the external source has timed out.
158 // This allows sensor::updateValue() to work normally,
159 // as it would do for internal sensors with values from hardware.
160 overriddenState = false;
161
162 // Invalidate the existing Value, similar to what internal sensors do,
163 // when they encounter errors trying to read from hardware.
164 updateValue(std::numeric_limits<double>::quiet_NaN());
165}
166
167std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
168 const std::chrono::steady_clock::time_point& now) const
169{
170 // Comparing 2 time_point will return duration
171 return (now - writeLast);
172}
173
174std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
175 const std::chrono::steady_clock::time_point& now) const
176{
177 // Comparing duration will return another duration
178 return (writeTimeout - ageElapsed(now));
179}
180
181void ExternalSensor::externalSetTrigger(void)
182{
183 if constexpr (debug)
184 {
185 std::cerr << "ExternalSensor " << name << " received " << value << "\n";
186 }
187
188 auto now = std::chrono::steady_clock::now();
189
190 writeBegin(now);
191
192 // Tell the owner to recalculate the expiration timer
193 writeHook(now);
194}