OCPP模块配置处理流程图
flowchart TD
A[CMake构建阶段] --> B[指定CONFIG参数]
B --> C[读取配置文件]
D[manifest.yaml] --> E[解析模块定义]
E --> F[生成加载器代码]
F --> G[编译模块]
C --> H[运行时配置处理]
H --> I[检查user-config]
I --> J[合并配置]
J --> K[创建Conf对象]
K --> L[初始化OCPP模块]
subgraph 构建时
A
B
C
D
E
F
G
end
subgraph 运行时
H
I
J
K
L
end
构建时配置文件选择
CMake通过 cmake/config-run-script.cmake 中的 generate_config_run_script 函数指定使用的配置文件:
function(generate_config_run_script)
# ...
if (NOT OPTNS_CONFIG)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} requires CONFIG parameter for the config name")
endif()
set(CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/config-${OPTNS_CONFIG}.yaml")
# ...
set(SCRIPT_OUTPUT_FILE "${SCRIPT_OUTPUT_PATH}/run-${OPTNS_CONFIG}.sh")
# ...- 首先接收一个CONFIG参数
- 然后构建配置文件路径:
set(CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/config-${OPTNS_CONFIG}.yaml") - 这表明EVerest使用命名约定来选择配置文件。配置文件名称格式为config-{配置名}.yaml
系统遵循命名约定,使用config-{配置名}.yaml格式的配置文件,例如:
config-hil.yaml
config-sil.yaml
config-sil-ocpp.yaml在构建过程中,CMake会为每个配置生成一个对应的运行脚本(如run-sil.sh或run-hil.sh)。运行时,用户通过执行特定的脚本来选择使用哪个配置文件:
# 运行使用sil配置的EVerest
./run-sil.sh
# 运行使用hil配置的EVerest
./run-hil.sh这里的sil是Software-in-the-Loop的意思。
配置文件覆盖机制
系统会检查user-config目录中是否存在同名配置文件。如果存在,将合并这两个配置:
user-config/config-sil-ocpp.yaml 中的设置会覆盖 config-sil-ocpp.yaml 中的同名设置这使得在不修改主配置文件的情况下自定义配置。
manifest.yaml处理阶段
EVerest框架首先处理每个模块的 manifest.yaml 文件,这是在CMake构建阶段完成的。
add_custom_command(
OUTPUT
${MODULE_LOADER_DIR}/ld-ev.hpp
${MODULE_LOADER_DIR}/ld-ev.cpp
COMMAND
${EV_CLI} module generate-loader
--disable-clang-format
--schemas-dir "$<TARGET_PROPERTY:generate_cpp_files,EVEREST_SCHEMA_DIR>"
--output-dir ${GENERATED_MODULE_DIR}
${RELATIVE_MODULE_DIR}
DEPENDS
${MODULE_PATH}/manifest.yaml
WORKING_DIRECTORY
${PROJECT_SOURCE_DIR}
COMMENT
"Generating ld-ev for module ${MODULE_NAME}"
)上面的代码在 everest-generate.cmake 文件中,他是核心生成脚本。这个命令会根据
manifest.yaml 文件生成两个关键文件:
- ld-ev.hpp - 加载器头文件
- ld-ev.cpp - 加载器实现文件
在模块的manifest.yaml可以看到其支持的配置项:
config:
ChargePointConfigPath:
description: Path to the configuration file
type: string
default: ocpp-config.json
MessageLogPath:
description: Path to folder where logs of all OCPP messages get written to
type: string
default: /tmp/everest_ocpp_logs
# 其他配置项...这些配置项会在构建时被解析,并在生成的加载器代码中使用。
生成的加载器代码结构
生成的 ld-ev.cpp 包含读取配置并创建Conf对象的代码。加载器会:
- 解析模块在配置文件中的
config_module和config_implementation部分 - 创建相应的配置结构体对象
- 将配置值填充到这些结构体中
OCPP模块配置文件示例
从 config-sil-ocpp.yaml 中可以看到OCPP模块的配置
ocpp:
module: OCPP
config_module:
ChargePointConfigPath: config-docker.json
connections:
evse_manager:
- module_id: evse_manager_1
implementation_id: evse
security:
- module_id: evse_security
implementation_id: mainConf对象创建过程
以 ocpp_1_6_charge_pointImpl.hpp 为例,这里定义了一个Conf结构体:
struct Conf {};
class ocpp_1_6_charge_pointImpl : public ocpp_1_6_charge_pointImplBase {
public:
ocpp_1_6_charge_pointImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<OCPP>& mod, Conf& config) :
ocpp_1_6_charge_pointImplBase(ev, "main"), mod(mod), config(config){};创建Conf对象的具体步骤:
- 加载器在运行时读取配置文件(如config-sil-ocpp.yaml)
- 解析config_module和每个接口实现的config_implementation部分
- 创建相应的Conf结构体实例
- 使用YAML解析库将配置值转换为C++类型并填充到Conf对象中
- 在模块初始化时,将这个Conf对象传递给相应的实现类构造函数
配置处理的代码生成
EVerest通过自动生成的代码处理配置,基本流程是:
// 在ld-ev.cpp中生成的代码示例(简化版)
void loadModule(framework::ModuleLoader& loader) {
// 读取配置
auto module_config = loader.getConfig();
// 创建模块级配置对象
ModuleConfig config;
if (module_config.contains("config_module")) {
auto& cfg = module_config["config_module"];
// 填充配置
if (cfg.contains("ChargePointConfigPath"))
config.ChargePointConfigPath = cfg["ChargePointConfigPath"].get<std::string>();
}
// 创建实现级配置对象
main::Conf main_config;
if (module_config.contains("config_implementation") &&
module_config["config_implementation"].contains("main")) {
auto& impl_cfg = module_config["config_implementation"]["main"];
// 填充实现级配置
}
// 创建模块实例
auto module = std::make_shared<OCPP>(loader.getModuleInfo(), config);
// 注册实现
module->registerImpl<main::ocpp_1_6_charge_pointImpl>("main", main_config);
}运行时模块实例化
当EVerest框架启动时:
- 加载配置文件(如config-sil-ocpp.yaml)
- 为每个配置的模块调用相应的加载器函数
- 加载器创建模块实例和所有配置的实现
- 连接模块间的接口依赖关系