#include "base_config.hpp" #include "common/entity_manager_interface.hpp" #include #include #include #include namespace phosphor::modbus::rtu::device::config { PHOSPHOR_LOG2_USING; using BasicVariantType = std::variant, std::vector, std::string, int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>; using InventoryBaseConfigMap = std::flat_map; using InventoryData = std::flat_map; using ManagedObjectType = std::flat_map; static constexpr std::array, 3> validParities = { {{"Odd", Parity::odd}, {"Even", Parity::even}, {"None", Parity::none}}}; template auto getValue(const InventoryBaseConfigMap& configMap, const std::string& key, const std::string& contextName) -> T { auto iter = configMap.find(key); if (iter == configMap.end()) { throw std::runtime_error( "Missing property " + key + " for " + contextName); } try { return std::get(iter->second); } catch (const std::bad_variant_access& ex) { throw std::runtime_error( "Incorrect type for property " + key + " in " + contextName); } } static inline auto getDataParity(Config& config, const InventoryBaseConfigMap& configMap) -> void { auto receivedParity = getValue(configMap, "DataParity", config.name); for (const auto& [parityStr, parity] : validParities) { if (parityStr == receivedParity) { config.parity = parity; break; } } if (config.parity == Parity::unknown) { throw std::runtime_error( "Invalid parity " + receivedParity + " for " + config.name); } } static auto processDeviceInterface(Config& config, const InventoryBaseConfigMap& configMap) -> void { debug("Processing device config"); config.name = getValue(configMap, "Name", config.name); std::replace(config.name.begin(), config.name.end(), ' ', '_'); config.address = getValue(configMap, "Address", config.name); getDataParity(config, configMap); config.baudRate = getValue(configMap, "BaudRate", config.name); config.portName = getValue(configMap, "SerialPort", config.name); getValue(configMap, "Type", config.name); } static const auto sensorTypes = std::unordered_map< std::string_view, std::pair>{ {"FanTach", {SensorValueIntf::namespace_path::fan_tach, SensorValueIntf::Unit::RPMS}}, {"LiquidFlow", {SensorValueIntf::namespace_path::liquidflow, SensorValueIntf::Unit::LPM}}, {"Power", {SensorValueIntf::namespace_path::power, SensorValueIntf::Unit::Watts}}, {"Pressure", {SensorValueIntf::namespace_path::pressure, SensorValueIntf::Unit::Pascals}}, {"Temperature", {SensorValueIntf::namespace_path::temperature, SensorValueIntf::Unit::DegreesC}}, }; static const auto formatTypes = std::unordered_map{ {"Integer", SensorFormat::integer}, {"Float", SensorFormat::floatingPoint}}; static auto processRegisterType(SensorRegister& sensorRegister, const InventoryBaseConfigMap& configMap) -> void { auto registerType = getValue(configMap, "RegisterType", sensorRegister.name); auto type = sensorTypes.find(registerType); if (type == sensorTypes.end()) { throw std::runtime_error("Invalid RegisterType " + registerType + " for " + sensorRegister.name); } sensorRegister.pathSuffix = type->second.first; sensorRegister.unit = type->second.second; } static auto processRegisterFormat(SensorRegister& sensorRegister, const InventoryBaseConfigMap& configMap) -> void { auto format = getValue(configMap, "Format", sensorRegister.name); auto formatIter = formatTypes.find(format); if (formatIter == formatTypes.end()) { throw std::runtime_error( "Invalid Format " + format + " for " + sensorRegister.name); } sensorRegister.format = formatIter->second; } static auto processSensorRegistersInterface( Config& config, const InventoryBaseConfigMap& configMap) -> void { SensorRegister sensorRegister = {}; sensorRegister.name = getValue(configMap, "Name", config.name); processRegisterType(sensorRegister, configMap); sensorRegister.offset = getValue(configMap, "Address", config.name); sensorRegister.size = getValue(configMap, "Size", config.name); sensorRegister.precision = getValue(configMap, "Precision", config.name); sensorRegister.shift = getValue(configMap, "Shift", config.name); sensorRegister.scale = getValue(configMap, "Scale", config.name); sensorRegister.isSigned = getValue(configMap, "Signed", config.name); processRegisterFormat(sensorRegister, configMap); config.sensorRegisters.emplace_back(sensorRegister); } static const auto statusBitTypes = std::unordered_map{ {"ControllerFailure", StatusType::controllerFailure}, {"FanFailure", StatusType::fanFailure}, {"FilterFailure", StatusType::filterFailure}, {"PowerFault", StatusType::powerFault}, {"PumpFailure", StatusType::pumpFailure}, {"LeakDetectedCritical", StatusType::leakDetectedCritical}, {"LeakDetectedWarning", StatusType::leakDetectedWarning}, {"SensorFailure", StatusType::sensorFailure}, {"SensorReadingCritical", StatusType::sensorReadingCritical}, {"SensorReadingWarning", StatusType::sensorReadingWarning}}; static auto processStatusBitsInterface(Config& config, const InventoryBaseConfigMap& configMap) -> void { debug("Processing StatusBits for {NAME}", "NAME", config.name); StatusBit statusBit = {}; statusBit.name = getValue(configMap, "Name", config.name); auto type = getValue(configMap, "StatusType", config.name); auto typeIter = statusBitTypes.find(type); if (typeIter == statusBitTypes.end()) { throw std::runtime_error( "Invalid StatusType " + type + " for " + statusBit.name); } statusBit.type = typeIter->second; statusBit.bitPosition = getValue(configMap, "BitPosition", config.name); statusBit.value = getValue(configMap, "Value", config.name); auto address = getValue(configMap, "Address", config.name); config.statusRegisters[address].emplace_back(statusBit); } static const auto firmwareRegisterTypes = std::unordered_map{ {"Version", FirmwareRegisterType::version}, {"Update", FirmwareRegisterType::update}}; static auto processFirmwareRegistersInterface( Config& config, const InventoryBaseConfigMap& configMap) -> void { debug("Processing FirmwareRegisters for {NAME}", "NAME", config.name); FirmwareRegister firmwareRegister = {}; firmwareRegister.name = getValue(configMap, "Name", config.name); firmwareRegister.offset = getValue(configMap, "Address", firmwareRegister.name); firmwareRegister.size = getValue(configMap, "Size", firmwareRegister.name); auto registerType = getValue(configMap, "RegisterType", firmwareRegister.name); auto registerTypeIter = firmwareRegisterTypes.find(registerType); if (registerTypeIter == firmwareRegisterTypes.end()) { throw std::runtime_error("Invalid RegisterType " + registerType + " for " + firmwareRegister.name); } firmwareRegister.type = registerTypeIter->second; config.firmwareRegisters.emplace_back(firmwareRegister); } static auto printConfig(const Config& config) -> void { info("Device Config for {NAME}: {ADDRESS} {PORT} {INV_PATH}", "NAME", config.name, "ADDRESS", config.address, "PORT", config.portName, "INV_PATH", config.inventoryPath); for (const auto& sensorRegister : config.sensorRegisters) { info( "Sensor Register {NAME} {ADDRESS} {SIZE} {PRECISION} {SCALE} {SIGNED} {FORMAT} {UNIT} {PATH_SUFFIX}", "NAME", sensorRegister.name, "ADDRESS", sensorRegister.offset, "SIZE", sensorRegister.size, "PRECISION", sensorRegister.precision, "SCALE", sensorRegister.scale, "SIGNED", sensorRegister.isSigned, "FORMAT", sensorRegister.format, "UNIT", sensorRegister.unit, "PATH_SUFFIX", sensorRegister.pathSuffix); } for (const auto& [address, statusBits] : config.statusRegisters) { for (const auto& statusBit : statusBits) { info("Status Bit {NAME} {ADDRESS} {BIT_POSITION} {VALUE} {TYPE}", "NAME", statusBit.name, "ADDRESS", address, "BIT_POSITION", statusBit.bitPosition, "VALUE", statusBit.value, "TYPE", statusBit.type); } } for (const auto& firmwareRegister : config.firmwareRegisters) { info("Firmware Register {NAME} {ADDRESS} {SIZE} {TYPE}", "NAME", firmwareRegister.name, "ADDRESS", firmwareRegister.offset, "SIZE", firmwareRegister.size, "TYPE", firmwareRegister.type); } } static auto getConfigSubTree(Config& config, const std::string& interfaceName, const InventoryData& deviceConfig) -> void { std::string firmwareRegistersInterface = interfaceName + ".FirmwareRegisters"; std::string sensorRegistersInterface = interfaceName + ".SensorRegisters"; std::string statusBitsInterface = interfaceName + ".StatusBits"; for (const auto& [curInterface, interfaceConfig] : deviceConfig) { if (curInterface == interfaceName) { processDeviceInterface(config, interfaceConfig); } else if (curInterface.starts_with(sensorRegistersInterface)) { processSensorRegistersInterface(config, interfaceConfig); } else if (curInterface.starts_with(statusBitsInterface)) { processStatusBitsInterface(config, interfaceConfig); } else if (curInterface.starts_with(firmwareRegistersInterface)) { processFirmwareRegistersInterface(config, interfaceConfig); } } } auto updateBaseConfig(sdbusplus::async::context& ctx, const sdbusplus::message::object_path& objectPath, const std::string& interfaceName, Config& config) -> sdbusplus::async::task { config.inventoryPath = objectPath.parent_path(); using InventoryIntf = sdbusplus::client::xyz::openbmc_project::inventory::Item<>; constexpr auto entityManager = sdbusplus::async::proxy() .service(entity_manager::EntityManagerInterface::serviceName) .path(InventoryIntf::namespace_path) .interface("org.freedesktop.DBus.ObjectManager"); for (const auto& [path, deviceConfig] : co_await entityManager.call(ctx, "GetManagedObjects")) { if (path.str != objectPath.str) { debug("Skipping device {PATH}", "PATH", path.str); continue; } debug("Processing device {PATH}", "PATH", path.str); try { getConfigSubTree(config, interfaceName, deviceConfig); } catch (std::exception& e) { error("Failed to process device {PATH} with {ERROR}", "PATH", path.str, "ERROR", e); co_return false; } } printConfig(config); co_return true; } } // namespace phosphor::modbus::rtu::device::config