12a40e939SJosh Lehan #include "ExternalSensor.hpp"
22a40e939SJosh Lehan 
32a40e939SJosh Lehan #include "SensorPaths.hpp"
4*eacbfdd1SEd Tanous #include "Thresholds.hpp"
5*eacbfdd1SEd Tanous #include "Utils.hpp"
6*eacbfdd1SEd Tanous #include "sensor.hpp"
72a40e939SJosh Lehan 
82a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp>
92a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp>
102a40e939SJosh Lehan 
117243217bSJosh Lehan #include <chrono>
12*eacbfdd1SEd Tanous #include <cstddef>
13*eacbfdd1SEd Tanous #include <functional>
142a40e939SJosh Lehan #include <iostream>
152a40e939SJosh Lehan #include <limits>
162a40e939SJosh Lehan #include <memory>
17*eacbfdd1SEd Tanous #include <stdexcept>
182a40e939SJosh Lehan #include <string>
19*eacbfdd1SEd Tanous #include <utility>
202a40e939SJosh Lehan #include <vector>
212a40e939SJosh Lehan 
227243217bSJosh Lehan static constexpr bool debug = false;
237243217bSJosh Lehan 
ExternalSensor(const std::string & objectType,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & conn,const std::string & sensorName,const std::string & sensorUnits,std::vector<thresholds::Threshold> && thresholdsIn,const std::string & sensorConfiguration,double maxReading,double minReading,double timeoutSecs,const PowerState & powerState)242a40e939SJosh Lehan ExternalSensor::ExternalSensor(
252a40e939SJosh Lehan     const std::string& objectType, sdbusplus::asio::object_server& objectServer,
262a40e939SJosh Lehan     std::shared_ptr<sdbusplus::asio::connection>& conn,
272a40e939SJosh Lehan     const std::string& sensorName, const std::string& sensorUnits,
287b7a9deaSJeff Lin     std::vector<thresholds::Threshold>&& thresholdsIn,
297243217bSJosh Lehan     const std::string& sensorConfiguration, double maxReading,
300362738dSJosh Lehan     double minReading, double timeoutSecs, const PowerState& powerState) :
31da98f095SZhikui Ren     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
32da98f095SZhikui Ren            objectType, true, true, maxReading, minReading, conn, powerState),
332049bd26SEd Tanous     objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
347243217bSJosh Lehan     writeTimeout(
357243217bSJosh Lehan         std::chrono::duration_cast<std::chrono::steady_clock::duration>(
367243217bSJosh Lehan             std::chrono::duration<double>(timeoutSecs))),
37b429f31dSEd Tanous     writePerishable(timeoutSecs > 0.0)
382a40e939SJosh Lehan {
392a40e939SJosh Lehan     // The caller must specify what physical characteristic
402a40e939SJosh Lehan     // an external sensor is expected to be measuring, such as temperature,
412a40e939SJosh Lehan     // as, unlike others, this is not implied by device type name.
426cb732a3SEd Tanous     std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
432a40e939SJosh Lehan     if (dbusPath.empty())
442a40e939SJosh Lehan     {
452a40e939SJosh Lehan         throw std::runtime_error("Units not in allow list");
462a40e939SJosh Lehan     }
476cb732a3SEd Tanous     std::string objectPath = "/xyz/openbmc_project/sensors/";
482a40e939SJosh Lehan     objectPath += dbusPath;
492a40e939SJosh Lehan     objectPath += '/';
502a40e939SJosh Lehan     objectPath += sensorName;
512a40e939SJosh Lehan 
522a40e939SJosh Lehan     sensorInterface = objectServer.add_interface(
532a40e939SJosh Lehan         objectPath, "xyz.openbmc_project.Sensor.Value");
542a40e939SJosh Lehan 
555667808aSJayashree Dhanapal     for (const auto& threshold : thresholds)
562a40e939SJosh Lehan     {
575667808aSJayashree Dhanapal         std::string interface = thresholds::getInterface(threshold.level);
585667808aSJayashree Dhanapal         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
595667808aSJayashree Dhanapal             objectServer.add_interface(objectPath, interface);
602a40e939SJosh Lehan     }
612a40e939SJosh Lehan 
62779c96a2SPatrick Williams     association = objectServer.add_interface(objectPath,
63779c96a2SPatrick Williams                                              association::interface);
643928741aSAndrei Kartashev     setInitialProperties(sensorUnits);
657243217bSJosh Lehan 
667243217bSJosh Lehan     if constexpr (debug)
677243217bSJosh Lehan     {
687243217bSJosh Lehan         std::cerr << "ExternalSensor " << name << " constructed: path "
697243217bSJosh Lehan                   << configurationPath << ", type " << objectType << ", min "
707243217bSJosh Lehan                   << minReading << ", max " << maxReading << ", timeout "
717243217bSJosh Lehan                   << std::chrono::duration_cast<std::chrono::microseconds>(
727243217bSJosh Lehan                          writeTimeout)
737243217bSJosh Lehan                          .count()
747243217bSJosh Lehan                   << " us\n";
757243217bSJosh Lehan     }
762a40e939SJosh Lehan }
772a40e939SJosh Lehan 
780362738dSJosh Lehan // Separate function from constructor, because of a gotcha: can't use the
790362738dSJosh Lehan // enable_shared_from_this() API until after the constructor has completed.
initWriteHook(std::function<void (std::chrono::steady_clock::time_point now)> && writeHookIn)800362738dSJosh Lehan void ExternalSensor::initWriteHook(
810362738dSJosh Lehan     std::function<void(std::chrono::steady_clock::time_point now)>&&
820362738dSJosh Lehan         writeHookIn)
830362738dSJosh Lehan {
840362738dSJosh Lehan     // Connect ExternalSensorMain with ExternalSensor
850362738dSJosh Lehan     writeHook = std::move(writeHookIn);
860362738dSJosh Lehan 
870362738dSJosh Lehan     // Connect ExternalSensor with Sensor
880362738dSJosh Lehan     auto weakThis = weak_from_this();
898a17c303SEd Tanous     externalSetHook = [weakThis]() {
900362738dSJosh Lehan         auto lockThis = weakThis.lock();
910362738dSJosh Lehan         if (lockThis)
920362738dSJosh Lehan         {
930362738dSJosh Lehan             lockThis->externalSetTrigger();
940362738dSJosh Lehan             return;
950362738dSJosh Lehan         }
960362738dSJosh Lehan         if constexpr (debug)
970362738dSJosh Lehan         {
980362738dSJosh Lehan             std::cerr << "ExternalSensor receive ignored, sensor gone\n";
990362738dSJosh Lehan         }
1008a17c303SEd Tanous     };
1010362738dSJosh Lehan }
1020362738dSJosh Lehan 
~ExternalSensor()1032a40e939SJosh Lehan ExternalSensor::~ExternalSensor()
1042a40e939SJosh Lehan {
1057243217bSJosh Lehan     // Make sure the write hook does not reference this object anymore
1067243217bSJosh Lehan     externalSetHook = nullptr;
1077243217bSJosh Lehan 
1082a40e939SJosh Lehan     objServer.remove_interface(association);
1095667808aSJayashree Dhanapal     for (const auto& iface : thresholdInterfaces)
1105667808aSJayashree Dhanapal     {
1115667808aSJayashree Dhanapal         objServer.remove_interface(iface);
1125667808aSJayashree Dhanapal     }
1132a40e939SJosh Lehan     objServer.remove_interface(sensorInterface);
1147243217bSJosh Lehan 
1157243217bSJosh Lehan     if constexpr (debug)
1167243217bSJosh Lehan     {
1177243217bSJosh Lehan         std::cerr << "ExternalSensor " << name << " destructed\n";
1187243217bSJosh Lehan     }
1192a40e939SJosh Lehan }
1202a40e939SJosh Lehan 
checkThresholds()121201a1015SEd Tanous void ExternalSensor::checkThresholds()
1222a40e939SJosh Lehan {
1232a40e939SJosh Lehan     thresholds::checkThresholds(this);
1242a40e939SJosh Lehan }
1257243217bSJosh Lehan 
isAliveAndPerishable() const126201a1015SEd Tanous bool ExternalSensor::isAliveAndPerishable() const
1277243217bSJosh Lehan {
1287243217bSJosh Lehan     return (writeAlive && writePerishable);
1297243217bSJosh Lehan }
1307243217bSJosh Lehan 
isAliveAndFresh(const std::chrono::steady_clock::time_point & now) const1317243217bSJosh Lehan bool ExternalSensor::isAliveAndFresh(
1327243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now) const
1337243217bSJosh Lehan {
1347243217bSJosh Lehan     // Must be alive and perishable, to have possibility of being fresh
1357243217bSJosh Lehan     if (!isAliveAndPerishable())
1367243217bSJosh Lehan     {
1377243217bSJosh Lehan         return false;
1387243217bSJosh Lehan     }
1397243217bSJosh Lehan 
1407243217bSJosh Lehan     // If age, as of now, is less than timeout, it is deemed fresh
14192b9629aSAndrew Jeffery     // NOLINTNEXTLINE
1427243217bSJosh Lehan     return (ageElapsed(now) < writeTimeout);
1437243217bSJosh Lehan }
1447243217bSJosh Lehan 
writeBegin(const std::chrono::steady_clock::time_point & now)1457243217bSJosh Lehan void ExternalSensor::writeBegin(
1467243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now)
1477243217bSJosh Lehan {
1487243217bSJosh Lehan     if (!writeAlive)
1497243217bSJosh Lehan     {
1507243217bSJosh Lehan         std::cerr << "ExternalSensor " << name
1517243217bSJosh Lehan                   << " online, receiving first value " << value << "\n";
1527243217bSJosh Lehan     }
1537243217bSJosh Lehan 
1547243217bSJosh Lehan     writeLast = now;
1557243217bSJosh Lehan     writeAlive = true;
1567243217bSJosh Lehan }
1577243217bSJosh Lehan 
writeInvalidate()158201a1015SEd Tanous void ExternalSensor::writeInvalidate()
1597243217bSJosh Lehan {
1607243217bSJosh Lehan     writeAlive = false;
1617243217bSJosh Lehan 
1627243217bSJosh Lehan     std::cerr << "ExternalSensor " << name << " offline, timed out\n";
1637243217bSJosh Lehan 
1647243217bSJosh Lehan     // Take back control of this sensor from the external override,
1657243217bSJosh Lehan     // as the external source has timed out.
1667243217bSJosh Lehan     // This allows sensor::updateValue() to work normally,
1677243217bSJosh Lehan     // as it would do for internal sensors with values from hardware.
1687243217bSJosh Lehan     overriddenState = false;
1697243217bSJosh Lehan 
1707243217bSJosh Lehan     // Invalidate the existing Value, similar to what internal sensors do,
1717243217bSJosh Lehan     // when they encounter errors trying to read from hardware.
1727243217bSJosh Lehan     updateValue(std::numeric_limits<double>::quiet_NaN());
1737243217bSJosh Lehan }
1747243217bSJosh Lehan 
ageElapsed(const std::chrono::steady_clock::time_point & now) const1757243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
1767243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now) const
1777243217bSJosh Lehan {
1787243217bSJosh Lehan     // Comparing 2 time_point will return duration
1797243217bSJosh Lehan     return (now - writeLast);
1807243217bSJosh Lehan }
1817243217bSJosh Lehan 
ageRemaining(const std::chrono::steady_clock::time_point & now) const1827243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
1837243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now) const
1847243217bSJosh Lehan {
1857243217bSJosh Lehan     // Comparing duration will return another duration
1867243217bSJosh Lehan     return (writeTimeout - ageElapsed(now));
1877243217bSJosh Lehan }
1887243217bSJosh Lehan 
externalSetTrigger()189201a1015SEd Tanous void ExternalSensor::externalSetTrigger()
1907243217bSJosh Lehan {
1917243217bSJosh Lehan     if constexpr (debug)
1927243217bSJosh Lehan     {
1937243217bSJosh Lehan         std::cerr << "ExternalSensor " << name << " received " << value << "\n";
1947243217bSJosh Lehan     }
1957243217bSJosh Lehan 
1967243217bSJosh Lehan     auto now = std::chrono::steady_clock::now();
1977243217bSJosh Lehan 
1987243217bSJosh Lehan     writeBegin(now);
1997243217bSJosh Lehan 
2007243217bSJosh Lehan     // Tell the owner to recalculate the expiration timer
2017243217bSJosh Lehan     writeHook(now);
2027243217bSJosh Lehan }
203