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