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