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), 31*2049bd26SEd Tanous objServer(objectServer), writeLast(std::chrono::steady_clock::now()), 327243217bSJosh Lehan writeTimeout( 337243217bSJosh Lehan std::chrono::duration_cast<std::chrono::steady_clock::duration>( 347243217bSJosh Lehan std::chrono::duration<double>(timeoutSecs))), 35b429f31dSEd Tanous writePerishable(timeoutSecs > 0.0) 362a40e939SJosh Lehan { 372a40e939SJosh Lehan // The caller must specify what physical characteristic 382a40e939SJosh Lehan // an external sensor is expected to be measuring, such as temperature, 392a40e939SJosh Lehan // as, unlike others, this is not implied by device type name. 406cb732a3SEd Tanous std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits); 412a40e939SJosh Lehan if (dbusPath.empty()) 422a40e939SJosh Lehan { 432a40e939SJosh Lehan throw std::runtime_error("Units not in allow list"); 442a40e939SJosh Lehan } 456cb732a3SEd Tanous std::string objectPath = "/xyz/openbmc_project/sensors/"; 462a40e939SJosh Lehan objectPath += dbusPath; 472a40e939SJosh Lehan objectPath += '/'; 482a40e939SJosh Lehan objectPath += sensorName; 492a40e939SJosh Lehan 502a40e939SJosh Lehan sensorInterface = objectServer.add_interface( 512a40e939SJosh Lehan objectPath, "xyz.openbmc_project.Sensor.Value"); 522a40e939SJosh Lehan 535667808aSJayashree Dhanapal for (const auto& threshold : thresholds) 542a40e939SJosh Lehan { 555667808aSJayashree Dhanapal std::string interface = thresholds::getInterface(threshold.level); 565667808aSJayashree Dhanapal thresholdInterfaces[static_cast<size_t>(threshold.level)] = 575667808aSJayashree Dhanapal objectServer.add_interface(objectPath, interface); 582a40e939SJosh Lehan } 592a40e939SJosh Lehan 602a40e939SJosh Lehan association = 612a40e939SJosh Lehan objectServer.add_interface(objectPath, association::interface); 623928741aSAndrei Kartashev setInitialProperties(sensorUnits); 637243217bSJosh Lehan 647243217bSJosh Lehan if constexpr (debug) 657243217bSJosh Lehan { 667243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " constructed: path " 677243217bSJosh Lehan << configurationPath << ", type " << objectType << ", min " 687243217bSJosh Lehan << minReading << ", max " << maxReading << ", timeout " 697243217bSJosh Lehan << std::chrono::duration_cast<std::chrono::microseconds>( 707243217bSJosh Lehan writeTimeout) 717243217bSJosh Lehan .count() 727243217bSJosh Lehan << " us\n"; 737243217bSJosh Lehan } 742a40e939SJosh Lehan } 752a40e939SJosh Lehan 760362738dSJosh Lehan // Separate function from constructor, because of a gotcha: can't use the 770362738dSJosh Lehan // enable_shared_from_this() API until after the constructor has completed. 780362738dSJosh Lehan void ExternalSensor::initWriteHook( 790362738dSJosh Lehan std::function<void(std::chrono::steady_clock::time_point now)>&& 800362738dSJosh Lehan writeHookIn) 810362738dSJosh Lehan { 820362738dSJosh Lehan // Connect ExternalSensorMain with ExternalSensor 830362738dSJosh Lehan writeHook = std::move(writeHookIn); 840362738dSJosh Lehan 850362738dSJosh Lehan // Connect ExternalSensor with Sensor 860362738dSJosh Lehan auto weakThis = weak_from_this(); 878a17c303SEd Tanous externalSetHook = [weakThis]() { 880362738dSJosh Lehan auto lockThis = weakThis.lock(); 890362738dSJosh Lehan if (lockThis) 900362738dSJosh Lehan { 910362738dSJosh Lehan lockThis->externalSetTrigger(); 920362738dSJosh Lehan return; 930362738dSJosh Lehan } 940362738dSJosh Lehan if constexpr (debug) 950362738dSJosh Lehan { 960362738dSJosh Lehan std::cerr << "ExternalSensor receive ignored, sensor gone\n"; 970362738dSJosh Lehan } 988a17c303SEd Tanous }; 990362738dSJosh Lehan } 1000362738dSJosh Lehan 1012a40e939SJosh Lehan ExternalSensor::~ExternalSensor() 1022a40e939SJosh Lehan { 1037243217bSJosh Lehan // Make sure the write hook does not reference this object anymore 1047243217bSJosh Lehan externalSetHook = nullptr; 1057243217bSJosh Lehan 1062a40e939SJosh Lehan objServer.remove_interface(association); 1075667808aSJayashree Dhanapal for (const auto& iface : thresholdInterfaces) 1085667808aSJayashree Dhanapal { 1095667808aSJayashree Dhanapal objServer.remove_interface(iface); 1105667808aSJayashree Dhanapal } 1112a40e939SJosh Lehan objServer.remove_interface(sensorInterface); 1127243217bSJosh Lehan 1137243217bSJosh Lehan if constexpr (debug) 1147243217bSJosh Lehan { 1157243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " destructed\n"; 1167243217bSJosh Lehan } 1172a40e939SJosh Lehan } 1182a40e939SJosh Lehan 1192a40e939SJosh Lehan void ExternalSensor::checkThresholds(void) 1202a40e939SJosh Lehan { 1212a40e939SJosh Lehan thresholds::checkThresholds(this); 1222a40e939SJosh Lehan } 1237243217bSJosh Lehan 1247243217bSJosh Lehan bool ExternalSensor::isAliveAndPerishable(void) const 1257243217bSJosh Lehan { 1267243217bSJosh Lehan return (writeAlive && writePerishable); 1277243217bSJosh Lehan } 1287243217bSJosh Lehan 1297243217bSJosh Lehan bool ExternalSensor::isAliveAndFresh( 1307243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1317243217bSJosh Lehan { 1327243217bSJosh Lehan // Must be alive and perishable, to have possibility of being fresh 1337243217bSJosh Lehan if (!isAliveAndPerishable()) 1347243217bSJosh Lehan { 1357243217bSJosh Lehan return false; 1367243217bSJosh Lehan } 1377243217bSJosh Lehan 1387243217bSJosh Lehan // If age, as of now, is less than timeout, it is deemed fresh 13992b9629aSAndrew Jeffery // NOLINTNEXTLINE 1407243217bSJosh Lehan return (ageElapsed(now) < writeTimeout); 1417243217bSJosh Lehan } 1427243217bSJosh Lehan 1437243217bSJosh Lehan void ExternalSensor::writeBegin( 1447243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) 1457243217bSJosh Lehan { 1467243217bSJosh Lehan if (!writeAlive) 1477243217bSJosh Lehan { 1487243217bSJosh Lehan std::cerr << "ExternalSensor " << name 1497243217bSJosh Lehan << " online, receiving first value " << value << "\n"; 1507243217bSJosh Lehan } 1517243217bSJosh Lehan 1527243217bSJosh Lehan writeLast = now; 1537243217bSJosh Lehan writeAlive = true; 1547243217bSJosh Lehan } 1557243217bSJosh Lehan 1567243217bSJosh Lehan void ExternalSensor::writeInvalidate(void) 1577243217bSJosh Lehan { 1587243217bSJosh Lehan writeAlive = false; 1597243217bSJosh Lehan 1607243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " offline, timed out\n"; 1617243217bSJosh Lehan 1627243217bSJosh Lehan // Take back control of this sensor from the external override, 1637243217bSJosh Lehan // as the external source has timed out. 1647243217bSJosh Lehan // This allows sensor::updateValue() to work normally, 1657243217bSJosh Lehan // as it would do for internal sensors with values from hardware. 1667243217bSJosh Lehan overriddenState = false; 1677243217bSJosh Lehan 1687243217bSJosh Lehan // Invalidate the existing Value, similar to what internal sensors do, 1697243217bSJosh Lehan // when they encounter errors trying to read from hardware. 1707243217bSJosh Lehan updateValue(std::numeric_limits<double>::quiet_NaN()); 1717243217bSJosh Lehan } 1727243217bSJosh Lehan 1737243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageElapsed( 1747243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1757243217bSJosh Lehan { 1767243217bSJosh Lehan // Comparing 2 time_point will return duration 1777243217bSJosh Lehan return (now - writeLast); 1787243217bSJosh Lehan } 1797243217bSJosh Lehan 1807243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageRemaining( 1817243217bSJosh Lehan const std::chrono::steady_clock::time_point& now) const 1827243217bSJosh Lehan { 1837243217bSJosh Lehan // Comparing duration will return another duration 1847243217bSJosh Lehan return (writeTimeout - ageElapsed(now)); 1857243217bSJosh Lehan } 1867243217bSJosh Lehan 1877243217bSJosh Lehan void ExternalSensor::externalSetTrigger(void) 1887243217bSJosh Lehan { 1897243217bSJosh Lehan if constexpr (debug) 1907243217bSJosh Lehan { 1917243217bSJosh Lehan std::cerr << "ExternalSensor " << name << " received " << value << "\n"; 1927243217bSJosh Lehan } 1937243217bSJosh Lehan 1947243217bSJosh Lehan auto now = std::chrono::steady_clock::now(); 1957243217bSJosh Lehan 1967243217bSJosh Lehan writeBegin(now); 1977243217bSJosh Lehan 1987243217bSJosh Lehan // Tell the owner to recalculate the expiration timer 1997243217bSJosh Lehan writeHook(now); 2007243217bSJosh Lehan } 201