xref: /openbmc/telemetry/src/sensor.cpp (revision 51497a0c)
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