blob: 3a6c4aaaad43f01d5e148641423e78f61d398464 [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
Josh Lehan2a40e932020-09-02 11:48:14 -07007#include <boost/date_time/posix_time/posix_time.hpp>
8#include <sdbusplus/asio/connection.hpp>
9#include <sdbusplus/asio/object_server.hpp>
10
Josh Lehan72432172021-03-17 13:35:43 -070011#include <chrono>
Josh Lehan2a40e932020-09-02 11:48:14 -070012#include <iostream>
13#include <istream>
14#include <limits>
15#include <memory>
16#include <string>
17#include <vector>
18
Josh Lehan72432172021-03-17 13:35:43 -070019static constexpr bool debug = false;
20
Josh Lehan2a40e932020-09-02 11:48:14 -070021ExternalSensor::ExternalSensor(
22 const std::string& objectType, sdbusplus::asio::object_server& objectServer,
23 std::shared_ptr<sdbusplus::asio::connection>& conn,
24 const std::string& sensorName, const std::string& sensorUnits,
Jeff Lin7b7a9de2021-02-22 11:16:27 +080025 std::vector<thresholds::Threshold>&& thresholdsIn,
Josh Lehan72432172021-03-17 13:35:43 -070026 const std::string& sensorConfiguration, double maxReading,
Josh Lehan03627382021-03-17 13:35:43 -070027 double minReading, double timeoutSecs, const PowerState& powerState) :
Zhikui Renda98f092021-11-01 09:41:08 -070028 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
29 objectType, true, true, maxReading, minReading, conn, powerState),
Ed Tanous2049bd22022-07-09 07:20:26 -070030 objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
Josh Lehan72432172021-03-17 13:35:43 -070031 writeTimeout(
32 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
33 std::chrono::duration<double>(timeoutSecs))),
Ed Tanousb429f312022-06-27 16:09:53 -070034 writePerishable(timeoutSecs > 0.0)
Josh Lehan2a40e932020-09-02 11:48:14 -070035{
36 // The caller must specify what physical characteristic
37 // an external sensor is expected to be measuring, such as temperature,
38 // as, unlike others, this is not implied by device type name.
Ed Tanous6cb732a2021-02-18 15:33:51 -080039 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
Josh Lehan2a40e932020-09-02 11:48:14 -070040 if (dbusPath.empty())
41 {
42 throw std::runtime_error("Units not in allow list");
43 }
Ed Tanous6cb732a2021-02-18 15:33:51 -080044 std::string objectPath = "/xyz/openbmc_project/sensors/";
Josh Lehan2a40e932020-09-02 11:48:14 -070045 objectPath += dbusPath;
46 objectPath += '/';
47 objectPath += sensorName;
48
49 sensorInterface = objectServer.add_interface(
50 objectPath, "xyz.openbmc_project.Sensor.Value");
51
Jayashree Dhanapal56678082022-01-04 17:27:20 +053052 for (const auto& threshold : thresholds)
Josh Lehan2a40e932020-09-02 11:48:14 -070053 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053054 std::string interface = thresholds::getInterface(threshold.level);
55 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
56 objectServer.add_interface(objectPath, interface);
Josh Lehan2a40e932020-09-02 11:48:14 -070057 }
58
59 association =
60 objectServer.add_interface(objectPath, association::interface);
Andrei Kartashev39287412022-02-04 16:04:47 +030061 setInitialProperties(sensorUnits);
Josh Lehan72432172021-03-17 13:35:43 -070062
Josh Lehan72432172021-03-17 13:35:43 -070063 if constexpr (debug)
64 {
65 std::cerr << "ExternalSensor " << name << " constructed: path "
66 << configurationPath << ", type " << objectType << ", min "
67 << minReading << ", max " << maxReading << ", timeout "
68 << std::chrono::duration_cast<std::chrono::microseconds>(
69 writeTimeout)
70 .count()
71 << " us\n";
72 }
Josh Lehan2a40e932020-09-02 11:48:14 -070073}
74
Josh Lehan03627382021-03-17 13:35:43 -070075// Separate function from constructor, because of a gotcha: can't use the
76// enable_shared_from_this() API until after the constructor has completed.
77void ExternalSensor::initWriteHook(
78 std::function<void(std::chrono::steady_clock::time_point now)>&&
79 writeHookIn)
80{
81 // Connect ExternalSensorMain with ExternalSensor
82 writeHook = std::move(writeHookIn);
83
84 // Connect ExternalSensor with Sensor
85 auto weakThis = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -070086 externalSetHook = [weakThis]() {
Josh Lehan03627382021-03-17 13:35:43 -070087 auto lockThis = weakThis.lock();
88 if (lockThis)
89 {
90 lockThis->externalSetTrigger();
91 return;
92 }
93 if constexpr (debug)
94 {
95 std::cerr << "ExternalSensor receive ignored, sensor gone\n";
96 }
Ed Tanous8a17c302021-09-02 15:07:11 -070097 };
Josh Lehan03627382021-03-17 13:35:43 -070098}
99
Josh Lehan2a40e932020-09-02 11:48:14 -0700100ExternalSensor::~ExternalSensor()
101{
Josh Lehan72432172021-03-17 13:35:43 -0700102 // Make sure the write hook does not reference this object anymore
103 externalSetHook = nullptr;
104
Josh Lehan2a40e932020-09-02 11:48:14 -0700105 objServer.remove_interface(association);
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530106 for (const auto& iface : thresholdInterfaces)
107 {
108 objServer.remove_interface(iface);
109 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700110 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700111
112 if constexpr (debug)
113 {
114 std::cerr << "ExternalSensor " << name << " destructed\n";
115 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700116}
117
118void ExternalSensor::checkThresholds(void)
119{
120 thresholds::checkThresholds(this);
121}
Josh Lehan72432172021-03-17 13:35:43 -0700122
123bool ExternalSensor::isAliveAndPerishable(void) const
124{
125 return (writeAlive && writePerishable);
126}
127
128bool ExternalSensor::isAliveAndFresh(
129 const std::chrono::steady_clock::time_point& now) const
130{
131 // Must be alive and perishable, to have possibility of being fresh
132 if (!isAliveAndPerishable())
133 {
134 return false;
135 }
136
137 // If age, as of now, is less than timeout, it is deemed fresh
Andrew Jeffery92b96292021-05-27 16:41:31 +0930138 // NOLINTNEXTLINE
Josh Lehan72432172021-03-17 13:35:43 -0700139 return (ageElapsed(now) < writeTimeout);
140}
141
142void ExternalSensor::writeBegin(
143 const std::chrono::steady_clock::time_point& now)
144{
145 if (!writeAlive)
146 {
147 std::cerr << "ExternalSensor " << name
148 << " online, receiving first value " << value << "\n";
149 }
150
151 writeLast = now;
152 writeAlive = true;
153}
154
155void ExternalSensor::writeInvalidate(void)
156{
157 writeAlive = false;
158
159 std::cerr << "ExternalSensor " << name << " offline, timed out\n";
160
161 // Take back control of this sensor from the external override,
162 // as the external source has timed out.
163 // This allows sensor::updateValue() to work normally,
164 // as it would do for internal sensors with values from hardware.
165 overriddenState = false;
166
167 // Invalidate the existing Value, similar to what internal sensors do,
168 // when they encounter errors trying to read from hardware.
169 updateValue(std::numeric_limits<double>::quiet_NaN());
170}
171
172std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
173 const std::chrono::steady_clock::time_point& now) const
174{
175 // Comparing 2 time_point will return duration
176 return (now - writeLast);
177}
178
179std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
180 const std::chrono::steady_clock::time_point& now) const
181{
182 // Comparing duration will return another duration
183 return (writeTimeout - ageElapsed(now));
184}
185
186void ExternalSensor::externalSetTrigger(void)
187{
188 if constexpr (debug)
189 {
190 std::cerr << "ExternalSensor " << name << " received " << value << "\n";
191 }
192
193 auto now = std::chrono::steady_clock::now();
194
195 writeBegin(now);
196
197 // Tell the owner to recalculate the expiration timer
198 writeHook(now);
199}