xref: /openbmc/telemetry/src/sensor.cpp (revision 405c1e4b)
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,
39          weakSelf = weak_from_this()](boost::system::error_code ec) {
40             phosphor::logging::log<phosphor::logging::level::WARNING>(
41                 "DBus 'GetProperty' call failed on Sensor Value",
42                 phosphor::logging::entry("SENSOR_PATH=%s", id.path.c_str()),
43                 phosphor::logging::entry("ERROR_CODE=%d", ec.value()));
44         },
45         [lock, weakSelf = weak_from_this()](double newValue) {
46             if (auto self = weakSelf.lock())
47             {
48                 self->updateValue(newValue);
49             }
50         });
51 }
52 
53 void Sensor::registerForUpdates(
54     const std::weak_ptr<interfaces::SensorListener>& weakListener)
55 {
56     listeners.erase(
57         std::remove_if(listeners.begin(), listeners.end(),
58                        [](const auto& listener) { return listener.expired(); }),
59         listeners.end());
60 
61     if (auto listener = weakListener.lock())
62     {
63         listeners.emplace_back(weakListener);
64 
65         if (value)
66         {
67             listener->sensorUpdated(*this, timestamp, *value);
68         }
69         else
70         {
71             async_read();
72         }
73     }
74 }
75 
76 void Sensor::updateValue(double newValue)
77 {
78     timestamp = std::time(0);
79 
80     if (value == newValue)
81     {
82         for (const auto& weakListener : listeners)
83         {
84             if (auto listener = weakListener.lock())
85             {
86                 listener->sensorUpdated(*this, timestamp);
87             }
88         }
89     }
90     else
91     {
92         value = newValue;
93 
94         for (const auto& weakListener : listeners)
95         {
96             if (auto listener = weakListener.lock())
97             {
98                 listener->sensorUpdated(*this, timestamp, *value);
99             }
100         }
101     }
102 }
103 
104 void Sensor::makeSignalMonitor()
105 {
106     if (signalMonitor)
107     {
108         return;
109     }
110 
111     using namespace std::string_literals;
112 
113     const auto param = "type='signal',member='PropertiesChanged',path='"s +
114                        sensorId.path +
115                        "',arg0='xyz.openbmc_project.Sensor.Value'"s;
116 
117     signalMonitor = std::make_unique<sdbusplus::bus::match::match>(
118         *bus, param,
119         [weakSelf = weak_from_this()](sdbusplus::message::message& message) {
120             signalProc(weakSelf, message);
121         });
122 }
123 
124 void Sensor::signalProc(const std::weak_ptr<Sensor>& weakSelf,
125                         sdbusplus::message::message& message)
126 {
127     if (auto self = weakSelf.lock())
128     {
129         std::string iface;
130         boost::container::flat_map<std::string, ValueVariant>
131             changed_properties;
132         std::vector<std::string> invalidated_properties;
133 
134         message.read(iface, changed_properties, invalidated_properties);
135 
136         if (iface == "xyz.openbmc_project.Sensor.Value")
137         {
138             const auto it = changed_properties.find("Value");
139             if (it != changed_properties.end())
140             {
141                 if (auto val = std::get_if<double>(&it->second))
142                 {
143                     self->updateValue(*val);
144                 }
145                 else
146                 {
147                     phosphor::logging::log<phosphor::logging::level::ERR>(
148                         "Failed to receive Value from Sensor "
149                         "PropertiesChanged signal",
150                         phosphor::logging::entry("SENSOR_PATH=%s",
151                                                  self->sensorId.path.c_str()));
152                 }
153             }
154         }
155     }
156 }
157