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