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