* -------------------------------------------------------------------------
* This file is part of the Vision SDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* Vision SDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: Obtains key-value pairs in a file..
* Author: MindX SDK
* Create: 2020
* History: NA
*/
#include "MxBase/ConfigUtil/ConfigUtil.h"
#include "MxBase/Utils/StringUtils.h"
#include "MxBase/Utils/FileUtils.h"
namespace MxBase {
const char COMMENT_CHARACTER = '#';
const long MAX_FILE_SIZE = 104857600;
const int MAX_FILE_LINES = 100000;
const int MAX_LINE_COL = 10000;
* @description: Constructor
* @param {type}
* @return: void
*/
ConfigData::ConfigData()
{
saveBuf_.str("");
}
* @description: Copy constructor
* @param {type}
* @return: void
*/
ConfigData::ConfigData(const ConfigData &other)
{
*this = other;
}
* @description: Assignment constructor
* @param {type}
* @return: this ConfigData itself
*/
ConfigData &ConfigData::operator = (const ConfigData &other)
{
saveBuf_.str("");
cfgFile_ = other.cfgFile_;
cfgJson_ = other.cfgJson_;
labelVec_ = other.labelVec_;
return *this;
}
* @description: Set value by key
* @param {type}
* @return: void
*/
APP_ERROR ConfigData::SetJsonValue(const std::string &key, const std::string &value, int pos)
{
if (MxBase::StringUtils::HasInvalidChar(key) || MxBase::StringUtils::HasInvalidChar(value)) {
LogError << "SetJsonValue: the key or value contains invalid char, please check."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (key.empty() || pos < -1) {
LogError << "The key [" << key << "] cannot be empty and the position [" << pos << "] cannot be less than -1."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (pos == -1) {
cfgJson_[key] = value;
} else {
cfgJson_[pos][key] = value;
}
return APP_ERR_OK;
}
* @description: Get json data
* @param {type}
* @return: json
*/
std::string ConfigData::GetCfgJson()
{
return cfgJson_.dump();
}
* @description: Init file
* @param {type}
* @return: void
*/
APP_ERROR ConfigData::InitFile(std::ifstream &inFile)
{
char tmpLine[MAX_LINE_COL] = { 0 };
std::string line;
std::string newLine;
std::string::size_type startPos;
std::string::size_type endPos;
std::string::size_type pos;
int absoluteLineNum = 0;
while (!inFile.eof()) {
inFile.getline(tmpLine, MAX_LINE_COL);
line = tmpLine;
if (inFile.rdstate() == std::ios_base::failbit) {
LogError << "Reading config file error. The number of char in line has exceeded, expected less than "
<< MAX_LINE_COL << "." << GetErrorInfo(APP_ERR_COMM_OPEN_FAIL);
return APP_ERR_COMM_OPEN_FAIL;
}
if (absoluteLineNum > MAX_FILE_LINES) {
LogError << "Reading config file error. The line num should be less than or equal to " << MAX_FILE_LINES
<< "." << GetErrorInfo(APP_ERR_COMM_OPEN_FAIL);
return APP_ERR_COMM_OPEN_FAIL;
}
absoluteLineNum++;
if (line.empty()) {
continue;
}
startPos = 0;
endPos = line.size() - 1;
pos = line.find(COMMENT_CHARACTER);
if (pos != std::string::npos) {
if (pos == 0) {
continue;
}
endPos = pos - 1;
}
newLine = line.substr(startPos, (endPos - startPos) + 1);
pos = newLine.find('=');
if (pos == std::string::npos) {
continue;
}
std::string na = newLine.substr(0, pos);
StringUtils::Trim(na);
std::string value = newLine.substr(pos + 1, endPos + 1 - (pos + 1));
StringUtils::Trim(value);
cfgFile_.insert(std::make_pair(na, value));
}
return APP_ERR_OK;
}
* @description: Init json
* @param {type}
* @return: void
*/
APP_ERROR ConfigData::InitJson(std::ifstream &inFile)
{
try {
cfgJson_ = nlohmann::json::parse(inFile);
} catch (const nlohmann::json::exception& e) {
LogError << "Parse Json file has invalid char." << GetErrorInfo(APP_ERR_COMM_OPEN_FAIL);
return APP_ERR_COMM_OPEN_FAIL;
}
if (StringUtils::HasInvalidChar(cfgJson_)) {
LogError << "Config file has invalid char." << GetErrorInfo(APP_ERR_COMM_OPEN_FAIL);
return APP_ERR_COMM_OPEN_FAIL;
}
return APP_ERR_OK;
}
* @description: Init json content
* @param {type}
* @return: void
*/
APP_ERROR ConfigData::InitContent(const std::string &content)
{
if (content.empty()) {
LogError << "The input string is empty." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (content.size() > MAX_FILE_SIZE) {
LogError << "The input string size over max size limit." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
nlohmann::json tmp;
try {
tmp = nlohmann::json::parse(content);
} catch (const nlohmann::json::exception& e) {
LogError << "content is not a json object." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (!tmp.is_object()) {
LogError << "content is not a json object." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (StringUtils::HasInvalidChar(tmp)) {
LogError << "Config file has invalid char." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
for (auto propInfo = tmp.begin(); propInfo != tmp.end(); propInfo++) {
if (!propInfo.value().is_string()) {
LogError << "propInfo is not a json string." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
cfgFile_.insert(std::make_pair(propInfo.key(), std::string(propInfo.value())));
}
return APP_ERR_OK;
}
APP_ERROR ConfigData::LoadLabels(const std::string &labelPath)
{
std::string canonicalizedPath;
if (!FileUtils::RegularFilePath(labelPath, canonicalizedPath)) {
LogError << "Failed to get canonicalized file path." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (!FileUtils::IsFileValid(canonicalizedPath, false)) {
LogError << "Invalid filePath." << GetErrorInfo(APP_ERR_COMM_FAILURE);
return APP_ERR_COMM_FAILURE;
}
std::ifstream in;
in.open(canonicalizedPath, std::ios_base::in);
std::string strName;
if (in.fail()) {
LogError<< "Failed to open label file." << GetErrorInfo(APP_ERR_COMM_OPEN_FAIL) ;
return APP_ERR_COMM_OPEN_FAIL;
}
labelVec_.clear();
int lineNum = 0;
int absoluteLineNum = 0;
while (std::getline(in, strName)) {
if (absoluteLineNum > MAX_FILE_LINES) {
LogError << "Reading config error. The line num should be less than or equal to " << MAX_FILE_LINES
<< "." << GetErrorInfo(APP_ERR_COMM_OPEN_FAIL);
in.close();
return APP_ERR_COMM_OPEN_FAIL;
}
absoluteLineNum++;
if (lineNum == 0 && strName.find('#') <= 1 && strName.length() != 1) {
continue;
}
size_t eraseIndex = strName.find_last_not_of("\r\n\t");
if (eraseIndex != std::string::npos) {
strName.erase(eraseIndex + 1, strName.size() - eraseIndex);
}
labelVec_.push_back(strName);
lineNum++;
}
in.close();
return APP_ERR_OK;
}
std::string ConfigData::GetClassName(const size_t classId)
{
if (classId >= labelVec_.size()) {
LogWarn << "Failed to get classId(" << classId << ") label, size(" << labelVec_.size() << ").";
return "";
}
return labelVec_[classId];
}
* @description: Load content
* @param config file path or file content
* @param data download Data
* @param mode file format
* @return: ErrorCode
*/
APP_ERROR ConfigUtil::LoadConfiguration(const std::string &config, ConfigData &data, ConfigMode mode)
{
APP_ERROR ret = APP_ERR_OK;
if (mode == ConfigMode::CONFIGCONTENT) {
ret = data.InitContent(config);
if (ret != APP_ERR_OK) {
LogError << "Init content failed." << GetErrorInfo(ret);
}
return ret;
}
std::string canonicalizedPath;
if (!FileUtils::RegularFilePath(config, canonicalizedPath)) {
LogError << "Failed to get canonicalized file path." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (!FileUtils::IsFileValid(canonicalizedPath, false)) {
LogError << "Invalid filePath." << GetErrorInfo(APP_ERR_COMM_FAILURE);
return APP_ERR_COMM_FAILURE;
}
std::ifstream inFile(canonicalizedPath);
if (!inFile.is_open()) {
return APP_ERR_COMM_OPEN_FAIL;
}
switch (mode) {
case CONFIGJSON:
ret = data.InitJson(inFile);
break;
case CONFIGFILE:
ret = data.InitFile(inFile);
break;
default:
LogError << "Unsupported file format." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
inFile.close();
return APP_ERR_COMM_INVALID_PARAM;
}
if (ret != APP_ERR_OK) {
inFile.close();
LogError << "Init configuration file failed." << GetErrorInfo(ret);
return ret;
}
inFile.close();
return ret;
}
}