xref: /openbmc/phosphor-modbus/rtu/device/base_device.cpp (revision cf77ef540b925e10e3078bbdfbd795a0c1d9ff1f)
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(SensorValueIntf::namespace_path::value) + "/" + sensorType +
36         "/" + sensorName);
37 }
38 
createSensors()39 auto 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 
getRawIntegerFromRegister(const std::vector<uint16_t> & reg,bool sign)62 static 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 
readSensorRegisters()112 auto 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 
158         co_await readStatusRegisters();
159 
160         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 
getObjectPath(const config::Config & config,config::StatusType type,const std::string & name)170 static 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 
readStatusRegisters()211 auto 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 
generateEvent(const config::StatusBit & statusBit,const sdbusplus::message::object_path & objectPath,double sensorValue,SensorValueIntf::Unit sensorUnit,bool statusAsserted)259 auto 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 
314 } // namespace phosphor::modbus::rtu::device
315