| /** |
| * Copyright © 2024 IBM Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "config_file_parser.hpp" |
| |
| #include "config_file_parser_error.hpp" |
| |
| #include <exception> |
| #include <fstream> |
| #include <optional> |
| |
| using json = nlohmann::json; |
| namespace fs = std::filesystem; |
| |
| namespace phosphor::power::sequencer::config_file_parser |
| { |
| |
| const std::filesystem::path standardConfigFileDirectory{ |
| "/usr/share/phosphor-power-sequencer"}; |
| |
| std::filesystem::path |
| find(const std::vector<std::string>& compatibleSystemTypes, |
| const std::filesystem::path& configFileDir) |
| { |
| fs::path pathName, possiblePath; |
| std::string fileName; |
| |
| for (const std::string& systemType : compatibleSystemTypes) |
| { |
| // Look for file name that is entire system type + ".json" |
| // Example: com.acme.Hardware.Chassis.Model.MegaServer.json |
| fileName = systemType + ".json"; |
| possiblePath = configFileDir / fileName; |
| if (fs::is_regular_file(possiblePath)) |
| { |
| pathName = possiblePath; |
| break; |
| } |
| |
| // Look for file name that is last node of system type + ".json" |
| // Example: MegaServer.json |
| std::string::size_type pos = systemType.rfind('.'); |
| if ((pos != std::string::npos) && ((systemType.size() - pos) > 1)) |
| { |
| fileName = systemType.substr(pos + 1) + ".json"; |
| possiblePath = configFileDir / fileName; |
| if (fs::is_regular_file(possiblePath)) |
| { |
| pathName = possiblePath; |
| break; |
| } |
| } |
| } |
| |
| return pathName; |
| } |
| |
| std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName) |
| { |
| try |
| { |
| // Use standard JSON parser to create tree of JSON elements |
| std::ifstream file{pathName}; |
| json rootElement = json::parse(file); |
| |
| // Parse tree of JSON elements and return corresponding C++ objects |
| return internal::parseRoot(rootElement); |
| } |
| catch (const std::exception& e) |
| { |
| throw ConfigFileParserError{pathName, e.what()}; |
| } |
| } |
| |
| namespace internal |
| { |
| |
| GPIO parseGPIO(const json& element) |
| { |
| verifyIsObject(element); |
| unsigned int propertyCount{0}; |
| |
| // Required line property |
| const json& lineElement = getRequiredProperty(element, "line"); |
| unsigned int line = parseUnsignedInteger(lineElement); |
| ++propertyCount; |
| |
| // Optional active_low property |
| bool activeLow{false}; |
| auto activeLowIt = element.find("active_low"); |
| if (activeLowIt != element.end()) |
| { |
| activeLow = parseBoolean(*activeLowIt); |
| ++propertyCount; |
| } |
| |
| // Verify no invalid properties exist |
| verifyPropertyCount(element, propertyCount); |
| |
| return GPIO(line, activeLow); |
| } |
| |
| std::unique_ptr<Rail> parseRail(const json& element) |
| { |
| verifyIsObject(element); |
| unsigned int propertyCount{0}; |
| |
| // Required name property |
| const json& nameElement = getRequiredProperty(element, "name"); |
| std::string name = parseString(nameElement); |
| ++propertyCount; |
| |
| // Optional presence property |
| std::optional<std::string> presence{}; |
| auto presenceIt = element.find("presence"); |
| if (presenceIt != element.end()) |
| { |
| presence = parseString(*presenceIt); |
| ++propertyCount; |
| } |
| |
| // Optional page property |
| std::optional<uint8_t> page{}; |
| auto pageIt = element.find("page"); |
| if (pageIt != element.end()) |
| { |
| page = parseUint8(*pageIt); |
| ++propertyCount; |
| } |
| |
| // Optional is_power_supply_rail property |
| bool isPowerSupplyRail{false}; |
| auto isPowerSupplyRailIt = element.find("is_power_supply_rail"); |
| if (isPowerSupplyRailIt != element.end()) |
| { |
| isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt); |
| ++propertyCount; |
| } |
| |
| // Optional check_status_vout property |
| bool checkStatusVout{false}; |
| auto checkStatusVoutIt = element.find("check_status_vout"); |
| if (checkStatusVoutIt != element.end()) |
| { |
| checkStatusVout = parseBoolean(*checkStatusVoutIt); |
| ++propertyCount; |
| } |
| |
| // Optional compare_voltage_to_limit property |
| bool compareVoltageToLimit{false}; |
| auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit"); |
| if (compareVoltageToLimitIt != element.end()) |
| { |
| compareVoltageToLimit = parseBoolean(*compareVoltageToLimitIt); |
| ++propertyCount; |
| } |
| |
| // Optional gpio property |
| std::optional<GPIO> gpio{}; |
| auto gpioIt = element.find("gpio"); |
| if (gpioIt != element.end()) |
| { |
| gpio = parseGPIO(*gpioIt); |
| ++propertyCount; |
| } |
| |
| // If check_status_vout or compare_voltage_to_limit property is true, the |
| // page property is required; verify page was specified |
| if ((checkStatusVout || compareVoltageToLimit) && !page.has_value()) |
| { |
| throw std::invalid_argument{"Required property missing: page"}; |
| } |
| |
| // Verify no invalid properties exist |
| verifyPropertyCount(element, propertyCount); |
| |
| return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, |
| checkStatusVout, compareVoltageToLimit, gpio); |
| } |
| |
| std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element) |
| { |
| verifyIsArray(element); |
| std::vector<std::unique_ptr<Rail>> rails; |
| for (auto& railElement : element) |
| { |
| rails.emplace_back(parseRail(railElement)); |
| } |
| return rails; |
| } |
| |
| std::vector<std::unique_ptr<Rail>> parseRoot(const json& element) |
| { |
| verifyIsObject(element); |
| unsigned int propertyCount{0}; |
| |
| // Required rails property |
| const json& railsElement = getRequiredProperty(element, "rails"); |
| std::vector<std::unique_ptr<Rail>> rails = parseRailArray(railsElement); |
| ++propertyCount; |
| |
| // Verify no invalid properties exist |
| verifyPropertyCount(element, propertyCount); |
| |
| return rails; |
| } |
| |
| } // namespace internal |
| |
| } // namespace phosphor::power::sequencer::config_file_parser |