blob: c075d55a36a58a15c3253cee4d374fff336d3749 [file] [log] [blame]
Jagpal Singh Gille92aba42025-10-16 00:00:13 -07001#include "base_device.hpp"
2
3#include <phosphor-logging/lg2.hpp>
Jagpal Singh Gill71848052025-10-16 00:28:58 -07004#include <xyz/openbmc_project/State/Leak/Detector/aserver.hpp>
Jagpal Singh Gille92aba42025-10-16 00:00:13 -07005
6#include <numeric>
7
8namespace phosphor::modbus::rtu::device
9{
10
11PHOSPHOR_LOG2_USING;
12
13BaseDevice::BaseDevice(sdbusplus::async::context& ctx,
Jagpal Singh Gill71848052025-10-16 00:28:58 -070014 const config::Config& config, PortIntf& serialPort,
15 EventIntf::Events& events) :
16 ctx(ctx), config(config), serialPort(serialPort), events(events)
Jagpal Singh Gille92aba42025-10-16 00:00:13 -070017{
18 createSensors();
19
20 info("Successfully created device {NAME}", "NAME", config.name);
21}
22
23static auto getObjectPath(const std::string& sensorType,
24 const std::string& sensorName)
25 -> sdbusplus::message::object_path
26{
27 return sdbusplus::message::object_path(
28 std::string(SensorValueIntf::namespace_path::value) + "/" + sensorType +
29 "/" + sensorName);
30}
31
32auto BaseDevice::createSensors() -> void
33{
34 for (const auto& sensorRegister : config.sensorRegisters)
35 {
36 SensorValueIntf::properties_t initProperties = {
37 std::numeric_limits<double>::quiet_NaN(),
38 std::numeric_limits<double>::quiet_NaN(),
39 std::numeric_limits<double>::quiet_NaN(), sensorRegister.unit};
40
41 auto sensorPath = getObjectPath(
42 sensorRegister.pathSuffix, config.name + "_" + sensorRegister.name);
43
44 auto sensor = std::make_unique<SensorValueIntf>(
45 ctx, sensorPath.str.c_str(), initProperties);
46
47 sensor->emit_added();
48
49 sensors.emplace(sensorRegister.name, std::move(sensor));
50 }
51
52 return;
53}
54
55static auto getRawIntegerFromRegister(const std::vector<uint16_t>& reg,
56 bool sign) -> int64_t
57{
58 if (reg.empty())
59 {
60 return 0;
61 }
62
63 uint64_t accumulator = 0;
64 for (auto val : reg)
65 {
66 accumulator = (accumulator << 16) | val;
67 }
68
69 int64_t result = 0;
70
71 if (sign)
72 {
73 if (reg.size() == 1)
74 {
75 result = static_cast<int16_t>(accumulator);
76 }
77 else if (reg.size() == 2)
78 {
79 result = static_cast<int32_t>(accumulator);
80 }
81 else
82 {
83 result = static_cast<int64_t>(accumulator);
84 }
85 }
86 else
87 {
88 if (reg.size() == 1)
89 {
90 result = static_cast<uint16_t>(accumulator);
91 }
92 else if (reg.size() == 2)
93 {
94 result = static_cast<uint32_t>(accumulator);
95 }
96 else
97 {
98 result = static_cast<int64_t>(accumulator);
99 }
100 }
101
102 return result;
103}
104
105auto BaseDevice::readSensorRegisters() -> sdbusplus::async::task<void>
106{
107 while (!ctx.stop_requested())
108 {
109 for (const auto& sensorRegister : config.sensorRegisters)
110 {
111 auto sensor = sensors.find(sensorRegister.name);
112 if (sensor == sensors.end())
113 {
114 error("Sensor not found for {NAME}", "NAME",
115 sensorRegister.name);
116 continue;
117 }
118
119 if (sensorRegister.size > 4)
120 {
121 error("Unsupported size for register {NAME}", "NAME",
122 sensorRegister.name);
123 continue;
124 }
125
126 auto registers = std::vector<uint16_t>(sensorRegister.size);
127 auto ret = co_await serialPort.readHoldingRegisters(
128 config.address, sensorRegister.offset, config.baudRate,
129 config.parity, registers);
130 if (!ret)
131 {
132 error(
133 "Failed to read holding registers {NAME} for {DEVICE_ADDRESS}",
134 "NAME", sensorRegister.name, "DEVICE_ADDRESS",
135 config.address);
136 continue;
137 }
138
139 double regVal = static_cast<double>(
140 getRawIntegerFromRegister(registers, sensorRegister.isSigned));
141 if (sensorRegister.format == config::SensorFormat::floatingPoint)
142 {
143 regVal = sensorRegister.shift +
144 (sensorRegister.scale *
145 (regVal / (1ULL << sensorRegister.precision)));
146 }
147
148 sensor->second->value(regVal);
149 }
150
Jagpal Singh Gill71848052025-10-16 00:28:58 -0700151 co_await readStatusRegisters();
152
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700153 constexpr auto pollInterval = 3;
154 co_await sdbusplus::async::sleep_for(
155 ctx, std::chrono::seconds(pollInterval));
156 debug("Polling sensors for {NAME} in {INTERVAL} seconds", "NAME",
157 config.name, "INTERVAL", pollInterval);
158 }
159
160 co_return;
161}
162
Jagpal Singh Gill71848052025-10-16 00:28:58 -0700163static auto getObjectPath(const config::Config& config, config::StatusType type,
164 const std::string& name)
165 -> sdbusplus::message::object_path
166{
167 switch (type)
168 {
169 case config::StatusType::sensorReadingCritical:
170 case config::StatusType::sensorReadingWarning:
171 case config::StatusType::sensorFailure:
172 return sdbusplus::message::object_path(
173 std::string(SensorValueIntf::namespace_path::value) + "/" +
174 name);
175 case config::StatusType::controllerFailure:
176 return config.inventoryPath;
177 case config::StatusType::pumpFailure:
178 return sdbusplus::message::object_path(
179 "/xyz/openbmc_project/state/pump/" + name);
180 case config::StatusType::filterFailure:
181 return sdbusplus::message::object_path(
182 "/xyz/openbmc_project/state/filter/" + name);
183 case config::StatusType::powerFault:
184 return sdbusplus::message::object_path(
185 "/xyz/openbmc_project/state/power_rail/" + name);
186 case config::StatusType::fanFailure:
187 return sdbusplus::message::object_path(
188 "/xyz/openbmc_project/state/fan/" + name);
189 case config::StatusType::leakDetectedCritical:
190 case config::StatusType::leakDetectedWarning:
191 using DetectorIntf =
192 sdbusplus::aserver::xyz::openbmc_project::state::leak::Detector<
193 Device>;
194 return sdbusplus::message::object_path(
195 std::string(DetectorIntf::namespace_path::value) + "/" +
196 DetectorIntf::namespace_path::detector + "/" + name);
197 case config::StatusType::unknown:
198 error("Unknown status type for {NAME}", "NAME", name);
199 }
200
201 return sdbusplus::message::object_path();
202}
203
204auto BaseDevice::readStatusRegisters() -> sdbusplus::async::task<void>
205{
206 for (const auto& [address, statusBits] : config.statusRegisters)
207 {
208 static constexpr auto maxRegisterSize = 1;
209 auto registers = std::vector<uint16_t>(maxRegisterSize);
210
211 auto ret = co_await serialPort.readHoldingRegisters(
212 config.address, address, config.baudRate, config.parity, registers);
213 if (!ret)
214 {
215 error("Failed to read holding registers for {DEVICE_ADDRESS}",
216 "DEVICE_ADDRESS", config.address);
217 continue;
218 }
219
220 for (const auto& statusBit : statusBits)
221 {
222 static constexpr auto maxBitPoistion = 15;
223 if (statusBit.bitPosition > maxBitPoistion)
224 {
225 error("Invalid status bit position {POSITION} for {NAME}",
226 "POSITION", statusBit.bitPosition, "NAME",
227 statusBit.name);
228 continue;
229 }
230 auto statusBitValue =
231 ((registers[0] & (1 << statusBit.bitPosition)) != 0);
232 auto statusAsserted = (statusBitValue == statusBit.value);
233 auto objectPath =
234 getObjectPath(config, statusBit.type, statusBit.name);
235 double sensorValue = std::numeric_limits<double>::quiet_NaN();
236 SensorValueIntf::Unit sensorUnit = SensorValueIntf::Unit::Percent;
237 auto sensorIter = sensors.find(statusBit.name);
238 if (sensorIter != sensors.end())
239 {
240 sensorValue = sensorIter->second->value();
241 sensorUnit = sensorIter->second->unit();
242 }
243
244 co_await generateEvent(statusBit, objectPath, sensorValue,
245 sensorUnit, statusAsserted);
246 }
247 }
248
249 co_return;
250}
251
252auto BaseDevice::generateEvent(
253 const config::StatusBit& statusBit,
254 const sdbusplus::message::object_path& objectPath, double sensorValue,
255 SensorValueIntf::Unit sensorUnit, bool statusAsserted)
256 -> sdbusplus::async::task<void>
257{
258 switch (statusBit.type)
259 {
260 case config::StatusType::sensorReadingCritical:
261 co_await events.generateSensorReadingEvent(
262 objectPath, EventIntf::EventLevel::critical, sensorValue,
263 sensorUnit, statusAsserted);
264 break;
265 case config::StatusType::sensorReadingWarning:
266 co_await events.generateSensorReadingEvent(
267 objectPath, EventIntf::EventLevel::warning, sensorValue,
268 sensorUnit, statusAsserted);
269 break;
270 case config::StatusType::sensorFailure:
271 co_await events.generateSensorFailureEvent(objectPath,
272 statusAsserted);
273 break;
274 case config::StatusType::controllerFailure:
275 co_await events.generateControllerFailureEvent(
276 objectPath, statusBit.name, statusAsserted);
277 break;
278 case config::StatusType::powerFault:
279 co_await events.generatePowerFaultEvent(objectPath, statusBit.name,
280 statusAsserted);
281 break;
282 case config::StatusType::filterFailure:
283 co_await events.generateFilterFailureEvent(objectPath,
284 statusAsserted);
285 break;
286 case config::StatusType::pumpFailure:
287 co_await events.generatePumpFailureEvent(objectPath,
288 statusAsserted);
289 break;
290 case config::StatusType::fanFailure:
291 co_await events.generateFanFailureEvent(objectPath, statusAsserted);
292 break;
293 case config::StatusType::leakDetectedCritical:
294 co_await events.generateLeakDetectedEvent(
295 objectPath, EventIntf::EventLevel::critical, statusAsserted);
296 break;
297 case config::StatusType::leakDetectedWarning:
298 co_await events.generateLeakDetectedEvent(
299 objectPath, EventIntf::EventLevel::warning, statusAsserted);
300 break;
301 case config::StatusType::unknown:
302 error("Unknown status type for {NAME}", "NAME", statusBit.name);
303 break;
304 }
305}
306
Jagpal Singh Gille92aba42025-10-16 00:00:13 -0700307} // namespace phosphor::modbus::rtu::device