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/date_time/posix_time/posix_time.hpp> 92a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp> 102a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp> 112a40e939SJosh Lehan 127243217bSJosh Lehan #include <chrono> 132a40e939SJosh Lehan #include <iostream> 142a40e939SJosh Lehan #include <istream> 152a40e939SJosh Lehan #include <limits> 162a40e939SJosh Lehan #include <memory> 172a40e939SJosh Lehan #include <string> 182a40e939SJosh Lehan #include <vector> 192a40e939SJosh Lehan 207243217bSJosh Lehan static constexpr bool debug = false; 217243217bSJosh Lehan 222a40e939SJosh Lehan ExternalSensor::ExternalSensor( 232a40e939SJosh Lehan const std::string& objectType, sdbusplus::asio::object_server& objectServer, 242a40e939SJosh Lehan std::shared_ptr<sdbusplus::asio::connection>& conn, 252a40e939SJosh Lehan const std::string& sensorName, const std::string& sensorUnits, 267b7a9deaSJeff Lin std::vector<thresholds::Threshold>&& thresholdsIn, 277243217bSJosh Lehan const std::string& sensorConfiguration, double maxReading, 280362738dSJosh Lehan double minReading, double timeoutSecs, const PowerState& powerState) : 29da98f095SZhikui Ren Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, 30da98f095SZhikui Ren objectType, true, true, maxReading, minReading, conn, powerState), 317243217bSJosh Lehan std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer), 327243217bSJosh Lehan writeLast(std::chrono::steady_clock::now()), 337243217bSJosh Lehan writeTimeout( 347243217bSJosh Lehan std::chrono::duration_cast<std::chrono::steady_clock::duration>( 357243217bSJosh Lehan std::chrono::duration<double>(timeoutSecs))), 36*b429f31dSEd Tanous writePerishable(timeoutSecs > 0.0) 372a40e939SJosh Lehan { 382a40e939SJosh Lehan // The caller must specify what physical characteristic 392a40e939SJosh Lehan // an external sensor is expected to be measuring, such as temperature, 402a40e939SJosh Lehan // as, unlike others, this is not implied by device type name. 416cb732a3SEd Tanous std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits); 422a40e939SJosh Lehan if (dbusPath.empty()) 432a40e939SJosh Lehan { 442a40e939SJosh Lehan throw std::runtime_error("Units not in allow list"); 452a40e939SJosh Lehan } 466cb732a3SEd Tanous std::string objectPath = "/xyz/openbmc_project/sensors/"; 472a40e939SJosh Lehan objectPath += dbusPath; 482a40e939SJosh Lehan objectPath += '/'; 492a40e939SJosh Lehan objectPath += sensorName; 502a40e939SJosh Lehan 512a40e939SJosh Lehan sensorInterface = objectServer.add_interface( 522a40e939SJosh Lehan objectPath, "xyz.openbmc_project.Sensor.Value"); 532a40e939SJosh Lehan 545667808aSJayashree Dhanapal for (const auto& threshold : thresholds) 552a40e939SJosh Lehan { 565667808aSJayashree Dhanapal std::string interface = thresholds::getInterface(threshold.level); 575667808aSJayashree Dhanapal thresholdInterfaces[static_cast<size_t>(threshold.level)] = 585667808aSJayashree Dhanapal objectServer.add_interface(objectPath, interface); 592a40e939SJosh Lehan } 602a40e939SJosh Lehan 612a40e939SJosh Lehan association = 622a40e939SJosh Lehan objectServer.add_interface(objectPath, association::interface); 633928741aSAndrei Kartashev setInitialProperties(sensorUnits); 647243217bSJosh Lehan 657243217bSJosh Lehan if constexpr (debug) 667243217bSJosh Lehan { 677243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " constructed: path " 687243217bSJosh Lehan << configurationPath << ", type " << objectType << ", min " 697243217bSJosh Lehan << minReading << ", max " << maxReading << ", timeout " 707243217bSJosh Lehan << std::chrono::duration_cast<std::chrono::microseconds>( 717243217bSJosh Lehan writeTimeout) 727243217bSJosh Lehan .count() 737243217bSJosh Lehan << " us\n"; 747243217bSJosh Lehan } 752a40e939SJosh Lehan } 762a40e939SJosh Lehan 770362738dSJosh Lehan // Separate function from constructor, because of a gotcha: can't use the 780362738dSJosh Lehan // enable_shared_from_this() API until after the constructor has completed. 790362738dSJosh Lehan void ExternalSensor::initWriteHook( 800362738dSJosh Lehan std::function<void(std::chrono::steady_clock::time_point now)>&& 810362738dSJosh Lehan writeHookIn) 820362738dSJosh Lehan { 830362738dSJosh Lehan // Connect ExternalSensorMain with ExternalSensor 840362738dSJosh Lehan writeHook = std::move(writeHookIn); 850362738dSJosh Lehan 860362738dSJosh Lehan // Connect ExternalSensor with Sensor 870362738dSJosh Lehan auto weakThis = weak_from_this(); 888a17c303SEd Tanous externalSetHook = [weakThis]() { 890362738dSJosh Lehan auto lockThis = weakThis.lock(); 900362738dSJosh Lehan if (lockThis) 910362738dSJosh Lehan { 920362738dSJosh Lehan lockThis->externalSetTrigger(); 930362738dSJosh Lehan return; 940362738dSJosh Lehan } 950362738dSJosh Lehan if constexpr (debug) 960362738dSJosh Lehan { 970362738dSJosh Lehan std::cerr << "ExternalSensor receive ignored, sensor gone\n"; 980362738dSJosh Lehan } 998a17c303SEd Tanous }; 1000362738dSJosh Lehan } 1010362738dSJosh Lehan 1022a40e939SJosh Lehan ExternalSensor::~ExternalSensor() 1032a40e939SJosh Lehan { 1047243217bSJosh Lehan // Make sure the write hook does not reference this object anymore 1057243217bSJosh Lehan externalSetHook = nullptr; 1067243217bSJosh Lehan 1072a40e939SJosh Lehan objServer.remove_interface(association); 1085667808aSJayashree Dhanapal for (const auto& iface : thresholdInterfaces) 1095667808aSJayashree Dhanapal { 1105667808aSJayashree Dhanapal objServer.remove_interface(iface); 1115667808aSJayashree Dhanapal } 1122a40e939SJosh Lehan objServer.remove_interface(sensorInterface); 1137243217bSJosh Lehan 1147243217bSJosh Lehan if constexpr (debug) 1157243217bSJosh Lehan { 1167243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " destructed\n"; 1177243217bSJosh Lehan } 1182a40e939SJosh Lehan } 1192a40e939SJosh Lehan 1202a40e939SJosh Lehan void ExternalSensor::checkThresholds(void) 1212a40e939SJosh Lehan { 1222a40e939SJosh Lehan thresholds::checkThresholds(this); 1232a40e939SJosh Lehan } 1247243217bSJosh Lehan 1257243217bSJosh Lehan bool ExternalSensor::isAliveAndPerishable(void) const 1267243217bSJosh Lehan { 1277243217bSJosh Lehan return (writeAlive && writePerishable); 1287243217bSJosh Lehan } 1297243217bSJosh Lehan 1307243217bSJosh Lehan bool ExternalSensor::isAliveAndFresh( 1317243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1327243217bSJosh Lehan { 1337243217bSJosh Lehan // Must be alive and perishable, to have possibility of being fresh 1347243217bSJosh Lehan if (!isAliveAndPerishable()) 1357243217bSJosh Lehan { 1367243217bSJosh Lehan return false; 1377243217bSJosh Lehan } 1387243217bSJosh Lehan 1397243217bSJosh Lehan // If age, as of now, is less than timeout, it is deemed fresh 14092b9629aSAndrew Jeffery // NOLINTNEXTLINE 1417243217bSJosh Lehan return (ageElapsed(now) < writeTimeout); 1427243217bSJosh Lehan } 1437243217bSJosh Lehan 1447243217bSJosh Lehan void ExternalSensor::writeBegin( 1457243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) 1467243217bSJosh Lehan { 1477243217bSJosh Lehan if (!writeAlive) 1487243217bSJosh Lehan { 1497243217bSJosh Lehan std::cerr << "ExternalSensor " << name 1507243217bSJosh Lehan << " online, receiving first value " << value << "\n"; 1517243217bSJosh Lehan } 1527243217bSJosh Lehan 1537243217bSJosh Lehan writeLast = now; 1547243217bSJosh Lehan writeAlive = true; 1557243217bSJosh Lehan } 1567243217bSJosh Lehan 1577243217bSJosh Lehan void ExternalSensor::writeInvalidate(void) 1587243217bSJosh Lehan { 1597243217bSJosh Lehan writeAlive = false; 1607243217bSJosh Lehan 1617243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " offline, timed out\n"; 1627243217bSJosh Lehan 1637243217bSJosh Lehan // Take back control of this sensor from the external override, 1647243217bSJosh Lehan // as the external source has timed out. 1657243217bSJosh Lehan // This allows sensor::updateValue() to work normally, 1667243217bSJosh Lehan // as it would do for internal sensors with values from hardware. 1677243217bSJosh Lehan overriddenState = false; 1687243217bSJosh Lehan 1697243217bSJosh Lehan // Invalidate the existing Value, similar to what internal sensors do, 1707243217bSJosh Lehan // when they encounter errors trying to read from hardware. 1717243217bSJosh Lehan updateValue(std::numeric_limits<double>::quiet_NaN()); 1727243217bSJosh Lehan } 1737243217bSJosh Lehan 1747243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageElapsed( 1757243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1767243217bSJosh Lehan { 1777243217bSJosh Lehan // Comparing 2 time_point will return duration 1787243217bSJosh Lehan return (now - writeLast); 1797243217bSJosh Lehan } 1807243217bSJosh Lehan 1817243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageRemaining( 1827243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1837243217bSJosh Lehan { 1847243217bSJosh Lehan // Comparing duration will return another duration 1857243217bSJosh Lehan return (writeTimeout - ageElapsed(now)); 1867243217bSJosh Lehan } 1877243217bSJosh Lehan 1887243217bSJosh Lehan void ExternalSensor::externalSetTrigger(void) 1897243217bSJosh Lehan { 1907243217bSJosh Lehan if constexpr (debug) 1917243217bSJosh Lehan { 1927243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " received " << value << "\n"; 1937243217bSJosh Lehan } 1947243217bSJosh Lehan 1957243217bSJosh Lehan auto now = std::chrono::steady_clock::now(); 1967243217bSJosh Lehan 1977243217bSJosh Lehan writeBegin(now); 1987243217bSJosh Lehan 1997243217bSJosh Lehan // Tell the owner to recalculate the expiration timer 2007243217bSJosh Lehan writeHook(now); 2017243217bSJosh Lehan } 202