blob: 961206ca885b667b66d0035e9f37f716ba42a952 [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>
Josh Lehan2a40e932020-09-02 11:48:14 -07008#include <boost/date_time/posix_time/posix_time.hpp>
9#include <sdbusplus/asio/connection.hpp>
10#include <sdbusplus/asio/object_server.hpp>
11
Josh Lehan72432172021-03-17 13:35:43 -070012#include <chrono>
Josh Lehan2a40e932020-09-02 11:48:14 -070013#include <iostream>
14#include <istream>
15#include <limits>
16#include <memory>
17#include <string>
18#include <vector>
19
Josh Lehan72432172021-03-17 13:35:43 -070020static constexpr bool debug = false;
21
Josh Lehan2a40e932020-09-02 11:48:14 -070022ExternalSensor::ExternalSensor(
23 const std::string& objectType, sdbusplus::asio::object_server& objectServer,
24 std::shared_ptr<sdbusplus::asio::connection>& conn,
25 const std::string& sensorName, const std::string& sensorUnits,
Jeff Lin7b7a9de2021-02-22 11:16:27 +080026 std::vector<thresholds::Threshold>&& thresholdsIn,
Josh Lehan72432172021-03-17 13:35:43 -070027 const std::string& sensorConfiguration, double maxReading,
Josh Lehan03627382021-03-17 13:35:43 -070028 double minReading, double timeoutSecs, const PowerState& powerState) :
Zhikui Renda98f092021-11-01 09:41:08 -070029 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
30 objectType, true, true, maxReading, minReading, conn, powerState),
Josh Lehan72432172021-03-17 13:35:43 -070031 std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer),
32 writeLast(std::chrono::steady_clock::now()),
33 writeTimeout(
34 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
35 std::chrono::duration<double>(timeoutSecs))),
Josh Lehan03627382021-03-17 13:35:43 -070036 writeAlive(false), writePerishable(timeoutSecs > 0.0)
Josh Lehan2a40e932020-09-02 11:48:14 -070037{
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.
Ed Tanous6cb732a2021-02-18 15:33:51 -080041 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
Josh Lehan2a40e932020-09-02 11:48:14 -070042 if (dbusPath.empty())
43 {
44 throw std::runtime_error("Units not in allow list");
45 }
Ed Tanous6cb732a2021-02-18 15:33:51 -080046 std::string objectPath = "/xyz/openbmc_project/sensors/";
Josh Lehan2a40e932020-09-02 11:48:14 -070047 objectPath += dbusPath;
48 objectPath += '/';
49 objectPath += sensorName;
50
51 sensorInterface = objectServer.add_interface(
52 objectPath, "xyz.openbmc_project.Sensor.Value");
53
54 if (thresholds::hasWarningInterface(thresholds))
55 {
56 thresholdInterfaceWarning = objectServer.add_interface(
57 objectPath, "xyz.openbmc_project.Sensor.Threshold.Warning");
58 }
59 if (thresholds::hasCriticalInterface(thresholds))
60 {
61 thresholdInterfaceCritical = objectServer.add_interface(
62 objectPath, "xyz.openbmc_project.Sensor.Threshold.Critical");
63 }
64
65 association =
66 objectServer.add_interface(objectPath, association::interface);
Zev Weiss6b6891c2021-04-22 02:46:21 -050067 setInitialProperties(conn, sensorUnits);
Josh Lehan72432172021-03-17 13:35:43 -070068
Josh Lehan72432172021-03-17 13:35:43 -070069 if constexpr (debug)
70 {
71 std::cerr << "ExternalSensor " << name << " constructed: path "
72 << configurationPath << ", type " << objectType << ", min "
73 << minReading << ", max " << maxReading << ", timeout "
74 << std::chrono::duration_cast<std::chrono::microseconds>(
75 writeTimeout)
76 .count()
77 << " us\n";
78 }
Josh Lehan2a40e932020-09-02 11:48:14 -070079}
80
Josh Lehan03627382021-03-17 13:35:43 -070081// Separate function from constructor, because of a gotcha: can't use the
82// enable_shared_from_this() API until after the constructor has completed.
83void ExternalSensor::initWriteHook(
84 std::function<void(std::chrono::steady_clock::time_point now)>&&
85 writeHookIn)
86{
87 // Connect ExternalSensorMain with ExternalSensor
88 writeHook = std::move(writeHookIn);
89
90 // Connect ExternalSensor with Sensor
91 auto weakThis = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -070092 externalSetHook = [weakThis]() {
Josh Lehan03627382021-03-17 13:35:43 -070093 auto lockThis = weakThis.lock();
94 if (lockThis)
95 {
96 lockThis->externalSetTrigger();
97 return;
98 }
99 if constexpr (debug)
100 {
101 std::cerr << "ExternalSensor receive ignored, sensor gone\n";
102 }
Ed Tanous8a17c302021-09-02 15:07:11 -0700103 };
Josh Lehan03627382021-03-17 13:35:43 -0700104}
105
Josh Lehan2a40e932020-09-02 11:48:14 -0700106ExternalSensor::~ExternalSensor()
107{
Josh Lehan72432172021-03-17 13:35:43 -0700108 // Make sure the write hook does not reference this object anymore
109 externalSetHook = nullptr;
110
Josh Lehan2a40e932020-09-02 11:48:14 -0700111 objServer.remove_interface(association);
112 objServer.remove_interface(thresholdInterfaceCritical);
113 objServer.remove_interface(thresholdInterfaceWarning);
114 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700115
116 if constexpr (debug)
117 {
118 std::cerr << "ExternalSensor " << name << " destructed\n";
119 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700120}
121
122void ExternalSensor::checkThresholds(void)
123{
124 thresholds::checkThresholds(this);
125}
Josh Lehan72432172021-03-17 13:35:43 -0700126
127bool ExternalSensor::isAliveAndPerishable(void) const
128{
129 return (writeAlive && writePerishable);
130}
131
132bool ExternalSensor::isAliveAndFresh(
133 const std::chrono::steady_clock::time_point& now) const
134{
135 // Must be alive and perishable, to have possibility of being fresh
136 if (!isAliveAndPerishable())
137 {
138 return false;
139 }
140
141 // If age, as of now, is less than timeout, it is deemed fresh
Andrew Jeffery92b96292021-05-27 16:41:31 +0930142 // NOLINTNEXTLINE
Josh Lehan72432172021-03-17 13:35:43 -0700143 return (ageElapsed(now) < writeTimeout);
144}
145
146void ExternalSensor::writeBegin(
147 const std::chrono::steady_clock::time_point& now)
148{
149 if (!writeAlive)
150 {
151 std::cerr << "ExternalSensor " << name
152 << " online, receiving first value " << value << "\n";
153 }
154
155 writeLast = now;
156 writeAlive = true;
157}
158
159void ExternalSensor::writeInvalidate(void)
160{
161 writeAlive = false;
162
163 std::cerr << "ExternalSensor " << name << " offline, timed out\n";
164
165 // Take back control of this sensor from the external override,
166 // as the external source has timed out.
167 // This allows sensor::updateValue() to work normally,
168 // as it would do for internal sensors with values from hardware.
169 overriddenState = false;
170
171 // Invalidate the existing Value, similar to what internal sensors do,
172 // when they encounter errors trying to read from hardware.
173 updateValue(std::numeric_limits<double>::quiet_NaN());
174}
175
176std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
177 const std::chrono::steady_clock::time_point& now) const
178{
179 // Comparing 2 time_point will return duration
180 return (now - writeLast);
181}
182
183std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
184 const std::chrono::steady_clock::time_point& now) const
185{
186 // Comparing duration will return another duration
187 return (writeTimeout - ageElapsed(now));
188}
189
190void ExternalSensor::externalSetTrigger(void)
191{
192 if constexpr (debug)
193 {
194 std::cerr << "ExternalSensor " << name << " received " << value << "\n";
195 }
196
197 auto now = std::chrono::steady_clock::now();
198
199 writeBegin(now);
200
201 // Tell the owner to recalculate the expiration timer
202 writeHook(now);
203}