xref: /openbmc/phosphor-modbus/rtu/device/base_device.cpp (revision 1f1d00458df8a375538de5f103c0566ae8a0159b)
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