xref: /openbmc/phosphor-modbus/rtu/device/base_device.cpp (revision 7184805ae4ede906935133e3e0f8ee2468bc781b)
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 
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     info("Successfully created device {NAME}", "NAME", config.name);
21 }
22 
23 static 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 
32 auto 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 
55 static 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 
105 auto 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 
151         co_await readStatusRegisters();
152 
153         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 
163 static 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 
204 auto 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 
252 auto 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 
307 } // namespace phosphor::modbus::rtu::device
308