blob: 4a3b369ea20a41ce0460686d6a204eb4dec8358f [file] [log] [blame]
Josh Lehan2a40e932020-09-02 11:48:14 -07001#include "ExternalSensor.hpp"
2
3#include "SensorPaths.hpp"
Ed Tanouseacbfdd2024-04-04 12:00:24 -07004#include "Thresholds.hpp"
5#include "Utils.hpp"
6#include "sensor.hpp"
Josh Lehan2a40e932020-09-02 11:48:14 -07007
George Liud630b3a2025-02-20 11:02:49 +08008#include <phosphor-logging/lg2.hpp>
Josh Lehan2a40e932020-09-02 11:48:14 -07009#include <sdbusplus/asio/connection.hpp>
10#include <sdbusplus/asio/object_server.hpp>
11
Josh Lehan72432172021-03-17 13:35:43 -070012#include <chrono>
Potin Lai02c43662025-05-01 00:03:12 +080013#include <cmath>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070014#include <cstddef>
15#include <functional>
Josh Lehan2a40e932020-09-02 11:48:14 -070016#include <limits>
17#include <memory>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070018#include <stdexcept>
Josh Lehan2a40e932020-09-02 11:48:14 -070019#include <string>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070020#include <utility>
Josh Lehan2a40e932020-09-02 11:48:14 -070021#include <vector>
22
23ExternalSensor::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,
Josh Lehan03627382021-03-17 13:35:43 -070029 double minReading, double timeoutSecs, const PowerState& powerState) :
Zhikui Renda98f092021-11-01 09:41:08 -070030 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
31 objectType, true, true, maxReading, minReading, conn, powerState),
Ed Tanous2049bd22022-07-09 07:20:26 -070032 objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
Josh Lehan72432172021-03-17 13:35:43 -070033 writeTimeout(
34 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
35 std::chrono::duration<double>(timeoutSecs))),
Ed Tanousb429f312022-06-27 16:09:53 -070036 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 += '/';
Potin Laidb26db22025-02-11 17:00:47 +080049 objectPath += name;
Josh Lehan2a40e932020-09-02 11:48:14 -070050
51 sensorInterface = objectServer.add_interface(
52 objectPath, "xyz.openbmc_project.Sensor.Value");
53
Jayashree Dhanapal56678082022-01-04 17:27:20 +053054 for (const auto& threshold : thresholds)
Josh Lehan2a40e932020-09-02 11:48:14 -070055 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053056 std::string interface = thresholds::getInterface(threshold.level);
57 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
58 objectServer.add_interface(objectPath, interface);
Josh Lehan2a40e932020-09-02 11:48:14 -070059 }
60
Patrick Williams2aaf7172024-08-16 15:20:40 -040061 association =
62 objectServer.add_interface(objectPath, association::interface);
Andrei Kartashev39287412022-02-04 16:04:47 +030063 setInitialProperties(sensorUnits);
Josh Lehan72432172021-03-17 13:35:43 -070064
Alexander Hansen89be6142025-09-18 15:36:16 +020065 lg2::debug(
66 "ExternalSensor '{NAME}' constructed: path '{PATH}', type '{TYPE}', "
67 "min '{MIN}', max '{MAX}', timeout '{TIMEOUT}' us",
68 "NAME", name, "PATH", objectPath, "TYPE", objectType, "MIN", minReading,
69 "MAX", maxReading, "TIMEOUT",
70 std::chrono::duration_cast<std::chrono::microseconds>(writeTimeout)
71 .count());
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 }
Alexander Hansen89be6142025-09-18 15:36:16 +020092 lg2::debug("ExternalSensor receive ignored, sensor gone");
Ed Tanous8a17c302021-09-02 15:07:11 -070093 };
Josh Lehan03627382021-03-17 13:35:43 -070094}
95
Josh Lehan2a40e932020-09-02 11:48:14 -070096ExternalSensor::~ExternalSensor()
97{
Josh Lehan72432172021-03-17 13:35:43 -070098 // Make sure the write hook does not reference this object anymore
99 externalSetHook = nullptr;
100
Josh Lehan2a40e932020-09-02 11:48:14 -0700101 objServer.remove_interface(association);
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530102 for (const auto& iface : thresholdInterfaces)
103 {
104 objServer.remove_interface(iface);
105 }
Josh Lehan2a40e932020-09-02 11:48:14 -0700106 objServer.remove_interface(sensorInterface);
Josh Lehan72432172021-03-17 13:35:43 -0700107
Alexander Hansen89be6142025-09-18 15:36:16 +0200108 lg2::debug("ExternalSensor '{NAME}' destructed", "NAME", name);
Josh Lehan2a40e932020-09-02 11:48:14 -0700109}
110
Ed Tanous201a1012024-04-03 18:07:28 -0700111void ExternalSensor::checkThresholds()
Josh Lehan2a40e932020-09-02 11:48:14 -0700112{
113 thresholds::checkThresholds(this);
114}
Josh Lehan72432172021-03-17 13:35:43 -0700115
Ed Tanous201a1012024-04-03 18:07:28 -0700116bool ExternalSensor::isAliveAndPerishable() const
Josh Lehan72432172021-03-17 13:35:43 -0700117{
118 return (writeAlive && writePerishable);
119}
120
121bool ExternalSensor::isAliveAndFresh(
122 const std::chrono::steady_clock::time_point& now) const
123{
124 // Must be alive and perishable, to have possibility of being fresh
125 if (!isAliveAndPerishable())
126 {
127 return false;
128 }
129
130 // If age, as of now, is less than timeout, it is deemed fresh
Andrew Jeffery92b96292021-05-27 16:41:31 +0930131 // NOLINTNEXTLINE
Josh Lehan72432172021-03-17 13:35:43 -0700132 return (ageElapsed(now) < writeTimeout);
133}
134
Patrick Williams556e04b2025-02-01 08:22:22 -0500135void ExternalSensor::writeBegin(
136 const std::chrono::steady_clock::time_point& now)
Josh Lehan72432172021-03-17 13:35:43 -0700137{
138 if (!writeAlive)
139 {
George Liud630b3a2025-02-20 11:02:49 +0800140 lg2::error(
141 "ExternalSensor '{NAME}' online, receiving first value '{VALUE}'",
142 "NAME", name, "VALUE", value);
Josh Lehan72432172021-03-17 13:35:43 -0700143 }
144
145 writeLast = now;
146 writeAlive = true;
147}
148
Ed Tanous201a1012024-04-03 18:07:28 -0700149void ExternalSensor::writeInvalidate()
Josh Lehan72432172021-03-17 13:35:43 -0700150{
151 writeAlive = false;
152
George Liud630b3a2025-02-20 11:02:49 +0800153 lg2::error("ExternalSensor '{NAME}' offline, timed out", "NAME", name);
Josh Lehan72432172021-03-17 13:35:43 -0700154
155 // Take back control of this sensor from the external override,
156 // as the external source has timed out.
157 // This allows sensor::updateValue() to work normally,
158 // as it would do for internal sensors with values from hardware.
159 overriddenState = false;
160
161 // Invalidate the existing Value, similar to what internal sensors do,
162 // when they encounter errors trying to read from hardware.
163 updateValue(std::numeric_limits<double>::quiet_NaN());
164}
165
166std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
167 const std::chrono::steady_clock::time_point& now) const
168{
169 // Comparing 2 time_point will return duration
170 return (now - writeLast);
171}
172
173std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
174 const std::chrono::steady_clock::time_point& now) const
175{
176 // Comparing duration will return another duration
177 return (writeTimeout - ageElapsed(now));
178}
179
Ed Tanous201a1012024-04-03 18:07:28 -0700180void ExternalSensor::externalSetTrigger()
Josh Lehan72432172021-03-17 13:35:43 -0700181{
Alexander Hansen89be6142025-09-18 15:36:16 +0200182 lg2::debug("ExternalSensor '{NAME}' received '{VALUE}'", "NAME", name,
183 "VALUE", value);
Josh Lehan72432172021-03-17 13:35:43 -0700184
Potin Lai02c43662025-05-01 00:03:12 +0800185 if (std::isfinite(value))
186 {
187 markAvailable(true);
188 }
189
Josh Lehan72432172021-03-17 13:35:43 -0700190 auto now = std::chrono::steady_clock::now();
191
192 writeBegin(now);
193
194 // Tell the owner to recalculate the expiration timer
195 writeHook(now);
196}