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