| Jagpal Singh Gill | e92aba4 | 2025-10-16 00:00:13 -0700 | [diff] [blame] | 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> |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 |