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