xref: /openbmc/telemetry/src/sensor.cpp (revision 9f9ff90a39219ff3a2f1179f74fc9a6dc857e5ab)
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::updateValue(double newValue)
79 {
80     timestamp = std::time(0);
81 
82     if (value == newValue)
83     {
84         for (const auto& weakListener : listeners)
85         {
86             if (auto listener = weakListener.lock())
87             {
88                 listener->sensorUpdated(*this, timestamp);
89             }
90         }
91     }
92     else
93     {
94         value = newValue;
95 
96         for (const auto& weakListener : listeners)
97         {
98             if (auto listener = weakListener.lock())
99             {
100                 listener->sensorUpdated(*this, timestamp, *value);
101             }
102         }
103     }
104 }
105 
106 void Sensor::makeSignalMonitor()
107 {
108     if (signalMonitor)
109     {
110         return;
111     }
112 
113     using namespace std::string_literals;
114 
115     const auto param = "type='signal',member='PropertiesChanged',path='"s +
116                        sensorId.path +
117                        "',arg0='xyz.openbmc_project.Sensor.Value'"s;
118 
119     signalMonitor = std::make_unique<sdbusplus::bus::match::match>(
120         *bus, param,
121         [weakSelf = weak_from_this()](sdbusplus::message::message& message) {
122             signalProc(weakSelf, message);
123         });
124 }
125 
126 void Sensor::signalProc(const std::weak_ptr<Sensor>& weakSelf,
127                         sdbusplus::message::message& message)
128 {
129     if (auto self = weakSelf.lock())
130     {
131         std::string iface;
132         boost::container::flat_map<std::string, ValueVariant>
133             changed_properties;
134         std::vector<std::string> invalidated_properties;
135 
136         message.read(iface, changed_properties, invalidated_properties);
137 
138         if (iface == "xyz.openbmc_project.Sensor.Value")
139         {
140             const auto it = changed_properties.find("Value");
141             if (it != changed_properties.end())
142             {
143                 if (auto val = std::get_if<double>(&it->second))
144                 {
145                     self->updateValue(*val);
146                 }
147                 else
148                 {
149                     phosphor::logging::log<phosphor::logging::level::ERR>(
150                         "Failed to receive Value from Sensor "
151                         "PropertiesChanged signal",
152                         phosphor::logging::entry("SENSOR_PATH=%s",
153                                                  self->sensorId.path.c_str()));
154                 }
155             }
156         }
157     }
158 }
159