xref: /openbmc/telemetry/src/sensor.cpp (revision 18e7101c)
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(
96             std::remove_if(
97                 listeners.begin(), listeners.end(),
98                 [listenerToUnregister = listener.get()](const auto& listener) {
99                     return (listener.expired() ||
100                             listener.lock().get() == listenerToUnregister);
101                 }),
102             listeners.end());
103     }
104 }
105 
106 void Sensor::updateValue(double newValue)
107 {
108     timestamp = Clock().steadyTimestamp();
109 
110     if (value != newValue)
111     {
112         value = newValue;
113 
114         for (const auto& weakListener : listeners)
115         {
116             if (auto listener = weakListener.lock())
117             {
118                 listener->sensorUpdated(*this, timestamp, *value);
119             }
120         }
121     }
122 }
123 
124 void Sensor::makeSignalMonitor()
125 {
126     if (signalMonitor)
127     {
128         return;
129     }
130 
131     using namespace std::string_literals;
132 
133     const auto param = "type='signal',member='PropertiesChanged',path='"s +
134                        sensorId.path +
135                        "',arg0='xyz.openbmc_project.Sensor.Value'"s;
136 
137     signalMonitor = std::make_unique<sdbusplus::bus::match_t>(
138         *bus, param,
139         [weakSelf = weak_from_this()](sdbusplus::message_t& message) {
140             signalProc(weakSelf, message);
141         });
142 }
143 
144 void Sensor::signalProc(const std::weak_ptr<Sensor>& weakSelf,
145                         sdbusplus::message_t& message)
146 {
147     if (auto self = weakSelf.lock())
148     {
149         std::string iface;
150         boost::container::flat_map<std::string, ValueVariant>
151             changed_properties;
152         std::vector<std::string> invalidated_properties;
153 
154         message.read(iface, changed_properties, invalidated_properties);
155 
156         if (iface == "xyz.openbmc_project.Sensor.Value")
157         {
158             const auto it = changed_properties.find("Value");
159             if (it != changed_properties.end())
160             {
161                 if (auto val = std::get_if<double>(&it->second))
162                 {
163                     self->updateValue(*val);
164                 }
165                 else
166                 {
167                     phosphor::logging::log<phosphor::logging::level::ERR>(
168                         "Failed to receive Value from Sensor "
169                         "PropertiesChanged signal",
170                         phosphor::logging::entry("SENSOR_PATH=%s",
171                                                  self->sensorId.path.c_str()));
172                 }
173             }
174         }
175     }
176 }
177 
178 LabeledSensorInfo Sensor::getLabeledSensorInfo() const
179 {
180     return LabeledSensorInfo(sensorId.service, sensorId.path, sensorMetadata);
181 }
182