12a40e939SJosh Lehan #include "ExternalSensor.hpp" 22a40e939SJosh Lehan 32a40e939SJosh Lehan #include "SensorPaths.hpp" 42a40e939SJosh Lehan 52a40e939SJosh Lehan #include <unistd.h> 62a40e939SJosh Lehan 72a40e939SJosh Lehan #include <boost/algorithm/string/predicate.hpp> 82a40e939SJosh Lehan #include <boost/algorithm/string/replace.hpp> 92a40e939SJosh Lehan #include <boost/date_time/posix_time/posix_time.hpp> 102a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp> 112a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp> 122a40e939SJosh Lehan 137243217bSJosh Lehan #include <chrono> 142a40e939SJosh Lehan #include <iostream> 152a40e939SJosh Lehan #include <istream> 162a40e939SJosh Lehan #include <limits> 172a40e939SJosh Lehan #include <memory> 182a40e939SJosh Lehan #include <string> 192a40e939SJosh Lehan #include <vector> 202a40e939SJosh Lehan 217243217bSJosh Lehan static constexpr bool debug = false; 227243217bSJosh Lehan 232a40e939SJosh Lehan ExternalSensor::ExternalSensor( 242a40e939SJosh Lehan const std::string& objectType, sdbusplus::asio::object_server& objectServer, 252a40e939SJosh Lehan std::shared_ptr<sdbusplus::asio::connection>& conn, 262a40e939SJosh Lehan const std::string& sensorName, const std::string& sensorUnits, 277b7a9deaSJeff Lin std::vector<thresholds::Threshold>&& thresholdsIn, 287243217bSJosh Lehan const std::string& sensorConfiguration, double maxReading, 290362738dSJosh Lehan double minReading, double timeoutSecs, const PowerState& powerState) : 302a40e939SJosh Lehan // TODO(): When the Mutable feature is integrated, 312a40e939SJosh Lehan // make sure all ExternalSensor instances are mutable, 322a40e939SJosh Lehan // because that is the entire point of ExternalSensor, 332a40e939SJosh Lehan // to accept sensor values written by an external source. 347b7a9deaSJeff Lin Sensor(boost::replace_all_copy(sensorName, " ", "_"), 35*1263c3daSBruce Lee std::move(thresholdsIn), sensorConfiguration, objectType, true, 36*1263c3daSBruce Lee maxReading, minReading, conn, powerState), 377243217bSJosh Lehan std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer), 387243217bSJosh Lehan writeLast(std::chrono::steady_clock::now()), 397243217bSJosh Lehan writeTimeout( 407243217bSJosh Lehan std::chrono::duration_cast<std::chrono::steady_clock::duration>( 417243217bSJosh Lehan std::chrono::duration<double>(timeoutSecs))), 420362738dSJosh Lehan writeAlive(false), writePerishable(timeoutSecs > 0.0) 432a40e939SJosh Lehan { 442a40e939SJosh Lehan // The caller must specify what physical characteristic 452a40e939SJosh Lehan // an external sensor is expected to be measuring, such as temperature, 462a40e939SJosh Lehan // as, unlike others, this is not implied by device type name. 476cb732a3SEd Tanous std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits); 482a40e939SJosh Lehan if (dbusPath.empty()) 492a40e939SJosh Lehan { 502a40e939SJosh Lehan throw std::runtime_error("Units not in allow list"); 512a40e939SJosh Lehan } 526cb732a3SEd Tanous std::string objectPath = "/xyz/openbmc_project/sensors/"; 532a40e939SJosh Lehan objectPath += dbusPath; 542a40e939SJosh Lehan objectPath += '/'; 552a40e939SJosh Lehan objectPath += sensorName; 562a40e939SJosh Lehan 572a40e939SJosh Lehan sensorInterface = objectServer.add_interface( 582a40e939SJosh Lehan objectPath, "xyz.openbmc_project.Sensor.Value"); 592a40e939SJosh Lehan 602a40e939SJosh Lehan if (thresholds::hasWarningInterface(thresholds)) 612a40e939SJosh Lehan { 622a40e939SJosh Lehan thresholdInterfaceWarning = objectServer.add_interface( 632a40e939SJosh Lehan objectPath, "xyz.openbmc_project.Sensor.Threshold.Warning"); 642a40e939SJosh Lehan } 652a40e939SJosh Lehan if (thresholds::hasCriticalInterface(thresholds)) 662a40e939SJosh Lehan { 672a40e939SJosh Lehan thresholdInterfaceCritical = objectServer.add_interface( 682a40e939SJosh Lehan objectPath, "xyz.openbmc_project.Sensor.Threshold.Critical"); 692a40e939SJosh Lehan } 702a40e939SJosh Lehan 712a40e939SJosh Lehan association = 722a40e939SJosh Lehan objectServer.add_interface(objectPath, association::interface); 736b6891c5SZev Weiss setInitialProperties(conn, sensorUnits); 747243217bSJosh Lehan 757243217bSJosh Lehan if constexpr (debug) 767243217bSJosh Lehan { 777243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " constructed: path " 787243217bSJosh Lehan << configurationPath << ", type " << objectType << ", min " 797243217bSJosh Lehan << minReading << ", max " << maxReading << ", timeout " 807243217bSJosh Lehan << std::chrono::duration_cast<std::chrono::microseconds>( 817243217bSJosh Lehan writeTimeout) 827243217bSJosh Lehan .count() 837243217bSJosh Lehan << " us\n"; 847243217bSJosh Lehan } 852a40e939SJosh Lehan } 862a40e939SJosh Lehan 870362738dSJosh Lehan // Separate function from constructor, because of a gotcha: can't use the 880362738dSJosh Lehan // enable_shared_from_this() API until after the constructor has completed. 890362738dSJosh Lehan void ExternalSensor::initWriteHook( 900362738dSJosh Lehan std::function<void(std::chrono::steady_clock::time_point now)>&& 910362738dSJosh Lehan writeHookIn) 920362738dSJosh Lehan { 930362738dSJosh Lehan // Connect ExternalSensorMain with ExternalSensor 940362738dSJosh Lehan writeHook = std::move(writeHookIn); 950362738dSJosh Lehan 960362738dSJosh Lehan // Connect ExternalSensor with Sensor 970362738dSJosh Lehan auto weakThis = weak_from_this(); 980362738dSJosh Lehan externalSetHook = std::move([weakThis]() { 990362738dSJosh Lehan auto lockThis = weakThis.lock(); 1000362738dSJosh Lehan if (lockThis) 1010362738dSJosh Lehan { 1020362738dSJosh Lehan lockThis->externalSetTrigger(); 1030362738dSJosh Lehan return; 1040362738dSJosh Lehan } 1050362738dSJosh Lehan if constexpr (debug) 1060362738dSJosh Lehan { 1070362738dSJosh Lehan std::cerr << "ExternalSensor receive ignored, sensor gone\n"; 1080362738dSJosh Lehan } 1090362738dSJosh Lehan }); 1100362738dSJosh Lehan } 1110362738dSJosh Lehan 1122a40e939SJosh Lehan ExternalSensor::~ExternalSensor() 1132a40e939SJosh Lehan { 1147243217bSJosh Lehan // Make sure the write hook does not reference this object anymore 1157243217bSJosh Lehan externalSetHook = nullptr; 1167243217bSJosh Lehan 1172a40e939SJosh Lehan objServer.remove_interface(association); 1182a40e939SJosh Lehan objServer.remove_interface(thresholdInterfaceCritical); 1192a40e939SJosh Lehan objServer.remove_interface(thresholdInterfaceWarning); 1202a40e939SJosh Lehan objServer.remove_interface(sensorInterface); 1217243217bSJosh Lehan 1227243217bSJosh Lehan if constexpr (debug) 1237243217bSJosh Lehan { 1247243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " destructed\n"; 1257243217bSJosh Lehan } 1262a40e939SJosh Lehan } 1272a40e939SJosh Lehan 1282a40e939SJosh Lehan void ExternalSensor::checkThresholds(void) 1292a40e939SJosh Lehan { 1302a40e939SJosh Lehan thresholds::checkThresholds(this); 1312a40e939SJosh Lehan } 1327243217bSJosh Lehan 1337243217bSJosh Lehan bool ExternalSensor::isAliveAndPerishable(void) const 1347243217bSJosh Lehan { 1357243217bSJosh Lehan return (writeAlive && writePerishable); 1367243217bSJosh Lehan } 1377243217bSJosh Lehan 1387243217bSJosh Lehan bool ExternalSensor::isAliveAndFresh( 1397243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1407243217bSJosh Lehan { 1417243217bSJosh Lehan // Must be alive and perishable, to have possibility of being fresh 1427243217bSJosh Lehan if (!isAliveAndPerishable()) 1437243217bSJosh Lehan { 1447243217bSJosh Lehan return false; 1457243217bSJosh Lehan } 1467243217bSJosh Lehan 1477243217bSJosh Lehan // If age, as of now, is less than timeout, it is deemed fresh 14892b9629aSAndrew Jeffery // NOLINTNEXTLINE 1497243217bSJosh Lehan return (ageElapsed(now) < writeTimeout); 1507243217bSJosh Lehan } 1517243217bSJosh Lehan 1527243217bSJosh Lehan void ExternalSensor::writeBegin( 1537243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) 1547243217bSJosh Lehan { 1557243217bSJosh Lehan if (!writeAlive) 1567243217bSJosh Lehan { 1577243217bSJosh Lehan std::cerr << "ExternalSensor " << name 1587243217bSJosh Lehan << " online, receiving first value " << value << "\n"; 1597243217bSJosh Lehan } 1607243217bSJosh Lehan 1617243217bSJosh Lehan writeLast = now; 1627243217bSJosh Lehan writeAlive = true; 1637243217bSJosh Lehan } 1647243217bSJosh Lehan 1657243217bSJosh Lehan void ExternalSensor::writeInvalidate(void) 1667243217bSJosh Lehan { 1677243217bSJosh Lehan writeAlive = false; 1687243217bSJosh Lehan 1697243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " offline, timed out\n"; 1707243217bSJosh Lehan 1717243217bSJosh Lehan // Take back control of this sensor from the external override, 1727243217bSJosh Lehan // as the external source has timed out. 1737243217bSJosh Lehan // This allows sensor::updateValue() to work normally, 1747243217bSJosh Lehan // as it would do for internal sensors with values from hardware. 1757243217bSJosh Lehan overriddenState = false; 1767243217bSJosh Lehan 1777243217bSJosh Lehan // Invalidate the existing Value, similar to what internal sensors do, 1787243217bSJosh Lehan // when they encounter errors trying to read from hardware. 1797243217bSJosh Lehan updateValue(std::numeric_limits<double>::quiet_NaN()); 1807243217bSJosh Lehan } 1817243217bSJosh Lehan 1827243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageElapsed( 1837243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1847243217bSJosh Lehan { 1857243217bSJosh Lehan // Comparing 2 time_point will return duration 1867243217bSJosh Lehan return (now - writeLast); 1877243217bSJosh Lehan } 1887243217bSJosh Lehan 1897243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageRemaining( 1907243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1917243217bSJosh Lehan { 1927243217bSJosh Lehan // Comparing duration will return another duration 1937243217bSJosh Lehan return (writeTimeout - ageElapsed(now)); 1947243217bSJosh Lehan } 1957243217bSJosh Lehan 1967243217bSJosh Lehan void ExternalSensor::externalSetTrigger(void) 1977243217bSJosh Lehan { 1987243217bSJosh Lehan if constexpr (debug) 1997243217bSJosh Lehan { 2007243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " received " << value << "\n"; 2017243217bSJosh Lehan } 2027243217bSJosh Lehan 2037243217bSJosh Lehan auto now = std::chrono::steady_clock::now(); 2047243217bSJosh Lehan 2057243217bSJosh Lehan writeBegin(now); 2067243217bSJosh Lehan 2077243217bSJosh Lehan // Tell the owner to recalculate the expiration timer 2087243217bSJosh Lehan writeHook(now); 2097243217bSJosh Lehan } 210