1 #include "sensor.hpp" 2 3 #include "utils/detached_timer.hpp" 4 5 #include <boost/container/flat_map.hpp> 6 #include <phosphor-logging/log.hpp> 7 #include <sdbusplus/asio/property.hpp> 8 #include <sdbusplus/bus/match.hpp> 9 10 #include <functional> 11 #include <iostream> 12 13 Sensor::Sensor(interfaces::Sensor::Id sensorId, boost::asio::io_context& ioc, 14 const std::shared_ptr<sdbusplus::asio::connection>& bus) : 15 sensorId(std::move(sensorId)), 16 ioc(ioc), bus(bus) 17 {} 18 19 Sensor::Id Sensor::makeId(std::string_view service, std::string_view path) 20 { 21 return Id("Sensor", service, path); 22 } 23 24 Sensor::Id Sensor::id() const 25 { 26 return sensorId; 27 } 28 29 void Sensor::async_read() 30 { 31 uniqueCall([this](auto lock) { async_read(std::move(lock)); }); 32 } 33 34 void Sensor::async_read(std::shared_ptr<utils::UniqueCall::Lock> lock) 35 { 36 makeSignalMonitor(); 37 38 sdbusplus::asio::getProperty<double>( 39 *bus, sensorId.service, sensorId.path, 40 "xyz.openbmc_project.Sensor.Value", "Value", 41 [lock, id = sensorId, 42 weakSelf = weak_from_this()](boost::system::error_code ec) { 43 phosphor::logging::log<phosphor::logging::level::ERR>( 44 "DBus 'GetProperty' call failed on Sensor Value", 45 phosphor::logging::entry("sensor=%s, ec=%lu", id.str().c_str(), 46 ec.value())); 47 48 if (auto self = weakSelf.lock()) 49 { 50 51 constexpr auto retryIntervalAfterFailedRead = 52 std::chrono::seconds(30); 53 utils::makeDetachedTimer( 54 self->ioc, retryIntervalAfterFailedRead, [weakSelf] { 55 if (auto self = weakSelf.lock()) 56 { 57 self->async_read(); 58 } 59 }); 60 } 61 }, 62 [lock, weakSelf = weak_from_this()](double newValue) { 63 if (auto self = weakSelf.lock()) 64 { 65 self->updateValue(newValue); 66 } 67 }); 68 } 69 70 void Sensor::registerForUpdates( 71 const std::weak_ptr<interfaces::SensorListener>& weakListener) 72 { 73 if (auto listener = weakListener.lock()) 74 { 75 listeners.emplace_back(weakListener); 76 77 if (value) 78 { 79 listener->sensorUpdated(*this, timestamp, *value); 80 } 81 else 82 { 83 async_read(); 84 } 85 } 86 } 87 88 void Sensor::updateValue(double newValue) 89 { 90 timestamp = std::time(0); 91 92 if (value == newValue) 93 { 94 for (const auto& weakListener : listeners) 95 { 96 if (auto listener = weakListener.lock()) 97 { 98 listener->sensorUpdated(*this, timestamp); 99 } 100 } 101 } 102 else 103 { 104 value = newValue; 105 106 for (const auto& weakListener : listeners) 107 { 108 if (auto listener = weakListener.lock()) 109 { 110 listener->sensorUpdated(*this, timestamp, *value); 111 } 112 } 113 } 114 } 115 116 void Sensor::makeSignalMonitor() 117 { 118 if (signalMonitor) 119 { 120 return; 121 } 122 123 using namespace std::string_literals; 124 125 const auto param = "type='signal',member='PropertiesChanged',path='"s + 126 sensorId.path + 127 "',arg0='xyz.openbmc_project.Sensor.Value'"s; 128 129 signalMonitor = std::make_unique<sdbusplus::bus::match::match>( 130 *bus, param, 131 [weakSelf = weak_from_this()](sdbusplus::message::message& message) { 132 signalProc(weakSelf, message); 133 }); 134 } 135 136 void Sensor::signalProc(const std::weak_ptr<Sensor>& weakSelf, 137 sdbusplus::message::message& message) 138 { 139 if (auto self = weakSelf.lock()) 140 { 141 std::string iface; 142 boost::container::flat_map<std::string, ValueVariant> 143 changed_properties; 144 std::vector<std::string> invalidated_properties; 145 146 message.read(iface, changed_properties, invalidated_properties); 147 148 if (iface == "xyz.openbmc_project.Sensor.Value") 149 { 150 const auto it = changed_properties.find("Value"); 151 if (it != changed_properties.end()) 152 { 153 if (auto val = std::get_if<double>(&it->second)) 154 { 155 self->updateValue(*val); 156 } 157 else 158 { 159 phosphor::logging::log<phosphor::logging::level::ERR>( 160 "Failed to receive Value from Sensor " 161 "PropertiesChanged signal", 162 phosphor::logging::entry("sensor=%s", 163 self->sensorId.path.c_str())); 164 } 165 } 166 } 167 } 168 } 169