| #include "ExternalSensor.hpp" |
| |
| #include "SensorPaths.hpp" |
| #include "Thresholds.hpp" |
| #include "Utils.hpp" |
| #include "sensor.hpp" |
| |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| |
| #include <chrono> |
| #include <cstddef> |
| #include <functional> |
| #include <iostream> |
| #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 += sensorName; |
| |
| 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) |
| { |
| std::cerr << "ExternalSensor " << name << " constructed: path " |
| << configurationPath << ", type " << objectType << ", min " |
| << minReading << ", max " << maxReading << ", timeout " |
| << std::chrono::duration_cast<std::chrono::microseconds>( |
| writeTimeout) |
| .count() |
| << " us\n"; |
| } |
| } |
| |
| // 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) |
| { |
| std::cerr << "ExternalSensor receive ignored, sensor gone\n"; |
| } |
| }; |
| } |
| |
| 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) |
| { |
| std::cerr << "ExternalSensor " << name << " destructed\n"; |
| } |
| } |
| |
| 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) |
| { |
| std::cerr << "ExternalSensor " << name |
| << " online, receiving first value " << value << "\n"; |
| } |
| |
| writeLast = now; |
| writeAlive = true; |
| } |
| |
| void ExternalSensor::writeInvalidate() |
| { |
| writeAlive = false; |
| |
| std::cerr << "ExternalSensor " << name << " offline, timed out\n"; |
| |
| // 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) |
| { |
| std::cerr << "ExternalSensor " << name << " received " << value << "\n"; |
| } |
| |
| auto now = std::chrono::steady_clock::now(); |
| |
| writeBegin(now); |
| |
| // Tell the owner to recalculate the expiration timer |
| writeHook(now); |
| } |