1 #include "ExternalSensor.hpp"
2
3 #include "SensorPaths.hpp"
4 #include "Thresholds.hpp"
5 #include "Utils.hpp"
6 #include "sensor.hpp"
7
8 #include <phosphor-logging/lg2.hpp>
9 #include <sdbusplus/asio/connection.hpp>
10 #include <sdbusplus/asio/object_server.hpp>
11
12 #include <chrono>
13 #include <cmath>
14 #include <cstddef>
15 #include <functional>
16 #include <limits>
17 #include <memory>
18 #include <stdexcept>
19 #include <string>
20 #include <utility>
21 #include <vector>
22
ExternalSensor(const std::string & objectType,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & conn,const std::string & sensorName,const std::string & sensorUnits,std::vector<thresholds::Threshold> && thresholdsIn,const std::string & sensorConfiguration,double maxReading,double minReading,double timeoutSecs,const PowerState & powerState)23 ExternalSensor::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,
27 std::vector<thresholds::Threshold>&& thresholdsIn,
28 const std::string& sensorConfiguration, double maxReading,
29 double minReading, double timeoutSecs, const PowerState& powerState) :
30 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
31 objectType, true, true, maxReading, minReading, conn, powerState),
32 objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
33 writeTimeout(
34 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
35 std::chrono::duration<double>(timeoutSecs))),
36 writePerishable(timeoutSecs > 0.0)
37 {
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.
41 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
42 if (dbusPath.empty())
43 {
44 throw std::runtime_error("Units not in allow list");
45 }
46 std::string objectPath = "/xyz/openbmc_project/sensors/";
47 objectPath += dbusPath;
48 objectPath += '/';
49 objectPath += name;
50
51 sensorInterface = objectServer.add_interface(
52 objectPath, "xyz.openbmc_project.Sensor.Value");
53
54 for (const auto& threshold : thresholds)
55 {
56 std::string interface = thresholds::getInterface(threshold.level);
57 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
58 objectServer.add_interface(objectPath, interface);
59 }
60
61 association =
62 objectServer.add_interface(objectPath, association::interface);
63 setInitialProperties(sensorUnits);
64
65 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());
72 }
73
74 // Separate function from constructor, because of a gotcha: can't use the
75 // enable_shared_from_this() API until after the constructor has completed.
initWriteHook(std::function<void (std::chrono::steady_clock::time_point now)> && writeHookIn)76 void 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();
85 externalSetHook = [weakThis]() {
86 auto lockThis = weakThis.lock();
87 if (lockThis)
88 {
89 lockThis->externalSetTrigger();
90 return;
91 }
92 lg2::debug("ExternalSensor receive ignored, sensor gone");
93 };
94 }
95
~ExternalSensor()96 ExternalSensor::~ExternalSensor()
97 {
98 // Make sure the write hook does not reference this object anymore
99 externalSetHook = nullptr;
100
101 objServer.remove_interface(association);
102 for (const auto& iface : thresholdInterfaces)
103 {
104 objServer.remove_interface(iface);
105 }
106 objServer.remove_interface(sensorInterface);
107
108 lg2::debug("ExternalSensor '{NAME}' destructed", "NAME", name);
109 }
110
checkThresholds()111 void ExternalSensor::checkThresholds()
112 {
113 thresholds::checkThresholds(this);
114 }
115
isAliveAndPerishable() const116 bool ExternalSensor::isAliveAndPerishable() const
117 {
118 return (writeAlive && writePerishable);
119 }
120
isAliveAndFresh(const std::chrono::steady_clock::time_point & now) const121 bool 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
131 // NOLINTNEXTLINE
132 return (ageElapsed(now) < writeTimeout);
133 }
134
writeBegin(const std::chrono::steady_clock::time_point & now)135 void ExternalSensor::writeBegin(
136 const std::chrono::steady_clock::time_point& now)
137 {
138 if (!writeAlive)
139 {
140 lg2::error(
141 "ExternalSensor '{NAME}' online, receiving first value '{VALUE}'",
142 "NAME", name, "VALUE", value);
143 }
144
145 writeLast = now;
146 writeAlive = true;
147 }
148
writeInvalidate()149 void ExternalSensor::writeInvalidate()
150 {
151 writeAlive = false;
152
153 lg2::error("ExternalSensor '{NAME}' offline, timed out", "NAME", name);
154
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
ageElapsed(const std::chrono::steady_clock::time_point & now) const166 std::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
ageRemaining(const std::chrono::steady_clock::time_point & now) const173 std::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
externalSetTrigger()180 void ExternalSensor::externalSetTrigger()
181 {
182 lg2::debug("ExternalSensor '{NAME}' received '{VALUE}'", "NAME", name,
183 "VALUE", value);
184
185 if (std::isfinite(value))
186 {
187 markAvailable(true);
188 }
189
190 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 }
197