blob: cc9e7c54ee7beb850147a6d9ccc3df6263e1b967 [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),
Ed Tanous2049bd22022-07-09 07:20:26 -070031 objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
Josh Lehan72432172021-03-17 13:35:43 -070032 writeTimeout(
33 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
34 std::chrono::duration<double>(timeoutSecs))),
Ed Tanousb429f312022-06-27 16:09:53 -070035 writePerishable(timeoutSecs > 0.0)
Josh Lehan2a40e932020-09-02 11:48:14 -070036{
37 // The caller must specify what physical characteristic
38 // an external sensor is expected to be measuring, such as temperature,
39 // as, unlike others, this is not implied by device type name.
Ed Tanous6cb732a2021-02-18 15:33:51 -080040 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
Josh Lehan2a40e932020-09-02 11:48:14 -070041 if (dbusPath.empty())
42 {
43 throw std::runtime_error("Units not in allow list");
44 }
Ed Tanous6cb732a2021-02-18 15:33:51 -080045 std::string objectPath = "/xyz/openbmc_project/sensors/";
Josh Lehan2a40e932020-09-02 11:48:14 -070046 objectPath += dbusPath;
47 objectPath += '/';
48 objectPath += sensorName;
49
50 sensorInterface = objectServer.add_interface(
51 objectPath, "xyz.openbmc_project.Sensor.Value");
52
Jayashree Dhanapal56678082022-01-04 17:27:20 +053053 for (const auto& threshold : thresholds)
Josh Lehan2a40e932020-09-02 11:48:14 -070054 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053055 std::string interface = thresholds::getInterface(threshold.level);
56 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
57 objectServer.add_interface(objectPath, interface);
Josh Lehan2a40e932020-09-02 11:48:14 -070058 }
59
60 association =
61 objectServer.add_interface(objectPath, association::interface);
Andrei Kartashev39287412022-02-04 16:04:47 +030062 setInitialProperties(sensorUnits);
Josh Lehan72432172021-03-17 13:35:43 -070063
Josh Lehan72432172021-03-17 13:35:43 -070064 if constexpr (debug)
65 {
66 std::cerr << "ExternalSensor " << name << " constructed: path "
67 << configurationPath << ", type " << objectType << ", min "
68 << minReading << ", max " << maxReading << ", timeout "
69 << std::chrono::duration_cast<std::chrono::microseconds>(
70 writeTimeout)
71 .count()
72 << " us\n";
73 }
Josh Lehan2a40e932020-09-02 11:48:14 -070074}
75
Josh Lehan03627382021-03-17 13:35:43 -070076// Separate function from constructor, because of a gotcha: can't use the
77// enable_shared_from_this() API until after the constructor has completed.
78void ExternalSensor::initWriteHook(
79 std::function<void(std::chrono::steady_clock::time_point now)>&&
80 writeHookIn)
81{
82 // Connect ExternalSensorMain with ExternalSensor
83 writeHook = std::move(writeHookIn);
84
85 // Connect ExternalSensor with Sensor
86 auto weakThis = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -070087 externalSetHook = [weakThis]() {
Josh Lehan03627382021-03-17 13:35:43 -070088 auto lockThis = weakThis.lock();
89 if (lockThis)
90 {
91 lockThis->externalSetTrigger();
92 return;
93 }
94 if constexpr (debug)
95 {
96 std::cerr << "ExternalSensor receive ignored, sensor gone\n";
97 }
Ed Tanous8a17c302021-09-02 15:07:11 -070098 };
Josh Lehan03627382021-03-17 13:35:43 -070099}
100
Josh Lehan2a40e932020-09-02 11:48:14 -0700101ExternalSensor::~ExternalSensor()
102{
Josh Lehan72432172021-03-17 13:35:43 -0700103 // Make sure the write hook does not reference this object anymore
104 externalSetHook = nullptr;
105
Josh Lehan2a40e932020-09-02 11:48:14 -0700106 objServer.remove_interface(association);
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530107 for (const auto& iface : thresholdInterfaces)
108 {
109 objServer.remove_interface(iface);
110 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700111 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700112
113 if constexpr (debug)
114 {
115 std::cerr << "ExternalSensor " << name << " destructed\n";
116 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700117}
118
119void ExternalSensor::checkThresholds(void)
120{
121 thresholds::checkThresholds(this);
122}
Josh Lehan72432172021-03-17 13:35:43 -0700123
124bool ExternalSensor::isAliveAndPerishable(void) const
125{
126 return (writeAlive && writePerishable);
127}
128
129bool ExternalSensor::isAliveAndFresh(
130 const std::chrono::steady_clock::time_point& now) const
131{
132 // Must be alive and perishable, to have possibility of being fresh
133 if (!isAliveAndPerishable())
134 {
135 return false;
136 }
137
138 // If age, as of now, is less than timeout, it is deemed fresh
Andrew Jeffery92b96292021-05-27 16:41:31 +0930139 // NOLINTNEXTLINE
Josh Lehan72432172021-03-17 13:35:43 -0700140 return (ageElapsed(now) < writeTimeout);
141}
142
143void ExternalSensor::writeBegin(
144 const std::chrono::steady_clock::time_point& now)
145{
146 if (!writeAlive)
147 {
148 std::cerr << "ExternalSensor " << name
149 << " online, receiving first value " << value << "\n";
150 }
151
152 writeLast = now;
153 writeAlive = true;
154}
155
156void ExternalSensor::writeInvalidate(void)
157{
158 writeAlive = false;
159
160 std::cerr << "ExternalSensor " << name << " offline, timed out\n";
161
162 // Take back control of this sensor from the external override,
163 // as the external source has timed out.
164 // This allows sensor::updateValue() to work normally,
165 // as it would do for internal sensors with values from hardware.
166 overriddenState = false;
167
168 // Invalidate the existing Value, similar to what internal sensors do,
169 // when they encounter errors trying to read from hardware.
170 updateValue(std::numeric_limits<double>::quiet_NaN());
171}
172
173std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
174 const std::chrono::steady_clock::time_point& now) const
175{
176 // Comparing 2 time_point will return duration
177 return (now - writeLast);
178}
179
180std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
181 const std::chrono::steady_clock::time_point& now) const
182{
183 // Comparing duration will return another duration
184 return (writeTimeout - ageElapsed(now));
185}
186
187void ExternalSensor::externalSetTrigger(void)
188{
189 if constexpr (debug)
190 {
191 std::cerr << "ExternalSensor " << name << " received " << value << "\n";
192 }
193
194 auto now = std::chrono::steady_clock::now();
195
196 writeBegin(now);
197
198 // Tell the owner to recalculate the expiration timer
199 writeHook(now);
200}