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