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