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