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