xref: /openbmc/phosphor-modbus/rtu/device/base_config.cpp (revision e92aba4516471f5a01d4ab1f93eb9919ec05c21f)
1 #include "base_config.hpp"
2 
3 #include "common/entity_manager_interface.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 #include <xyz/openbmc_project/Inventory/Item/client.hpp>
7 #include <xyz/openbmc_project/State/Leak/Detector/aserver.hpp>
8 
9 #include <flat_map>
10 
11 namespace phosphor::modbus::rtu::device::config
12 {
13 
14 PHOSPHOR_LOG2_USING;
15 
16 using BasicVariantType =
17     std::variant<std::vector<std::string>, std::vector<uint8_t>, std::string,
18                  int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
19                  uint16_t, uint8_t, bool>;
20 using InventoryBaseConfigMap = std::flat_map<std::string, BasicVariantType>;
21 using InventoryData = std::flat_map<std::string, InventoryBaseConfigMap>;
22 using ManagedObjectType =
23     std::flat_map<sdbusplus::message::object_path, InventoryData>;
24 
25 static constexpr std::array<std::pair<std::string_view, Parity>, 3>
26     validParities = {
27         {{"Odd", Parity::odd}, {"Even", Parity::even}, {"None", Parity::none}}};
28 
29 template <typename T>
getValue(const InventoryBaseConfigMap & configMap,const std::string & key,const std::string & contextName)30 auto getValue(const InventoryBaseConfigMap& configMap, const std::string& key,
31               const std::string& contextName) -> T
32 {
33     auto iter = configMap.find(key);
34     if (iter == configMap.end())
35     {
36         throw std::runtime_error(
37             "Missing property " + key + " for " + contextName);
38     }
39 
40     try
41     {
42         return std::get<T>(iter->second);
43     }
44     catch (const std::bad_variant_access& ex)
45     {
46         throw std::runtime_error(
47             "Incorrect type for property " + key + " in " + contextName);
48     }
49 }
50 
getDataParity(Config & config,const InventoryBaseConfigMap & configMap)51 static inline auto getDataParity(Config& config,
52                                  const InventoryBaseConfigMap& configMap)
53     -> void
54 {
55     auto receivedParity =
56         getValue<std::string>(configMap, "DataParity", config.name);
57 
58     for (const auto& [parityStr, parity] : validParities)
59     {
60         if (parityStr == receivedParity)
61         {
62             config.parity = parity;
63             break;
64         }
65     }
66 
67     if (config.parity == Parity::unknown)
68     {
69         throw std::runtime_error(
70             "Invalid parity " + receivedParity + " for " + config.name);
71     }
72 }
73 
processDeviceInterface(Config & config,const InventoryBaseConfigMap & configMap)74 static auto processDeviceInterface(Config& config,
75                                    const InventoryBaseConfigMap& configMap)
76     -> void
77 {
78     debug("Processing device config");
79 
80     config.name = getValue<std::string>(configMap, "Name", config.name);
81 
82     std::replace(config.name.begin(), config.name.end(), ' ', '_');
83 
84     config.address = getValue<uint64_t>(configMap, "Address", config.name);
85 
86     getDataParity(config, configMap);
87 
88     config.baudRate = getValue<uint64_t>(configMap, "BaudRate", config.name);
89 
90     config.portName =
91         getValue<std::string>(configMap, "SerialPort", config.name);
92 
93     getValue<std::string>(configMap, "Type", config.name);
94 }
95 
96 static const auto sensorTypes = std::unordered_map<
97     std::string_view, std::pair<std::string_view, SensorValueIntf::Unit>>{
98     {"FanTach",
99      {SensorValueIntf::namespace_path::fan_tach, SensorValueIntf::Unit::RPMS}},
100     {"LiquidFlow",
101      {SensorValueIntf::namespace_path::liquidflow, SensorValueIntf::Unit::LPM}},
102     {"Power",
103      {SensorValueIntf::namespace_path::power, SensorValueIntf::Unit::Watts}},
104     {"Pressure",
105      {SensorValueIntf::namespace_path::pressure,
106       SensorValueIntf::Unit::Pascals}},
107     {"Temperature",
108      {SensorValueIntf::namespace_path::temperature,
109       SensorValueIntf::Unit::DegreesC}},
110 };
111 
112 static const auto formatTypes =
113     std::unordered_map<std::string_view, SensorFormat>{
114         {"Integer", SensorFormat::integer},
115         {"Float", SensorFormat::floatingPoint}};
116 
processRegisterType(SensorRegister & sensorRegister,const InventoryBaseConfigMap & configMap)117 static auto processRegisterType(SensorRegister& sensorRegister,
118                                 const InventoryBaseConfigMap& configMap) -> void
119 {
120     auto registerType =
121         getValue<std::string>(configMap, "RegisterType", sensorRegister.name);
122 
123     auto type = sensorTypes.find(registerType);
124     if (type == sensorTypes.end())
125     {
126         throw std::runtime_error("Invalid RegisterType " + registerType +
127                                  " for " + sensorRegister.name);
128     }
129     sensorRegister.pathSuffix = type->second.first;
130     sensorRegister.unit = type->second.second;
131 }
132 
processRegisterFormat(SensorRegister & sensorRegister,const InventoryBaseConfigMap & configMap)133 static auto processRegisterFormat(SensorRegister& sensorRegister,
134                                   const InventoryBaseConfigMap& configMap)
135     -> void
136 {
137     auto format =
138         getValue<std::string>(configMap, "Format", sensorRegister.name);
139 
140     auto formatIter = formatTypes.find(format);
141     if (formatIter == formatTypes.end())
142     {
143         throw std::runtime_error(
144             "Invalid Format " + format + " for " + sensorRegister.name);
145     }
146     sensorRegister.format = formatIter->second;
147 }
148 
processSensorRegistersInterface(Config & config,const InventoryBaseConfigMap & configMap)149 static auto processSensorRegistersInterface(
150     Config& config, const InventoryBaseConfigMap& configMap) -> void
151 {
152     SensorRegister sensorRegister = {};
153 
154     sensorRegister.name = getValue<std::string>(configMap, "Name", config.name);
155 
156     processRegisterType(sensorRegister, configMap);
157 
158     sensorRegister.offset =
159         getValue<uint64_t>(configMap, "Address", config.name);
160 
161     sensorRegister.size = getValue<uint64_t>(configMap, "Size", config.name);
162 
163     sensorRegister.precision =
164         getValue<uint64_t>(configMap, "Precision", config.name);
165 
166     sensorRegister.shift = getValue<double>(configMap, "Shift", config.name);
167 
168     sensorRegister.scale = getValue<double>(configMap, "Scale", config.name);
169 
170     sensorRegister.isSigned = getValue<bool>(configMap, "Signed", config.name);
171 
172     processRegisterFormat(sensorRegister, configMap);
173 
174     config.sensorRegisters.emplace_back(sensorRegister);
175 }
176 
177 static const auto statusBitTypes =
178     std::unordered_map<std::string_view, StatusType>{
179         {"ControllerFailure", StatusType::controllerFailure},
180         {"FanFailure", StatusType::fanFailure},
181         {"FilterFailure", StatusType::filterFailure},
182         {"PowerFault", StatusType::powerFault},
183         {"PumpFailure", StatusType::pumpFailure},
184         {"LeakDetectedCritical", StatusType::leakDetectedCritical},
185         {"LeakDetectedWarning", StatusType::leakDetectedWarning},
186         {"SensorFailure", StatusType::sensorFailure},
187         {"SensorReadingCritical", StatusType::sensorReadingCritical},
188         {"SensorReadingWarning", StatusType::sensorReadingWarning}};
189 
processStatusBitsInterface(Config & config,const InventoryBaseConfigMap & configMap)190 static auto processStatusBitsInterface(Config& config,
191                                        const InventoryBaseConfigMap& configMap)
192     -> void
193 {
194     debug("Processing StatusBits for {NAME}", "NAME", config.name);
195 
196     StatusBit statusBit = {};
197 
198     statusBit.name = getValue<std::string>(configMap, "Name", config.name);
199 
200     auto type = getValue<std::string>(configMap, "StatusType", config.name);
201     auto typeIter = statusBitTypes.find(type);
202     if (typeIter == statusBitTypes.end())
203     {
204         throw std::runtime_error(
205             "Invalid StatusType " + type + " for " + statusBit.name);
206     }
207     statusBit.type = typeIter->second;
208 
209     statusBit.bitPosition =
210         getValue<uint64_t>(configMap, "BitPosition", config.name);
211 
212     statusBit.value = getValue<bool>(configMap, "Value", config.name);
213 
214     auto address = getValue<uint64_t>(configMap, "Address", config.name);
215 
216     config.statusRegisters[address].emplace_back(statusBit);
217 }
218 
219 static const auto firmwareRegisterTypes =
220     std::unordered_map<std::string_view, FirmwareRegisterType>{
221         {"Version", FirmwareRegisterType::version},
222         {"Update", FirmwareRegisterType::update}};
223 
processFirmwareRegistersInterface(Config & config,const InventoryBaseConfigMap & configMap)224 static auto processFirmwareRegistersInterface(
225     Config& config, const InventoryBaseConfigMap& configMap) -> void
226 {
227     debug("Processing FirmwareRegisters for {NAME}", "NAME", config.name);
228 
229     FirmwareRegister firmwareRegister = {};
230 
231     firmwareRegister.name =
232         getValue<std::string>(configMap, "Name", config.name);
233 
234     firmwareRegister.offset =
235         getValue<uint64_t>(configMap, "Address", firmwareRegister.name);
236 
237     firmwareRegister.size =
238         getValue<uint64_t>(configMap, "Size", firmwareRegister.name);
239 
240     auto registerType =
241         getValue<std::string>(configMap, "RegisterType", firmwareRegister.name);
242     auto registerTypeIter = firmwareRegisterTypes.find(registerType);
243     if (registerTypeIter == firmwareRegisterTypes.end())
244     {
245         throw std::runtime_error("Invalid RegisterType " + registerType +
246                                  " for " + firmwareRegister.name);
247     }
248     firmwareRegister.type = registerTypeIter->second;
249 
250     config.firmwareRegisters.emplace_back(firmwareRegister);
251 }
252 
printConfig(const Config & config)253 static auto printConfig(const Config& config) -> void
254 {
255     info("Device Config for {NAME}: {ADDRESS} {PORT} {INV_PATH}", "NAME",
256          config.name, "ADDRESS", config.address, "PORT", config.portName,
257          "INV_PATH", config.inventoryPath);
258 
259     for (const auto& sensorRegister : config.sensorRegisters)
260     {
261         info(
262             "Sensor Register {NAME} {ADDRESS} {SIZE} {PRECISION} {SCALE} {SIGNED} {FORMAT} {UNIT} {PATH_SUFFIX}",
263             "NAME", sensorRegister.name, "ADDRESS", sensorRegister.offset,
264             "SIZE", sensorRegister.size, "PRECISION", sensorRegister.precision,
265             "SCALE", sensorRegister.scale, "SIGNED", sensorRegister.isSigned,
266             "FORMAT", sensorRegister.format, "UNIT", sensorRegister.unit,
267             "PATH_SUFFIX", sensorRegister.pathSuffix);
268     }
269 
270     for (const auto& [address, statusBits] : config.statusRegisters)
271     {
272         for (const auto& statusBit : statusBits)
273         {
274             info("Status Bit {NAME} {ADDRESS} {BIT_POSITION} {VALUE} {TYPE}",
275                  "NAME", statusBit.name, "ADDRESS", address, "BIT_POSITION",
276                  statusBit.bitPosition, "VALUE", statusBit.value, "TYPE",
277                  statusBit.type);
278         }
279     }
280 
281     for (const auto& firmwareRegister : config.firmwareRegisters)
282     {
283         info("Firmware Register {NAME} {ADDRESS} {SIZE} {TYPE}", "NAME",
284              firmwareRegister.name, "ADDRESS", firmwareRegister.offset, "SIZE",
285              firmwareRegister.size, "TYPE", firmwareRegister.type);
286     }
287 }
288 
getConfigSubTree(Config & config,const std::string & interfaceName,const InventoryData & deviceConfig)289 static auto getConfigSubTree(Config& config, const std::string& interfaceName,
290                              const InventoryData& deviceConfig) -> void
291 {
292     std::string firmwareRegistersInterface =
293         interfaceName + ".FirmwareRegisters";
294     std::string sensorRegistersInterface = interfaceName + ".SensorRegisters";
295     std::string statusBitsInterface = interfaceName + ".StatusBits";
296 
297     for (const auto& [curInterface, interfaceConfig] : deviceConfig)
298     {
299         if (curInterface == interfaceName)
300         {
301             processDeviceInterface(config, interfaceConfig);
302         }
303         else if (curInterface.starts_with(sensorRegistersInterface))
304         {
305             processSensorRegistersInterface(config, interfaceConfig);
306         }
307         else if (curInterface.starts_with(statusBitsInterface))
308         {
309             processStatusBitsInterface(config, interfaceConfig);
310         }
311         else if (curInterface.starts_with(firmwareRegistersInterface))
312         {
313             processFirmwareRegistersInterface(config, interfaceConfig);
314         }
315     }
316 }
317 
updateBaseConfig(sdbusplus::async::context & ctx,const sdbusplus::message::object_path & objectPath,const std::string & interfaceName,Config & config)318 auto updateBaseConfig(sdbusplus::async::context& ctx,
319                       const sdbusplus::message::object_path& objectPath,
320                       const std::string& interfaceName, Config& config)
321     -> sdbusplus::async::task<bool>
322 {
323     config.inventoryPath = objectPath.parent_path();
324 
325     using InventoryIntf =
326         sdbusplus::client::xyz::openbmc_project::inventory::Item<>;
327 
328     constexpr auto entityManager =
329         sdbusplus::async::proxy()
330             .service(entity_manager::EntityManagerInterface::serviceName)
331             .path(InventoryIntf::namespace_path)
332             .interface("org.freedesktop.DBus.ObjectManager");
333 
334     for (const auto& [path, deviceConfig] :
335          co_await entityManager.call<ManagedObjectType>(ctx,
336                                                         "GetManagedObjects"))
337     {
338         if (path.str != objectPath.str)
339         {
340             debug("Skipping device {PATH}", "PATH", path.str);
341             continue;
342         }
343         debug("Processing device {PATH}", "PATH", path.str);
344 
345         try
346         {
347             getConfigSubTree(config, interfaceName, deviceConfig);
348         }
349         catch (std::exception& e)
350         {
351             error("Failed to process device {PATH} with {ERROR}", "PATH",
352                   path.str, "ERROR", e);
353             co_return false;
354         }
355     }
356 
357     printConfig(config);
358 
359     co_return true;
360 }
361 
362 } // namespace phosphor::modbus::rtu::device::config
363