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