#include "ExternalSensor.hpp" #include "SensorPaths.hpp" #include "Thresholds.hpp" #include "Utils.hpp" #include "sensor.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr bool debug = false; ExternalSensor::ExternalSensor( const std::string& objectType, sdbusplus::asio::object_server& objectServer, std::shared_ptr& conn, const std::string& sensorName, const std::string& sensorUnits, std::vector&& 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::duration(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(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(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&& 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::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); }