项目依赖

阅读CMakeLists.txt文件可以知道,项目EVerest依赖于多个组件:

  1. everest-cmake - 项目的构建系统工具
  2. Boost库 - 使用了filesystem、program_options、system和thread组件
  3. EVerest-framework - 核心框架
  4. everest-sunspec - 太阳能相关组件
  5. everest-modbus - Modbus通信协议支持
  6. everest-ocpp - 开放充电点协议(Open Charge Point Protocol)支持
  7. everest-openv2g - 电动汽车到电网(V2G)通信支持
  8. PalSigslot - 信号槽库
  9. fsm - 有限状态机
  10. slac - 可能是Signal Level Attenuation Characterization的缩写,用于电力线通信
  11. pugixml - XML解析库

这些项目通过EDM管理,当EDM不使能时,cmake会去查找本地安装的路径。

架构图

graph LR
    A[EVerest Core] --> B[模块系统]
    A --> C[接口定义]
    A --> D[构建系统]
    
    B --> B1[通信模块]
    B --> B2[协议模块]
    B --> B3[硬件接入模块]
    B --> B4[能源管理模块]
    
    B1 --> B1a[SerialCommHub]
    B1 --> B1b[SLAC]
    
    B2 --> B2a[OCPP]
    B2 --> B2b[ISO15118]
    
    B3 --> B3a[GenericPowermeter]
    
    B2b --> B2b1[PyJosev-SECC]
    B2b --> B2b2[PyEvJosev-EVCC]
    
    C --> C1[YAML接口定义]
    C1 --> C1a[kvs.yaml]
    C1 --> C1b[energy.yaml]
    C1 --> C1c[auth.yaml]
    C1 --> C1d[slac.yaml]
    C1 --> C1e[power.yaml]
    
    D --> D1[CMake系统]
    D --> D2[ev-project-bootstrap]

核心组件解析

模块系统

EVerest采用模块化设计,每个模块专注于特定功能

GenericPowermeter

# 通过ModbusRTU协议连接和读取电表数据
# 支持AC和DC电表,通过配置文件描述寄存器映射
# 读取数据包括:能量、功率、电压、电流、频率等

SerialCommHub

// 串行通信中心,处理Modbus通信
response = modbus.txrx(target_device_id, tiny_modbus::FunctionCode::READ_MULTIPLE_HOLDING_REGISTERS,
                       first_register_address, num_registers_to_read);

OCPP模块

// 实现开放充电点协议
bool ocpp_1_6_charge_pointImpl::handle_restart() {
    std::lock_guard<std::mutex>(this->m);
    mod->charging_schedules_timer->interval(std::chrono::seconds(this->mod->config.PublishChargingScheduleIntervalS));
    bool success = mod->charge_point->restart();
    if (success) {
        this->mod->ocpp_stopped = false;
    }
    return success;
}

ISO15118实现

PyJosev - 充电站侧实现(SECC)

async def secc_handler_main_loop(module_config: dict):
    # 启动ISO 15118 SECC控制器
    config = Config()
    patch_josev_config(config, module_config)
    sim_evse_controller = await SimEVSEController.create()
    await SECCHandler(
        exi_codec=ExificientEXICodec(), evse_controller=sim_evse_controller, config=config
    ).start(config.iface)

PyEvJosev - 车辆侧实现(EVCC)

async def evcc_handler_main_loop(module_config: dict):
    # 启动ISO 15118 EVCC控制器
    iface = determine_network_interface(module_config['device'])
    evcc_config = EVCCConfig()
    patch_josev_config(evcc_config, module_config)
    await EVCCHandler(
        evcc_config=evcc_config,
        iface=iface,
        exi_codec=ExificientEXICodec(),
        ev_controller=SimEVController(evcc_config),
    ).start()

接口定义系统

EVerest使用YAML文件定义模块间接口,采用统一的格式定义命令和变量

# 接口示例 (kvs.yaml - 键值存储接口)
description: This interface defines a simple key-value-store interface
cmds:
  store:
    description: This command stores a value under a given key
    arguments:
      key:
        description: Key to store the value for
        type: string
        pattern: ^[A-Za-z0-9_.]*$

OCPP模块

以OCPP模块为例,学习EVerest-core的构建到执行的过程。

架构流程图

graph TD
    A[OCPP模块初始化] --> B[模块构造与依赖注入]
    B --> C[init调用]
    C --> D[ready调用]
    D --> E[启动ChargePoint服务]
    
    E --> F1[处理OCPP核心功能]
    E --> F2[提供令牌验证]
    E --> F3[提供令牌授权]
    
    F1 --> G1[通信控制]
    F1 --> G2[配置管理]
    F1 --> G3[数据传输]
    F1 --> G4[安全事件]

    G1 --> H1[启动/停止连接]
    G1 --> H2[重新连接]
    
    G2 --> I1[获取配置]
    G2 --> I2[设置配置]
    G2 --> I3[监控配置变更]
    
    G3 --> J1[DataTransfer请求/响应]
    
    G4 --> K1[安全事件通知]
    
    subgraph 定时任务
    L1[充电计划定时器]
    end
    
    E --> L1

核心组件解析

模块结构与初始化

依赖注入机制

OCPP模块的初始化过程包括 构造函数中的依赖注入和init/ready方法:

// OCPP.hpp - 模块构造函数
OCPP(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider,
     std::unique_ptr<ocpp_1_6_charge_pointImplBase> p_main,
     std::unique_ptr<auth_token_validatorImplBase> p_auth_validator,
     std::unique_ptr<auth_token_providerImplBase> p_auth_provider,
     std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager,
     std::vector<std::unique_ptr<external_energy_limitsIntf>> r_connector_zero_sink,
     std::unique_ptr<reservationIntf> r_reservation, std::unique_ptr<authIntf> r_auth,
     std::unique_ptr<systemIntf> r_system, std::unique_ptr<evse_securityIntf> r_security, 
     Conf& config) :
    ModuleBase(info),
    mqtt(mqtt_provider),
    p_main(std::move(p_main)),
    p_auth_validator(std::move(p_auth_validator)),
    p_auth_provider(std::move(p_auth_provider)),
    r_evse_manager(std::move(r_evse_manager)),
    r_connector_zero_sink(std::move(r_connector_zero_sink)),
    r_reservation(std::move(r_reservation)),
    r_auth(std::move(r_auth)),
    r_system(std::move(r_system)),
    r_security(std::move(r_security)),
    config(config){};

这个构造函数显示该模块的复杂性,它需要多个接口交互,包括:

  • 提供的接口: OCPP充电点、认证令牌验证、认证令牌提供
  • 依赖的接口: EVSE管理器、能源限制、预约、认证、系统和安全

这种注入方式遵循依赖倒置原则,通过接口而非具体实现进行交互,提高了系统的解耦性和可测试性。

初始化流程

OCPP模块的初始化分为两个阶段

初始化阶段 (init)
  • 创建ChargePoint实例
  • 配置数据库路径
  • 设置安全参数
  • 建立MQTT连接
  • 初始化定时器
就绪阶段 (ready)
  • 启动WebSocket连接
  • 设置充电计划定时器
  • 注册回调函数
  • 订阅相关主题
初始化代码示例
void OCPP::init() {
    // 配置文件路径处理
    ocpp_share_path = std::filesystem::path(this->info.paths.share);
    
    // 创建ChargePoint核心对象
    charge_point = std::make_unique<ocpp::v16::ChargePoint>(
        /* 配置参数 */
    );
}
 
void OCPP::ready() {
	// 设置充电计划定时器
    charging_schedules_timer = std::make_unique<Everest::SteadyTimer>(
        [this](){
            // 定时发布充电计划
            this->publish_charging_schedules(
                this->charge_point->get_composite_schedule(
                    std::chrono::seconds(this->config.PublishChargingScheduleDurationS)
                )
            );
        }
    );
}

ChargePoint核心组件

ChargePoint是OCPP模块的核心组件,负责所有OCPP通信:

  • WebSocket管理:建立和维护与CSMS的WebSocket连接
  • 消息序列化:将OCPP消息转换为JSON格式
  • 会话管理:处理认证和会话状态
  • 事务处理:管理充电事务的开始、进行和结束
  • 配置管理:处理配置请求和更新

接口实现

ocpp_1_6_charge_pointImpl

ocpp_1_6_charge_pointImpl类实现了OCPP 1.6协议的主要功能:

bool ocpp_1_6_charge_pointImpl::handle_stop() {
    std::lock_guard<std::mutex>(this->m);
    mod->charging_schedules_timer->stop();
    bool success = mod->charge_point->stop();
    if (success) {
        this->mod->ocpp_stopped = true;
    }
    return success;
}
 
bool ocpp_1_6_charge_pointImpl::handle_restart() {
    std::lock_guard<std::mutex>(this->m);
    mod->charging_schedules_timer->interval(std::chrono::seconds(this->mod->config.PublishChargingScheduleIntervalS));
    bool success = mod->charge_point->restart();
    if (success) {
        this->mod->ocpp_stopped = false;
    }
    return success;
}

数据转换函数示例:

types::ocpp::KeyValue to_everest(const ocpp::v16::KeyValue& key_value) {
    types::ocpp::KeyValue _key_value;
    _key_value.key = key_value.key.get();
    _key_value.read_only = key_value.readonly;
    if (key_value.value.has_value()) {
        _key_value.value = key_value.value.value().get();
    }
    return _key_value;
}

auth_token_validatorImpl

实现令牌验证功能,将验证请求传递给OCPP后端:

auth_token_validatorImpl::handle_validate_token(types::authorization::ProvidedIdToken& provided_token) {
    if (provided_token.authorization_type == types::authorization::AuthorizationType::PlugAndCharge) {
        return validate_pnc_request(provided_token);
    } else {
        return validate_standard_request(provided_token);
    }
};

auth_token_providerImpl

提供令牌信息给其他模块:

// 伪代码
void auth_token_providerImpl::handle_get_token_info(const std::string& id_token, types::authorization::TokenInfo& token_info) {
    auto response = mod->charge_point->get_token_info(id_token);
    token_info.auth_status = convert_auth_status(response.status);
    // 填充其他令牌信息
}

这里为什么没有具体的实现代码?

配置管理功能

模块支持全面的配置管理,包括获取、设置和监控配置:

// 获取配置
types::ocpp::GetConfigurationResponse ocpp_1_6_charge_pointImpl::handle_get_configuration_key(Array& keys) {
    ocpp::v16::GetConfigurationRequest request;
    std::vector<ocpp::CiString<50>> _keys;
    for (const auto& key : keys) {
        _keys.push_back(key);
    }
    request.key = _keys;
 
    const auto response = this->mod->charge_point->get_configuration_key(request);
    return to_everest(response);
}
 
// 监控配置变更
void ocpp_1_6_charge_pointImpl::handle_monitor_configuration_keys(Array& keys) {
    for (const auto& key : keys) {
        this->mod->charge_point->register_configuration_key_changed_callback(
            key,
            [this](const ocpp::v16::KeyValue key_value) { 
                this->publish_configuration_key(to_everest(key_value)); 
            });
    }
}

安全机制

OCPP模块通过EvseSecurity类实现OCPP安全功能:

class evse_securityImpl : public evse_securityImplBase {
public:
    evse_securityImpl() = delete;
    evse_securityImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<EvseSecurity>& mod, Conf& config) :
        evse_securityImplBase(ev, "main"), mod(mod), config(config){};
protected:
//伪代码
    EvseSecurity(evse_securityIntf& r_security);
    
    // 证书安装
    ocpp::InstallCertificateResult install_ca_certificate(const std::string& certificate,
                                                         const ocpp::CaCertificateType& certificate_type) override;
    
    // 证书删除
    ocpp::DeleteCertificateResult 
    delete_certificate(const ocpp::CertificateHashDataType& certificate_hash_data) override;
    
    // 证书验证
    ocpp::InstallCertificateResult verify_certificate(const std::string& certificate_chain,
                                                     const ocpp::CertificateSigningUseEnum& certificate_type) override;
    
    // 更多安全操作...
};

定时任务

模块使用定时器定期执行任务:

// 初始化定时器
charging_schedules_timer = std::make_unique<Everest::SteadyTimer>();
 
// 设置定时任务
charging_schedules_timer->interval(std::chrono::seconds(config.PublishChargingScheduleIntervalS));
charging_schedules_timer->start();

模块执行流程

启动流程

sequenceDiagram
    participant E as EVerest框架
    participant O as OCPP模块
    participant CP as ChargePoint核心
    participant EM as EVSE管理器
    participant S as 安全模块
    
    E->>O: 构造
    O->>O: 依赖注入
    E->>O: init()
    O->>O: 初始化EVSE Ready Map
    O->>EM: 订阅EVSE Ready事件
    O->>O: 加载配置文件
    O->>O: 创建ChargePoint实例
    O->>S: 创建EvseSecurity包装
    E->>O: ready()
    O->>O: 初始化EVSE连接器映射
    O->>CP: 注册多个回调函数
    O->>CP: 启动WebSocket连接
    O->>O: 设置充电计划定时器
  1. 模块构造 - 依赖注入
  2. init() - 初始化内部状态
  3. ready() - 启动服务
  4. 建立WebSocket连接
  5. 注册回调函数
  6. 开始定时任务

EVSE就绪映射初始化

模块使用 evse_ready_map 追踪所有EVSE的就绪状态:

void OCPP::init_evse_ready_map() {
    std::lock_guard<std::mutex> lk(this->evse_ready_mutex);
    for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
        this->evse_ready_map[evse_id] = false;
    }
}

在init方法中,模块订阅每个EVSE的就绪状态:

for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
    this->r_evse_manager.at(evse_id - 1)->subscribe_ready([this, evse_id](bool ready) {
        std::lock_guard<std::mutex> lk(this->evse_ready_mutex);
        if (ready) {
            this->evse_ready_map[evse_id] = true;
            this->evse_ready_cv.notify_one();
        }
    });
}

这种设计确保OCPP模块只有在所有EVSE都准备好后才会完全启动服务。

配置文件处理

OCPP模块处理两种配置文件:

  • 主配置文件:包含基本OCPP设置
  • 用户配置文件:包含用户自定义设置

OCPP模块配置文件处理流程里面详细写了配置文件的处理流程。

// 寻找并加载主配置文件
auto configured_config_path = fs::path(this->config.ChargePointConfigPath);
if (!fs::exists(configured_config_path) && configured_config_path.is_relative()) {
    configured_config_path = this->ocpp_share_path / configured_config_path;
}
 
// 解析JSON配置
std::ifstream ifs(config_path.c_str());
std::string config_file((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
auto json_config = json::parse(config_file);
json_config.at("Core").at("NumberOfConnectors") = this->r_evse_manager.size();
 
// 合并用户配置
if (fs::exists(user_config_path)) {
    std::ifstream ifs(user_config_path.c_str());
    std::string user_config_file((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
    const auto user_config = json::parse(user_config_file);
    json_config.merge_patch(user_config);
}

这种方法允许系统管理员通过两个配置文件分别维护基本设置和用户定制,增强了配置的灵活性。

EVSE-连接器映射建立

OCPP需要维护EVerest内部EVSE/连接器ID与OCPP协议ID的映射关系

void OCPP::init_evse_connector_map() {
    int32_t ocpp_connector_id = 1; // OCPP连接器ID
    int32_t evse_id = 1;           // EVerest EVSE管理器ID
    
    for (const auto& evse : this->r_evse_manager) {
        const auto _evse = evse->call_get_evse();
        std::map<int32_t, int32_t> connector_map; // 映射EVerest连接器ID到OCPP连接器ID
        
        // 检查EVSE ID是否连续
        if (_evse.id != evse_id) {
            throw std::runtime_error("Configured evse_id(s) must be starting with 1 counting upwards");
        }
        
        // 处理每个连接器
        for (const auto& connector : _evse.connectors) {
            connector_map[connector.id] = ocpp_connector_id;
            this->connector_evse_index_map[ocpp_connector_id] = evse_id - 1; // 索引对应r_evse_manager
            ocpp_connector_id++;
        }
        
        // 处理没有显式连接器的EVSE
        if (connector_map.size() == 0) {
            this->connector_evse_index_map[ocpp_connector_id] = evse_id - 1;
            connector_map[1] = ocpp_connector_id;
            ocpp_connector_id++;
        }
        
        this->evse_connector_map[_evse.id] = connector_map;
        evse_id++;
    }
}

这个映射是OCPP模块的核心数据结构之一,用于在OCPP连接器ID与EVerest内部ID之间转换,确保消息正确路由。

ChargePoint核心创建

在init方法结束时,模块创建了OCPP通信的核心组件

this->charge_point = std::make_unique<ocpp::v16::ChargePoint>(
    json_config.dump(),                         // 配置JSON
    this->ocpp_share_path,                      // 共享路径
    user_config_path,                           // 用户配置路径
    std::filesystem::path(this->config.DatabasePath),  // 数据库路径
    sql_init_path,                              // SQL初始化脚本路径
    std::filesystem::path(this->config.MessageLogPath),  // 消息日志路径
    std::make_shared<EvseSecurity>(*this->r_security));  // 安全组件

这一步创建了处理所有OCPP通信的核心对象,并为其提供了:

  • 配置数据
  • 持久化存储位置
  • 日志记录路径
  • 安全接口

EvseSecurity是一个适配器类,将EVerest安全接口转换为OCPP库需要的接口格式。

回调函数注册

在ready方法中,模块注册了多个回调函数,建立了OCPP事件与EVerest动作之间的桥梁

充电控制回调
// 暂停充电回调
this->charge_point->register_pause_charging_callback([this](int32_t connector) {
    if (this->connector_evse_index_map.count(connector)) {
        return this->r_evse_manager.at(this->connector_evse_index_map.at(connector))->call_pause_charging();
    } else {
        return false;
    }
});
 
// 恢复充电回调
this->charge_point->register_resume_charging_callback([this](int32_t connector) {
    if (this->connector_evse_index_map.count(connector)) {
        return this->r_evse_manager.at(this->connector_evse_index_map.at(connector))->call_resume_charging();
    } else {
        return false;
    }
});
 
// 停止交易回调
this->charge_point->register_stop_transaction_callback([this](int32_t connector, ocpp::v16::Reason reason) {
    if (this->connector_evse_index_map.count(connector)) {
        types::evse_manager::StopTransactionRequest req;
        req.reason = types::evse_manager::string_to_stop_transaction_reason(
            ocpp::v16::conversions::reason_to_string(reason));
        return this->r_evse_manager.at(this->connector_evse_index_map.at(connector))->call_stop_transaction(req);
    } else {
        return false;
    }
});

这些回调确保了OCPP服务端发送的控制命令能够传递到正确的EVSE管理器,并转换为相应的EVerest操作。

充电计划回调
this->charge_point->register_signal_set_charging_profiles_callback([this]() {
    // 当CSMS发送新的充电配置文件并被ChargePoint接受时执行
    EVLOG_info << "Received new Charging Schedules from CSMS";
    const auto charging_schedules =
        this->charge_point->get_all_composite_charging_schedules(this->config.PublishChargingScheduleDurationS);
    this->set_external_limits(charging_schedules);
    this->publish_charging_schedules(charging_schedules);
});

这个回调处理来自CSMS的充电计划变更,将其应用到能源限制系统并发布给其他模块。

系统级回调
// 重置允许检查
this->charge_point->register_is_reset_allowed_callback([this](ocpp::v16::ResetType type) {
    const auto reset_type = types::system::string_to_reset_type(ocpp::v16::conversions::reset_type_to_string(type));
    return this->r_system->call_is_reset_allowed(reset_type);
});
 
// 执行重置
this->charge_point->register_reset_callback([this](ocpp::v16::ResetType type) {
    const auto reset_type = types::system::string_to_reset_type(ocpp::v16::conversions::reset_type_to_string(type));
    this->r_system->call_reset(reset_type, false);
});

这些回调处理CSMS的重置请求,先检查是否允许重置,然后执行实际的重置操作。

连接状态回调
this->charge_point->register_connection_state_changed_callback(
    [this](bool is_connected) { this->p_main->publish_is_connected(is_connected); });

这个回调监视WebSocket连接状态,并通过MQTT发布状态变更,使其他组件能够了解OCPP连接状态。

定时器设置与调度

模块设置了一个定时器,定期发布充电计划

if (this->config.PublishChargingScheduleIntervalS > 0) {
    this->charging_schedules_timer->interval(std::chrono::seconds(this->config.PublishChargingScheduleIntervalS));
}

这个定时器确保充电站中的所有组件都能及时获取最新的充电计划信息,即使未发生变更。

OCPP消息处理流程

graph TD
    A[CSMS] -->|发送消息| B[ChargePoint]
    B -->|处理消息| C{消息类型}
    
    C -->|SetChargingProfile| D[调用充电计划回调]
    C -->|Reset| E[调用重置回调]
    C -->|RemoteStartTransaction| F[开始充电交易]
    C -->|RemoteStopTransaction| G[调用停止交易回调]
    
    D --> H[设置能源限制]
    D --> I[发布充电计划]
    
    E --> J[检查是否允许重置]
    J -->|允许| K[执行系统重置]
    
    F --> L[验证令牌]
    L -->|有效| M[启动充电]
    
    G --> N[停止充电]
    
    O[EVSE状态变更] --> P[发送StatusNotification]
    P --> A

当EVSE状态变更时,OCPP模块将状态变更报告给CSMS

void OCPP::handle_evse_state_change(int32_t evse_id, types::evse_manager::EVSEState state) {
    // 查找对应的OCPP连接器ID
    if (!this->evse_connector_map.count(evse_id)) {
        return;
    }
    
    // 转换状态
    ocpp::v16::ChargePointStatus ocpp_status = convert_evse_state_to_ocpp(state);
    
    // 对每个连接器发送状态通知
    for (const auto& [connector_id, ocpp_connector_id] : this->evse_connector_map.at(evse_id)) {
        this->charge_point->status_notification(ocpp_connector_id, ocpp_status);
    }
}

接收消息

WebSocket接收CSMS消息
JSON解析为OCPP对象

消息分发

根据消息类型调用对应处理函数
执行业务逻辑

响应生成

创建响应对象
序列化为JSON
通过WebSocket发送

状态更新

更新内部状态
发布相关事件

认证流程

  1. 接收认证令牌
  2. 发送Authorize.req消息到CSMS
  3. 接收Authorize.conf响应
  4. 转换OCPP认证状态为EVerest认证状态
  5. 返回认证结果

当收到充电请求时,OCPP模块验证授权令牌

bool auth_token_validatorImpl::handle_validate_token(const std::string& id_token,
                                                   types::authorization::TokenInfo& token_info_out) {
    // 利用OCPP验证令牌
    ocpp::v16::AuthorizeRequest request;
    request.idTag = id_token;
    
    auto response = mod->charge_point->authorize(request);
    
    // 转换结果为EVerest格式
    token_info_out = to_everest_token_info(response.idTagInfo);
    
    return response.idTagInfo.status == ocpp::v16::AuthorizationStatus::Accepted;
}

充电计划处理

  1. 定时器触发
  2. 请求最新充电计划
  3. 转换为EVerest格式
  4. 发布到MQTT
  5. 传递给能源限制接口

与其他模块交互

EVSE管理器交互

// 伪代码
void handle_evse_status_change(int32_t evse_id, evse_manager::EVSEState state) {
    // 转换状态
    ocpp::v16::ChargePointStatus cp_status = convert_evse_state(state);
    
    // 向CSMS发送状态变更
    charge_point->status_notification(evse_id, cp_status);
}

安全模块交互

// 初始化安全组件
auto evse_security = std::make_shared<EvseSecurity>(*r_security);
 
// 配置ChargePoint使用安全组件
charge_point_config.evse_security = evse_security;

EvseSecurity类是OCPP安全功能的适配器

EvseSecurity::EvseSecurity(evse_securityIntf& r_security) : r_security(r_security) {
}
 
ocpp::InstallCertificateResult EvseSecurity::install_ca_certificate(const std::string& certificate,
                                                                   const ocpp::CaCertificateType& certificate_type) {
    // 转换参数到EVerest格式
    auto ev_cert_type = conversions::from_ocpp(certificate_type);
    
    // 调用EVerest安全接口
    auto result = this->r_security->call_install_ca_certificate(certificate, ev_cert_type);
    
    // 转换结果回OCPP格式
    return conversions::to_ocpp(result);
}

这种适配器模式允许OCPP模块使用EVerest的安全基础设施,同时保持接口的一致性和代码的分离。

OCPPExtensionExample扩展

通过OCPPExtensionExample模式,可以扩展OCPP功能:

class OCPPExtensionExample : public Everest::ModuleBase {
public:
    OCPPExtensionExample(const ModuleInfo& info, 
                        std::unique_ptr<emptyImplBase> p_empty,
                        std::unique_ptr<ocpp_1_6_charge_pointIntf> r_ocpp, 
                        Conf& config);
                        
    void ready() {
        // 监控配置变更
        std::vector<std::string> keys_to_monitor = parse_keys(config.keys_to_monitor);
        r_ocpp->monitor_configuration_keys(keys_to_monitor);
        
        // 订阅变更通知
        r_ocpp->subscribe_configuration_key([this](types::ocpp::KeyValue key_value) {
            // 处理配置变更
        });
    }
};

OCPP 2.0.1

todo