| #include "ExternalSensor.hpp" | 
 |  | 
 | #include "SensorPaths.hpp" | 
 | #include "Thresholds.hpp" | 
 | #include "Utils.hpp" | 
 | #include "sensor.hpp" | 
 |  | 
 | #include <phosphor-logging/lg2.hpp> | 
 | #include <sdbusplus/asio/connection.hpp> | 
 | #include <sdbusplus/asio/object_server.hpp> | 
 |  | 
 | #include <chrono> | 
 | #include <cmath> | 
 | #include <cstddef> | 
 | #include <functional> | 
 | #include <limits> | 
 | #include <memory> | 
 | #include <stdexcept> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | static constexpr bool debug = false; | 
 |  | 
 | ExternalSensor::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) : | 
 |     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, | 
 |            objectType, true, true, maxReading, minReading, conn, powerState), | 
 |     objServer(objectServer), writeLast(std::chrono::steady_clock::now()), | 
 |     writeTimeout( | 
 |         std::chrono::duration_cast<std::chrono::steady_clock::duration>( | 
 |             std::chrono::duration<double>(timeoutSecs))), | 
 |     writePerishable(timeoutSecs > 0.0) | 
 | { | 
 |     // The caller must specify what physical characteristic | 
 |     // an external sensor is expected to be measuring, such as temperature, | 
 |     // as, unlike others, this is not implied by device type name. | 
 |     std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits); | 
 |     if (dbusPath.empty()) | 
 |     { | 
 |         throw std::runtime_error("Units not in allow list"); | 
 |     } | 
 |     std::string objectPath = "/xyz/openbmc_project/sensors/"; | 
 |     objectPath += dbusPath; | 
 |     objectPath += '/'; | 
 |     objectPath += name; | 
 |  | 
 |     sensorInterface = objectServer.add_interface( | 
 |         objectPath, "xyz.openbmc_project.Sensor.Value"); | 
 |  | 
 |     for (const auto& threshold : thresholds) | 
 |     { | 
 |         std::string interface = thresholds::getInterface(threshold.level); | 
 |         thresholdInterfaces[static_cast<size_t>(threshold.level)] = | 
 |             objectServer.add_interface(objectPath, interface); | 
 |     } | 
 |  | 
 |     association = | 
 |         objectServer.add_interface(objectPath, association::interface); | 
 |     setInitialProperties(sensorUnits); | 
 |  | 
 |     if constexpr (debug) | 
 |     { | 
 |         lg2::error( | 
 |             "ExternalSensor '{NAME}' constructed: path '{PATH}', type '{TYPE}', " | 
 |             "min '{MIN}', max '{MAX}', timeout '{TIMEOUT}' us", | 
 |             "NAME", name, "PATH", objectPath, "TYPE", objectType, "MIN", | 
 |             minReading, "MAX", maxReading, "TIMEOUT", | 
 |             std::chrono::duration_cast<std::chrono::microseconds>(writeTimeout) | 
 |                 .count()); | 
 |     } | 
 | } | 
 |  | 
 | // Separate function from constructor, because of a gotcha: can't use the | 
 | // enable_shared_from_this() API until after the constructor has completed. | 
 | void ExternalSensor::initWriteHook( | 
 |     std::function<void(std::chrono::steady_clock::time_point now)>&& | 
 |         writeHookIn) | 
 | { | 
 |     // Connect ExternalSensorMain with ExternalSensor | 
 |     writeHook = std::move(writeHookIn); | 
 |  | 
 |     // Connect ExternalSensor with Sensor | 
 |     auto weakThis = weak_from_this(); | 
 |     externalSetHook = [weakThis]() { | 
 |         auto lockThis = weakThis.lock(); | 
 |         if (lockThis) | 
 |         { | 
 |             lockThis->externalSetTrigger(); | 
 |             return; | 
 |         } | 
 |         if constexpr (debug) | 
 |         { | 
 |             lg2::error("ExternalSensor receive ignored, sensor gone"); | 
 |         } | 
 |     }; | 
 | } | 
 |  | 
 | ExternalSensor::~ExternalSensor() | 
 | { | 
 |     // Make sure the write hook does not reference this object anymore | 
 |     externalSetHook = nullptr; | 
 |  | 
 |     objServer.remove_interface(association); | 
 |     for (const auto& iface : thresholdInterfaces) | 
 |     { | 
 |         objServer.remove_interface(iface); | 
 |     } | 
 |     objServer.remove_interface(sensorInterface); | 
 |  | 
 |     if constexpr (debug) | 
 |     { | 
 |         lg2::error("ExternalSensor '{NAME}' destructed", "NAME", name); | 
 |     } | 
 | } | 
 |  | 
 | void ExternalSensor::checkThresholds() | 
 | { | 
 |     thresholds::checkThresholds(this); | 
 | } | 
 |  | 
 | bool ExternalSensor::isAliveAndPerishable() const | 
 | { | 
 |     return (writeAlive && writePerishable); | 
 | } | 
 |  | 
 | bool ExternalSensor::isAliveAndFresh( | 
 |     const std::chrono::steady_clock::time_point& now) const | 
 | { | 
 |     // Must be alive and perishable, to have possibility of being fresh | 
 |     if (!isAliveAndPerishable()) | 
 |     { | 
 |         return false; | 
 |     } | 
 |  | 
 |     // If age, as of now, is less than timeout, it is deemed fresh | 
 |     // NOLINTNEXTLINE | 
 |     return (ageElapsed(now) < writeTimeout); | 
 | } | 
 |  | 
 | void ExternalSensor::writeBegin( | 
 |     const std::chrono::steady_clock::time_point& now) | 
 | { | 
 |     if (!writeAlive) | 
 |     { | 
 |         lg2::error( | 
 |             "ExternalSensor '{NAME}' online, receiving first value '{VALUE}'", | 
 |             "NAME", name, "VALUE", value); | 
 |     } | 
 |  | 
 |     writeLast = now; | 
 |     writeAlive = true; | 
 | } | 
 |  | 
 | void ExternalSensor::writeInvalidate() | 
 | { | 
 |     writeAlive = false; | 
 |  | 
 |     lg2::error("ExternalSensor '{NAME}' offline, timed out", "NAME", name); | 
 |  | 
 |     // Take back control of this sensor from the external override, | 
 |     // as the external source has timed out. | 
 |     // This allows sensor::updateValue() to work normally, | 
 |     // as it would do for internal sensors with values from hardware. | 
 |     overriddenState = false; | 
 |  | 
 |     // Invalidate the existing Value, similar to what internal sensors do, | 
 |     // when they encounter errors trying to read from hardware. | 
 |     updateValue(std::numeric_limits<double>::quiet_NaN()); | 
 | } | 
 |  | 
 | std::chrono::steady_clock::duration ExternalSensor::ageElapsed( | 
 |     const std::chrono::steady_clock::time_point& now) const | 
 | { | 
 |     // Comparing 2 time_point will return duration | 
 |     return (now - writeLast); | 
 | } | 
 |  | 
 | std::chrono::steady_clock::duration ExternalSensor::ageRemaining( | 
 |     const std::chrono::steady_clock::time_point& now) const | 
 | { | 
 |     // Comparing duration will return another duration | 
 |     return (writeTimeout - ageElapsed(now)); | 
 | } | 
 |  | 
 | void ExternalSensor::externalSetTrigger() | 
 | { | 
 |     if constexpr (debug) | 
 |     { | 
 |         lg2::error("ExternalSensor '{NAME}' received '{VALUE}'", "NAME", name, | 
 |                    "VALUE", value); | 
 |     } | 
 |  | 
 |     if (std::isfinite(value)) | 
 |     { | 
 |         markAvailable(true); | 
 |     } | 
 |  | 
 |     auto now = std::chrono::steady_clock::now(); | 
 |  | 
 |     writeBegin(now); | 
 |  | 
 |     // Tell the owner to recalculate the expiration timer | 
 |     writeHook(now); | 
 | } |