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