1 #include "ExternalSensor.hpp" 2 3 #include "SensorPaths.hpp" 4 #include "Thresholds.hpp" 5 #include "Utils.hpp" 6 #include "sensor.hpp" 7 8 #include <sdbusplus/asio/connection.hpp> 9 #include <sdbusplus/asio/object_server.hpp> 10 11 #include <chrono> 12 #include <cstddef> 13 #include <functional> 14 #include <iostream> 15 #include <limits> 16 #include <memory> 17 #include <stdexcept> 18 #include <string> 19 #include <utility> 20 #include <vector> 21 22 static constexpr bool debug = false; 23 24 ExternalSensor::ExternalSensor( 25 const std::string& objectType, sdbusplus::asio::object_server& objectServer, 26 std::shared_ptr<sdbusplus::asio::connection>& conn, 27 const std::string& sensorName, const std::string& sensorUnits, 28 std::vector<thresholds::Threshold>&& thresholdsIn, 29 const std::string& sensorConfiguration, double maxReading, 30 double minReading, double timeoutSecs, const PowerState& powerState) : 31 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, 32 objectType, true, true, maxReading, minReading, conn, powerState), 33 objServer(objectServer), writeLast(std::chrono::steady_clock::now()), 34 writeTimeout( 35 std::chrono::duration_cast<std::chrono::steady_clock::duration>( 36 std::chrono::duration<double>(timeoutSecs))), 37 writePerishable(timeoutSecs > 0.0) 38 { 39 // The caller must specify what physical characteristic 40 // an external sensor is expected to be measuring, such as temperature, 41 // as, unlike others, this is not implied by device type name. 42 std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits); 43 if (dbusPath.empty()) 44 { 45 throw std::runtime_error("Units not in allow list"); 46 } 47 std::string objectPath = "/xyz/openbmc_project/sensors/"; 48 objectPath += dbusPath; 49 objectPath += '/'; 50 objectPath += sensorName; 51 52 sensorInterface = objectServer.add_interface( 53 objectPath, "xyz.openbmc_project.Sensor.Value"); 54 55 for (const auto& threshold : thresholds) 56 { 57 std::string interface = thresholds::getInterface(threshold.level); 58 thresholdInterfaces[static_cast<size_t>(threshold.level)] = 59 objectServer.add_interface(objectPath, interface); 60 } 61 62 association = 63 objectServer.add_interface(objectPath, association::interface); 64 setInitialProperties(sensorUnits); 65 66 if constexpr (debug) 67 { 68 std::cerr << "ExternalSensor " << name << " constructed: path " 69 << configurationPath << ", type " << objectType << ", min " 70 << minReading << ", max " << maxReading << ", timeout " 71 << std::chrono::duration_cast<std::chrono::microseconds>( 72 writeTimeout) 73 .count() 74 << " us\n"; 75 } 76 } 77 78 // Separate function from constructor, because of a gotcha: can't use the 79 // enable_shared_from_this() API until after the constructor has completed. 80 void ExternalSensor::initWriteHook( 81 std::function<void(std::chrono::steady_clock::time_point now)>&& 82 writeHookIn) 83 { 84 // Connect ExternalSensorMain with ExternalSensor 85 writeHook = std::move(writeHookIn); 86 87 // Connect ExternalSensor with Sensor 88 auto weakThis = weak_from_this(); 89 externalSetHook = [weakThis]() { 90 auto lockThis = weakThis.lock(); 91 if (lockThis) 92 { 93 lockThis->externalSetTrigger(); 94 return; 95 } 96 if constexpr (debug) 97 { 98 std::cerr << "ExternalSensor receive ignored, sensor gone\n"; 99 } 100 }; 101 } 102 103 ExternalSensor::~ExternalSensor() 104 { 105 // Make sure the write hook does not reference this object anymore 106 externalSetHook = nullptr; 107 108 objServer.remove_interface(association); 109 for (const auto& iface : thresholdInterfaces) 110 { 111 objServer.remove_interface(iface); 112 } 113 objServer.remove_interface(sensorInterface); 114 115 if constexpr (debug) 116 { 117 std::cerr << "ExternalSensor " << name << " destructed\n"; 118 } 119 } 120 121 void ExternalSensor::checkThresholds() 122 { 123 thresholds::checkThresholds(this); 124 } 125 126 bool ExternalSensor::isAliveAndPerishable() const 127 { 128 return (writeAlive && writePerishable); 129 } 130 131 bool ExternalSensor::isAliveAndFresh( 132 const std::chrono::steady_clock::time_point& now) const 133 { 134 // Must be alive and perishable, to have possibility of being fresh 135 if (!isAliveAndPerishable()) 136 { 137 return false; 138 } 139 140 // If age, as of now, is less than timeout, it is deemed fresh 141 // NOLINTNEXTLINE 142 return (ageElapsed(now) < writeTimeout); 143 } 144 145 void ExternalSensor::writeBegin( 146 const std::chrono::steady_clock::time_point& now) 147 { 148 if (!writeAlive) 149 { 150 std::cerr << "ExternalSensor " << name 151 << " online, receiving first value " << value << "\n"; 152 } 153 154 writeLast = now; 155 writeAlive = true; 156 } 157 158 void ExternalSensor::writeInvalidate() 159 { 160 writeAlive = false; 161 162 std::cerr << "ExternalSensor " << name << " offline, timed out\n"; 163 164 // Take back control of this sensor from the external override, 165 // as the external source has timed out. 166 // This allows sensor::updateValue() to work normally, 167 // as it would do for internal sensors with values from hardware. 168 overriddenState = false; 169 170 // Invalidate the existing Value, similar to what internal sensors do, 171 // when they encounter errors trying to read from hardware. 172 updateValue(std::numeric_limits<double>::quiet_NaN()); 173 } 174 175 std::chrono::steady_clock::duration ExternalSensor::ageElapsed( 176 const std::chrono::steady_clock::time_point& now) const 177 { 178 // Comparing 2 time_point will return duration 179 return (now - writeLast); 180 } 181 182 std::chrono::steady_clock::duration ExternalSensor::ageRemaining( 183 const std::chrono::steady_clock::time_point& now) const 184 { 185 // Comparing duration will return another duration 186 return (writeTimeout - ageElapsed(now)); 187 } 188 189 void ExternalSensor::externalSetTrigger() 190 { 191 if constexpr (debug) 192 { 193 std::cerr << "ExternalSensor " << name << " received " << value << "\n"; 194 } 195 196 auto now = std::chrono::steady_clock::now(); 197 198 writeBegin(now); 199 200 // Tell the owner to recalculate the expiration timer 201 writeHook(now); 202 } 203