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/algorithm/string/replace.hpp>
92a40e939SJosh Lehan #include <boost/date_time/posix_time/posix_time.hpp>
102a40e939SJosh Lehan #include <sdbusplus/asio/connection.hpp>
112a40e939SJosh Lehan #include <sdbusplus/asio/object_server.hpp>
122a40e939SJosh Lehan 
13*7243217bSJosh Lehan #include <chrono>
142a40e939SJosh Lehan #include <iostream>
152a40e939SJosh Lehan #include <istream>
162a40e939SJosh Lehan #include <limits>
172a40e939SJosh Lehan #include <memory>
182a40e939SJosh Lehan #include <string>
192a40e939SJosh Lehan #include <vector>
202a40e939SJosh Lehan 
21*7243217bSJosh Lehan static constexpr bool debug = false;
22*7243217bSJosh Lehan 
232a40e939SJosh Lehan ExternalSensor::ExternalSensor(
242a40e939SJosh Lehan     const std::string& objectType, sdbusplus::asio::object_server& objectServer,
252a40e939SJosh Lehan     std::shared_ptr<sdbusplus::asio::connection>& conn,
262a40e939SJosh Lehan     const std::string& sensorName, const std::string& sensorUnits,
277b7a9deaSJeff Lin     std::vector<thresholds::Threshold>&& thresholdsIn,
28*7243217bSJosh Lehan     const std::string& sensorConfiguration, double maxReading,
29*7243217bSJosh Lehan     double minReading, double timeoutSecs, const PowerState& powerState,
30*7243217bSJosh Lehan     std::function<void(std::chrono::steady_clock::time_point now)>&&
31*7243217bSJosh Lehan         writeHookIn) :
322a40e939SJosh Lehan     // TODO(): When the Mutable feature is integrated,
332a40e939SJosh Lehan     // make sure all ExternalSensor instances are mutable,
342a40e939SJosh Lehan     // because that is the entire point of ExternalSensor,
352a40e939SJosh Lehan     // to accept sensor values written by an external source.
367b7a9deaSJeff Lin     Sensor(boost::replace_all_copy(sensorName, " ", "_"),
377b7a9deaSJeff Lin            std::move(thresholdsIn), sensorConfiguration, objectType, maxReading,
387b7a9deaSJeff Lin            minReading, conn, powerState),
39*7243217bSJosh Lehan     std::enable_shared_from_this<ExternalSensor>(), objServer(objectServer),
40*7243217bSJosh Lehan     writeLast(std::chrono::steady_clock::now()),
41*7243217bSJosh Lehan     writeTimeout(
42*7243217bSJosh Lehan         std::chrono::duration_cast<std::chrono::steady_clock::duration>(
43*7243217bSJosh Lehan             std::chrono::duration<double>(timeoutSecs))),
44*7243217bSJosh Lehan     writeAlive(false), writePerishable(timeoutSecs > 0.0),
45*7243217bSJosh Lehan     writeHook(std::move(writeHookIn))
462a40e939SJosh Lehan {
472a40e939SJosh Lehan     // The caller must specify what physical characteristic
482a40e939SJosh Lehan     // an external sensor is expected to be measuring, such as temperature,
492a40e939SJosh Lehan     // as, unlike others, this is not implied by device type name.
506cb732a3SEd Tanous     std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
512a40e939SJosh Lehan     if (dbusPath.empty())
522a40e939SJosh Lehan     {
532a40e939SJosh Lehan         throw std::runtime_error("Units not in allow list");
542a40e939SJosh Lehan     }
556cb732a3SEd Tanous     std::string objectPath = "/xyz/openbmc_project/sensors/";
562a40e939SJosh Lehan     objectPath += dbusPath;
572a40e939SJosh Lehan     objectPath += '/';
582a40e939SJosh Lehan     objectPath += sensorName;
592a40e939SJosh Lehan 
602a40e939SJosh Lehan     sensorInterface = objectServer.add_interface(
612a40e939SJosh Lehan         objectPath, "xyz.openbmc_project.Sensor.Value");
622a40e939SJosh Lehan 
632a40e939SJosh Lehan     if (thresholds::hasWarningInterface(thresholds))
642a40e939SJosh Lehan     {
652a40e939SJosh Lehan         thresholdInterfaceWarning = objectServer.add_interface(
662a40e939SJosh Lehan             objectPath, "xyz.openbmc_project.Sensor.Threshold.Warning");
672a40e939SJosh Lehan     }
682a40e939SJosh Lehan     if (thresholds::hasCriticalInterface(thresholds))
692a40e939SJosh Lehan     {
702a40e939SJosh Lehan         thresholdInterfaceCritical = objectServer.add_interface(
712a40e939SJosh Lehan             objectPath, "xyz.openbmc_project.Sensor.Threshold.Critical");
722a40e939SJosh Lehan     }
732a40e939SJosh Lehan 
742a40e939SJosh Lehan     association =
752a40e939SJosh Lehan         objectServer.add_interface(objectPath, association::interface);
762a40e939SJosh Lehan     setInitialProperties(conn);
77*7243217bSJosh Lehan 
78*7243217bSJosh Lehan     externalSetHook = [weakThis = weak_from_this()]() {
79*7243217bSJosh Lehan         auto lockThis = weakThis.lock();
80*7243217bSJosh Lehan         if (lockThis)
81*7243217bSJosh Lehan         {
82*7243217bSJosh Lehan             lockThis->externalSetTrigger();
83*7243217bSJosh Lehan         }
84*7243217bSJosh Lehan     };
85*7243217bSJosh Lehan 
86*7243217bSJosh Lehan     if constexpr (debug)
87*7243217bSJosh Lehan     {
88*7243217bSJosh Lehan         std::cerr << "ExternalSensor " << name << " constructed: path "
89*7243217bSJosh Lehan                   << configurationPath << ", type " << objectType << ", min "
90*7243217bSJosh Lehan                   << minReading << ", max " << maxReading << ", timeout "
91*7243217bSJosh Lehan                   << std::chrono::duration_cast<std::chrono::microseconds>(
92*7243217bSJosh Lehan                          writeTimeout)
93*7243217bSJosh Lehan                          .count()
94*7243217bSJosh Lehan                   << " us\n";
95*7243217bSJosh Lehan     }
962a40e939SJosh Lehan }
972a40e939SJosh Lehan 
982a40e939SJosh Lehan ExternalSensor::~ExternalSensor()
992a40e939SJosh Lehan {
100*7243217bSJosh Lehan     // Make sure the write hook does not reference this object anymore
101*7243217bSJosh Lehan     externalSetHook = nullptr;
102*7243217bSJosh Lehan 
1032a40e939SJosh Lehan     objServer.remove_interface(association);
1042a40e939SJosh Lehan     objServer.remove_interface(thresholdInterfaceCritical);
1052a40e939SJosh Lehan     objServer.remove_interface(thresholdInterfaceWarning);
1062a40e939SJosh Lehan     objServer.remove_interface(sensorInterface);
107*7243217bSJosh Lehan 
108*7243217bSJosh Lehan     if constexpr (debug)
109*7243217bSJosh Lehan     {
110*7243217bSJosh Lehan         std::cerr << "ExternalSensor " << name << " destructed\n";
111*7243217bSJosh Lehan     }
1122a40e939SJosh Lehan }
1132a40e939SJosh Lehan 
1142a40e939SJosh Lehan void ExternalSensor::checkThresholds(void)
1152a40e939SJosh Lehan {
1162a40e939SJosh Lehan     thresholds::checkThresholds(this);
1172a40e939SJosh Lehan }
118*7243217bSJosh Lehan 
119*7243217bSJosh Lehan bool ExternalSensor::isAliveAndPerishable(void) const
120*7243217bSJosh Lehan {
121*7243217bSJosh Lehan     return (writeAlive && writePerishable);
122*7243217bSJosh Lehan }
123*7243217bSJosh Lehan 
124*7243217bSJosh Lehan bool ExternalSensor::isAliveAndFresh(
125*7243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now) const
126*7243217bSJosh Lehan {
127*7243217bSJosh Lehan     // Must be alive and perishable, to have possibility of being fresh
128*7243217bSJosh Lehan     if (!isAliveAndPerishable())
129*7243217bSJosh Lehan     {
130*7243217bSJosh Lehan         return false;
131*7243217bSJosh Lehan     }
132*7243217bSJosh Lehan 
133*7243217bSJosh Lehan     // If age, as of now, is less than timeout, it is deemed fresh
134*7243217bSJosh Lehan     return (ageElapsed(now) < writeTimeout);
135*7243217bSJosh Lehan }
136*7243217bSJosh Lehan 
137*7243217bSJosh Lehan void ExternalSensor::writeBegin(
138*7243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now)
139*7243217bSJosh Lehan {
140*7243217bSJosh Lehan     if (!writeAlive)
141*7243217bSJosh Lehan     {
142*7243217bSJosh Lehan         std::cerr << "ExternalSensor " << name
143*7243217bSJosh Lehan                   << " online, receiving first value " << value << "\n";
144*7243217bSJosh Lehan     }
145*7243217bSJosh Lehan 
146*7243217bSJosh Lehan     writeLast = now;
147*7243217bSJosh Lehan     writeAlive = true;
148*7243217bSJosh Lehan }
149*7243217bSJosh Lehan 
150*7243217bSJosh Lehan void ExternalSensor::writeInvalidate(void)
151*7243217bSJosh Lehan {
152*7243217bSJosh Lehan     writeAlive = false;
153*7243217bSJosh Lehan 
154*7243217bSJosh Lehan     std::cerr << "ExternalSensor " << name << " offline, timed out\n";
155*7243217bSJosh Lehan 
156*7243217bSJosh Lehan     // Take back control of this sensor from the external override,
157*7243217bSJosh Lehan     // as the external source has timed out.
158*7243217bSJosh Lehan     // This allows sensor::updateValue() to work normally,
159*7243217bSJosh Lehan     // as it would do for internal sensors with values from hardware.
160*7243217bSJosh Lehan     overriddenState = false;
161*7243217bSJosh Lehan 
162*7243217bSJosh Lehan     // Invalidate the existing Value, similar to what internal sensors do,
163*7243217bSJosh Lehan     // when they encounter errors trying to read from hardware.
164*7243217bSJosh Lehan     updateValue(std::numeric_limits<double>::quiet_NaN());
165*7243217bSJosh Lehan }
166*7243217bSJosh Lehan 
167*7243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
168*7243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now) const
169*7243217bSJosh Lehan {
170*7243217bSJosh Lehan     // Comparing 2 time_point will return duration
171*7243217bSJosh Lehan     return (now - writeLast);
172*7243217bSJosh Lehan }
173*7243217bSJosh Lehan 
174*7243217bSJosh Lehan std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
175*7243217bSJosh Lehan     const std::chrono::steady_clock::time_point& now) const
176*7243217bSJosh Lehan {
177*7243217bSJosh Lehan     // Comparing duration will return another duration
178*7243217bSJosh Lehan     return (writeTimeout - ageElapsed(now));
179*7243217bSJosh Lehan }
180*7243217bSJosh Lehan 
181*7243217bSJosh Lehan void ExternalSensor::externalSetTrigger(void)
182*7243217bSJosh Lehan {
183*7243217bSJosh Lehan     if constexpr (debug)
184*7243217bSJosh Lehan     {
185*7243217bSJosh Lehan         std::cerr << "ExternalSensor " << name << " received " << value << "\n";
186*7243217bSJosh Lehan     }
187*7243217bSJosh Lehan 
188*7243217bSJosh Lehan     auto now = std::chrono::steady_clock::now();
189*7243217bSJosh Lehan 
190*7243217bSJosh Lehan     writeBegin(now);
191*7243217bSJosh Lehan 
192*7243217bSJosh Lehan     // Tell the owner to recalculate the expiration timer
193*7243217bSJosh Lehan     writeHook(now);
194*7243217bSJosh Lehan }
195