12a40e939SJosh Lehan #include "ExternalSensor.hpp" 22a40e939SJosh Lehan 32a40e939SJosh Lehan #include "SensorPaths.hpp" 42a40e939SJosh Lehan 52a40e939SJosh Lehan #include <unistd.h> 62a40e939SJosh Lehan 72a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp> 82a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp> 92a40e939SJosh Lehan 107243217bSJosh Lehan #include <chrono> 112a40e939SJosh Lehan #include <iostream> 122a40e939SJosh Lehan #include <istream> 132a40e939SJosh Lehan #include <limits> 142a40e939SJosh Lehan #include <memory> 152a40e939SJosh Lehan #include <string> 162a40e939SJosh Lehan #include <vector> 172a40e939SJosh Lehan 187243217bSJosh Lehan static constexpr bool debug = false; 197243217bSJosh Lehan 202a40e939SJosh Lehan ExternalSensor::ExternalSensor( 212a40e939SJosh Lehan const std::string& objectType, sdbusplus::asio::object_server& objectServer, 222a40e939SJosh Lehan std::shared_ptr<sdbusplus::asio::connection>& conn, 232a40e939SJosh Lehan const std::string& sensorName, const std::string& sensorUnits, 247b7a9deaSJeff Lin std::vector<thresholds::Threshold>&& thresholdsIn, 257243217bSJosh Lehan const std::string& sensorConfiguration, double maxReading, 260362738dSJosh Lehan double minReading, double timeoutSecs, const PowerState& powerState) : 27da98f095SZhikui Ren Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, 28da98f095SZhikui Ren objectType, true, true, maxReading, minReading, conn, powerState), 292049bd26SEd Tanous objServer(objectServer), writeLast(std::chrono::steady_clock::now()), 307243217bSJosh Lehan writeTimeout( 317243217bSJosh Lehan std::chrono::duration_cast<std::chrono::steady_clock::duration>( 327243217bSJosh Lehan std::chrono::duration<double>(timeoutSecs))), 33b429f31dSEd Tanous writePerishable(timeoutSecs > 0.0) 342a40e939SJosh Lehan { 352a40e939SJosh Lehan // The caller must specify what physical characteristic 362a40e939SJosh Lehan // an external sensor is expected to be measuring, such as temperature, 372a40e939SJosh Lehan // as, unlike others, this is not implied by device type name. 386cb732a3SEd Tanous std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits); 392a40e939SJosh Lehan if (dbusPath.empty()) 402a40e939SJosh Lehan { 412a40e939SJosh Lehan throw std::runtime_error("Units not in allow list"); 422a40e939SJosh Lehan } 436cb732a3SEd Tanous std::string objectPath = "/xyz/openbmc_project/sensors/"; 442a40e939SJosh Lehan objectPath += dbusPath; 452a40e939SJosh Lehan objectPath += '/'; 462a40e939SJosh Lehan objectPath += sensorName; 472a40e939SJosh Lehan 482a40e939SJosh Lehan sensorInterface = objectServer.add_interface( 492a40e939SJosh Lehan objectPath, "xyz.openbmc_project.Sensor.Value"); 502a40e939SJosh Lehan 515667808aSJayashree Dhanapal for (const auto& threshold : thresholds) 522a40e939SJosh Lehan { 535667808aSJayashree Dhanapal std::string interface = thresholds::getInterface(threshold.level); 545667808aSJayashree Dhanapal thresholdInterfaces[static_cast<size_t>(threshold.level)] = 555667808aSJayashree Dhanapal objectServer.add_interface(objectPath, interface); 562a40e939SJosh Lehan } 572a40e939SJosh Lehan 58779c96a2SPatrick Williams association = objectServer.add_interface(objectPath, 59779c96a2SPatrick Williams association::interface); 603928741aSAndrei Kartashev setInitialProperties(sensorUnits); 617243217bSJosh Lehan 627243217bSJosh Lehan if constexpr (debug) 637243217bSJosh Lehan { 647243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " constructed: path " 657243217bSJosh Lehan << configurationPath << ", type " << objectType << ", min " 667243217bSJosh Lehan << minReading << ", max " << maxReading << ", timeout " 677243217bSJosh Lehan << std::chrono::duration_cast<std::chrono::microseconds>( 687243217bSJosh Lehan writeTimeout) 697243217bSJosh Lehan .count() 707243217bSJosh Lehan << " us\n"; 717243217bSJosh Lehan } 722a40e939SJosh Lehan } 732a40e939SJosh Lehan 740362738dSJosh Lehan // Separate function from constructor, because of a gotcha: can't use the 750362738dSJosh Lehan // enable_shared_from_this() API until after the constructor has completed. 760362738dSJosh Lehan void ExternalSensor::initWriteHook( 770362738dSJosh Lehan std::function<void(std::chrono::steady_clock::time_point now)>&& 780362738dSJosh Lehan writeHookIn) 790362738dSJosh Lehan { 800362738dSJosh Lehan // Connect ExternalSensorMain with ExternalSensor 810362738dSJosh Lehan writeHook = std::move(writeHookIn); 820362738dSJosh Lehan 830362738dSJosh Lehan // Connect ExternalSensor with Sensor 840362738dSJosh Lehan auto weakThis = weak_from_this(); 858a17c303SEd Tanous externalSetHook = [weakThis]() { 860362738dSJosh Lehan auto lockThis = weakThis.lock(); 870362738dSJosh Lehan if (lockThis) 880362738dSJosh Lehan { 890362738dSJosh Lehan lockThis->externalSetTrigger(); 900362738dSJosh Lehan return; 910362738dSJosh Lehan } 920362738dSJosh Lehan if constexpr (debug) 930362738dSJosh Lehan { 940362738dSJosh Lehan std::cerr << "ExternalSensor receive ignored, sensor gone\n"; 950362738dSJosh Lehan } 968a17c303SEd Tanous }; 970362738dSJosh Lehan } 980362738dSJosh Lehan 992a40e939SJosh Lehan ExternalSensor::~ExternalSensor() 1002a40e939SJosh Lehan { 1017243217bSJosh Lehan // Make sure the write hook does not reference this object anymore 1027243217bSJosh Lehan externalSetHook = nullptr; 1037243217bSJosh Lehan 1042a40e939SJosh Lehan objServer.remove_interface(association); 1055667808aSJayashree Dhanapal for (const auto& iface : thresholdInterfaces) 1065667808aSJayashree Dhanapal { 1075667808aSJayashree Dhanapal objServer.remove_interface(iface); 1085667808aSJayashree Dhanapal } 1092a40e939SJosh Lehan objServer.remove_interface(sensorInterface); 1107243217bSJosh Lehan 1117243217bSJosh Lehan if constexpr (debug) 1127243217bSJosh Lehan { 1137243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " destructed\n"; 1147243217bSJosh Lehan } 1152a40e939SJosh Lehan } 1162a40e939SJosh Lehan 117*201a1015SEd Tanous void ExternalSensor::checkThresholds() 1182a40e939SJosh Lehan { 1192a40e939SJosh Lehan thresholds::checkThresholds(this); 1202a40e939SJosh Lehan } 1217243217bSJosh Lehan 122*201a1015SEd Tanous bool ExternalSensor::isAliveAndPerishable() const 1237243217bSJosh Lehan { 1247243217bSJosh Lehan return (writeAlive && writePerishable); 1257243217bSJosh Lehan } 1267243217bSJosh Lehan 1277243217bSJosh Lehan bool ExternalSensor::isAliveAndFresh( 1287243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1297243217bSJosh Lehan { 1307243217bSJosh Lehan // Must be alive and perishable, to have possibility of being fresh 1317243217bSJosh Lehan if (!isAliveAndPerishable()) 1327243217bSJosh Lehan { 1337243217bSJosh Lehan return false; 1347243217bSJosh Lehan } 1357243217bSJosh Lehan 1367243217bSJosh Lehan // If age, as of now, is less than timeout, it is deemed fresh 13792b9629aSAndrew Jeffery // NOLINTNEXTLINE 1387243217bSJosh Lehan return (ageElapsed(now) < writeTimeout); 1397243217bSJosh Lehan } 1407243217bSJosh Lehan 1417243217bSJosh Lehan void ExternalSensor::writeBegin( 1427243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) 1437243217bSJosh Lehan { 1447243217bSJosh Lehan if (!writeAlive) 1457243217bSJosh Lehan { 1467243217bSJosh Lehan std::cerr << "ExternalSensor " << name 1477243217bSJosh Lehan << " online, receiving first value " << value << "\n"; 1487243217bSJosh Lehan } 1497243217bSJosh Lehan 1507243217bSJosh Lehan writeLast = now; 1517243217bSJosh Lehan writeAlive = true; 1527243217bSJosh Lehan } 1537243217bSJosh Lehan 154*201a1015SEd Tanous void ExternalSensor::writeInvalidate() 1557243217bSJosh Lehan { 1567243217bSJosh Lehan writeAlive = false; 1577243217bSJosh Lehan 1587243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " offline, timed out\n"; 1597243217bSJosh Lehan 1607243217bSJosh Lehan // Take back control of this sensor from the external override, 1617243217bSJosh Lehan // as the external source has timed out. 1627243217bSJosh Lehan // This allows sensor::updateValue() to work normally, 1637243217bSJosh Lehan // as it would do for internal sensors with values from hardware. 1647243217bSJosh Lehan overriddenState = false; 1657243217bSJosh Lehan 1667243217bSJosh Lehan // Invalidate the existing Value, similar to what internal sensors do, 1677243217bSJosh Lehan // when they encounter errors trying to read from hardware. 1687243217bSJosh Lehan updateValue(std::numeric_limits<double>::quiet_NaN()); 1697243217bSJosh Lehan } 1707243217bSJosh Lehan 1717243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageElapsed( 1727243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1737243217bSJosh Lehan { 1747243217bSJosh Lehan // Comparing 2 time_point will return duration 1757243217bSJosh Lehan return (now - writeLast); 1767243217bSJosh Lehan } 1777243217bSJosh Lehan 1787243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageRemaining( 1797243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1807243217bSJosh Lehan { 1817243217bSJosh Lehan // Comparing duration will return another duration 1827243217bSJosh Lehan return (writeTimeout - ageElapsed(now)); 1837243217bSJosh Lehan } 1847243217bSJosh Lehan 185*201a1015SEd Tanous void ExternalSensor::externalSetTrigger() 1867243217bSJosh Lehan { 1877243217bSJosh Lehan if constexpr (debug) 1887243217bSJosh Lehan { 1897243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " received " << value << "\n"; 1907243217bSJosh Lehan } 1917243217bSJosh Lehan 1927243217bSJosh Lehan auto now = std::chrono::steady_clock::now(); 1937243217bSJosh Lehan 1947243217bSJosh Lehan writeBegin(now); 1957243217bSJosh Lehan 1967243217bSJosh Lehan // Tell the owner to recalculate the expiration timer 1977243217bSJosh Lehan writeHook(now); 1987243217bSJosh Lehan } 199