blob: 83754b1b5ba105e8aa5ac43d5e598e8efd1c8566 [file] [log] [blame]
/**
* 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