diff --git a/cmake/ev-project-bootstrap.cmake b/cmake/ev-project-bootstrap.cmake index 0e37b9272..e9f5ce6db 100644 --- a/cmake/ev-project-bootstrap.cmake +++ b/cmake/ev-project-bootstrap.cmake @@ -1,6 +1,6 @@ set_property( GLOBAL - PROPERTY EVEREST_REQUIRED_EV_CLI_VERSION "0.3.0" + PROPERTY EVEREST_REQUIRED_EV_CLI_VERSION "0.4.0" ) # FIXME (aw): clean up this inclusion chain diff --git a/cmake/everest-generate.cmake b/cmake/everest-generate.cmake index ccfa78a73..bbfe81839 100644 --- a/cmake/everest-generate.cmake +++ b/cmake/everest-generate.cmake @@ -743,4 +743,4 @@ endfunction() set(EVEREST_EXCLUDE_MODULES "" CACHE STRING "A list of modules that will not be built") set(EVEREST_INCLUDE_MODULES "" CACHE STRING "A list of modules that will be built. If the list is empty, all modules will be built.") -option(EVEREST_EXCLUDE_CPP_MODULES "Exclude all C++ modules from the build" OFF) +option(EVEREST_EXCLUDE_CPP_MODULES "Exclude all C++ modules from the build" OFF) \ No newline at end of file diff --git a/config/config-sil-energy-management.yaml b/config/config-sil-energy-management.yaml index 199f8b5e8..f8bc6de4c 100644 --- a/config/config-sil-energy-management.yaml +++ b/config/config-sil-energy-management.yaml @@ -15,6 +15,9 @@ active_modules: supported_ISO15118_2: true evse_manager_1: module: EvseManager + mapping: + module: + evse: 1 config_module: connector_id: 1 evse_id: DE*PNX*E12345*1 @@ -43,6 +46,9 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager + mapping: + module: + evse: 2 config_module: connector_id: 2 evse_id: DE*PNX*E12345*2 @@ -159,25 +165,56 @@ active_modules: connections: price_information: [] energy_consumer: - - module_id: evse_manager_1 + - module_id: evse_manager_1_sink implementation_id: energy_grid - - module_id: evse_manager_2 + - module_id: evse_manager_2_sink implementation_id: energy_grid powermeter: - module_id: yeti_driver_1 implementation_id: powermeter + evse_manager_1_sink: + module: EnergyNode + mapping: + module: + evse: 1 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_1 + implementation_id: energy_grid + evse_manager_2_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_2 + implementation_id: energy_grid api: module: API connections: evse_manager: - module_id: evse_manager_1 implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse random_delay: - module_id: evse_manager_1 implementation_id: random_delay error_history: - module_id: error_history implementation_id: error_history + evse_energy_sink: + - module_id: evse_manager_1_sink + implementation_id: external_limits + - module_id: evse_manager_2_sink + implementation_id: external_limits error_history: module: ErrorHistory config_implementation: diff --git a/config/config-sil-ocpp-custom-extension.yaml b/config/config-sil-ocpp-custom-extension.yaml index 0a13a162e..bd18e3ab8 100644 --- a/config/config-sil-ocpp-custom-extension.yaml +++ b/config/config-sil-ocpp-custom-extension.yaml @@ -15,7 +15,9 @@ active_modules: supported_ISO15118_2: true evse_manager_1: module: EvseManager - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 evse_id: "1" @@ -25,6 +27,7 @@ active_modules: ac_hlc_enabled: false ac_hlc_use_5percent: false ac_enforce_hlc: false + external_ready_to_start_charging: true connections: bsp: - module_id: yeti_driver_1 @@ -40,7 +43,9 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -50,6 +55,7 @@ active_modules: ac_hlc_enabled: false ac_hlc_use_5percent: false ac_enforce_hlc: false + external_ready_to_start_charging: true connections: bsp: - module_id: yeti_driver_2 @@ -65,12 +71,16 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 slac: @@ -152,6 +162,13 @@ active_modules: data_transfer: - module_id: ocpp_extension implementation_id: data_transfer + evse_energy_sink: + - module_id: grid_connection_point + implementation_id: external_limits + - module_id: evse_manager_1_ocpp_sink + implementation_id: external_limits + - module_id: evse_manager_2_ocpp_sink + implementation_id: external_limits evse_security: module: EvseSecurity config_module: @@ -175,30 +192,94 @@ active_modules: energy_trunk: - module_id: grid_connection_point implementation_id: energy_grid - grid_connection_point: + evse_manager_1_ocpp_sink: module: EnergyNode + mapping: + module: + evse: 1 config_module: - fuse_limit_A: 40.0 + fuse_limit_A: 32.0 phase_count: 3 connections: - price_information: [] energy_consumer: - module_id: evse_manager_1 implementation_id: energy_grid + evse_manager_2_ocpp_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: - module_id: evse_manager_2 implementation_id: energy_grid + evse_manager_1_api_sink: + module: EnergyNode + mapping: + module: + evse: 1 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_1_ocpp_sink + implementation_id: energy_grid powermeter: - module_id: yeti_driver_1 implementation_id: powermeter + evse_manager_2_api_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_2_ocpp_sink + implementation_id: energy_grid + powermeter: + - module_id: yeti_driver_2 + implementation_id: powermeter + grid_connection_point: + module: EnergyNode + mapping: + module: + evse: 0 + config_module: + fuse_limit_A: 40.0 + phase_count: 3 + connections: + price_information: [] + energy_consumer: + - module_id: evse_manager_1_api_sink + implementation_id: energy_grid + - module_id: evse_manager_2_api_sink + implementation_id: energy_grid api: module: API connections: evse_manager: - module_id: evse_manager_1 implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse + ocpp: + - module_id: ocpp + implementation_id: ocpp_generic error_history: - module_id: error_history implementation_id: error_history + evse_energy_sink: + - module_id: evse_manager_1_api_sink + implementation_id: external_limits + - module_id: evse_manager_2_api_sink + implementation_id: external_limits error_history: module: ErrorHistory config_implementation: diff --git a/config/config-sil-ocpp-pnc.yaml b/config/config-sil-ocpp-pnc.yaml index 9771903a6..e5b092279 100644 --- a/config/config-sil-ocpp-pnc.yaml +++ b/config/config-sil-ocpp-pnc.yaml @@ -18,7 +18,9 @@ active_modules: is_cert_install_needed: true evse_manager_1: module: EvseManager - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 evse_id: "DE*PNX*00001" @@ -28,6 +30,8 @@ active_modules: ac_hlc_enabled: true ac_hlc_use_5percent: false ac_enforce_hlc: false + external_ready_to_start_charging: true + request_zero_power_in_idle: true connections: bsp: - module_id: yeti_driver_1 @@ -43,7 +47,9 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -53,6 +59,8 @@ active_modules: ac_hlc_enabled: false ac_hlc_use_5percent: false ac_enforce_hlc: false + external_ready_to_start_charging: true + request_zero_power_in_idle: true connections: bsp: - module_id: yeti_driver_2 @@ -68,12 +76,16 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 slac: @@ -157,6 +169,13 @@ active_modules: security: - module_id: evse_security implementation_id: main + evse_energy_sink: + - module_id: grid_connection_point + implementation_id: external_limits + - module_id: evse_manager_1_ocpp_sink + implementation_id: external_limits + - module_id: evse_manager_2_ocpp_sink + implementation_id: external_limits evse_security: module: EvseSecurity config_module: @@ -169,30 +188,94 @@ active_modules: energy_trunk: - module_id: grid_connection_point implementation_id: energy_grid - grid_connection_point: + evse_manager_1_ocpp_sink: module: EnergyNode + mapping: + module: + evse: 1 config_module: - fuse_limit_A: 40.0 + fuse_limit_A: 32.0 phase_count: 3 connections: - price_information: [] energy_consumer: - module_id: evse_manager_1 implementation_id: energy_grid + evse_manager_2_ocpp_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: - module_id: evse_manager_2 implementation_id: energy_grid + evse_manager_1_api_sink: + module: EnergyNode + mapping: + module: + evse: 1 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_1_ocpp_sink + implementation_id: energy_grid powermeter: - module_id: yeti_driver_1 implementation_id: powermeter + evse_manager_2_api_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_2_ocpp_sink + implementation_id: energy_grid + powermeter: + - module_id: yeti_driver_2 + implementation_id: powermeter + grid_connection_point: + module: EnergyNode + mapping: + module: + evse: 0 + config_module: + fuse_limit_A: 40.0 + phase_count: 3 + connections: + price_information: [] + energy_consumer: + - module_id: evse_manager_1_api_sink + implementation_id: energy_grid + - module_id: evse_manager_2_api_sink + implementation_id: energy_grid api: module: API connections: evse_manager: - module_id: evse_manager_1 implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse + ocpp: + - module_id: ocpp + implementation_id: ocpp_generic error_history: - module_id: error_history implementation_id: error_history + evse_energy_sink: + - module_id: evse_manager_1_api_sink + implementation_id: external_limits + - module_id: evse_manager_2_api_sink + implementation_id: external_limits error_history: module: ErrorHistory config_implementation: diff --git a/config/config-sil-ocpp.yaml b/config/config-sil-ocpp.yaml index 7ad065089..d279bcb45 100644 --- a/config/config-sil-ocpp.yaml +++ b/config/config-sil-ocpp.yaml @@ -16,7 +16,9 @@ active_modules: persistent_store: module: PersistentStore evse_manager_1: - evse: 1 + mapping: + module: + evse: 1 module: EvseManager config_module: connector_id: 1 @@ -47,7 +49,9 @@ active_modules: implementation_id: main evse_manager_2: module: EvseManager - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -73,13 +77,17 @@ active_modules: - module_id: iso15118_charger implementation_id: charger yeti_driver_1: - evse: 1 module: JsYetiSimulator + mapping: + module: + evse: 1 config_module: connector_id: 1 yeti_driver_2: - evse: 2 module: JsYetiSimulator + mapping: + module: + evse: 2 config_module: connector_id: 2 slac: @@ -161,9 +169,13 @@ active_modules: display_message: - module_id: display_message implementation_id: display_message - connector_zero_sink: + evse_energy_sink: - module_id: grid_connection_point implementation_id: external_limits + - module_id: evse_manager_1_ocpp_sink + implementation_id: external_limits + - module_id: evse_manager_2_ocpp_sink + implementation_id: external_limits display_message: module: TerminalCostAndPriceMessage connections: @@ -182,33 +194,94 @@ active_modules: energy_trunk: - module_id: grid_connection_point implementation_id: energy_grid - grid_connection_point: + evse_manager_1_ocpp_sink: module: EnergyNode + mapping: + module: + evse: 1 config_module: - fuse_limit_A: 40.0 + fuse_limit_A: 32.0 phase_count: 3 connections: - price_information: [] energy_consumer: - module_id: evse_manager_1 implementation_id: energy_grid + evse_manager_2_ocpp_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: - module_id: evse_manager_2 implementation_id: energy_grid + evse_manager_1_api_sink: + module: EnergyNode + mapping: + module: + evse: 1 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_1_ocpp_sink + implementation_id: energy_grid powermeter: - module_id: yeti_driver_1 implementation_id: powermeter + evse_manager_2_api_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_2_ocpp_sink + implementation_id: energy_grid + powermeter: + - module_id: yeti_driver_2 + implementation_id: powermeter + grid_connection_point: + module: EnergyNode + mapping: + module: + evse: 0 + config_module: + fuse_limit_A: 40.0 + phase_count: 3 + connections: + price_information: [] + energy_consumer: + - module_id: evse_manager_1_api_sink + implementation_id: energy_grid + - module_id: evse_manager_2_api_sink + implementation_id: energy_grid api: module: API connections: evse_manager: - module_id: evse_manager_1 implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse ocpp: - module_id: ocpp implementation_id: ocpp_generic error_history: - module_id: error_history implementation_id: error_history + evse_energy_sink: + - module_id: evse_manager_1_api_sink + implementation_id: external_limits + - module_id: evse_manager_2_api_sink + implementation_id: external_limits error_history: module: ErrorHistory config_implementation: diff --git a/config/config-sil-ocpp201-pnc.yaml b/config/config-sil-ocpp201-pnc.yaml index 834e353bf..f3f8ac76a 100644 --- a/config/config-sil-ocpp201-pnc.yaml +++ b/config/config-sil-ocpp201-pnc.yaml @@ -18,7 +18,9 @@ active_modules: is_cert_install_needed: true evse_manager_1: module: EvseManager - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 evse_id: "DE*PNX*00001" @@ -43,7 +45,9 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -68,12 +72,16 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 slac: @@ -128,6 +136,13 @@ active_modules: security: - module_id: evse_security implementation_id: main + evse_energy_sink: + - module_id: grid_connection_point + implementation_id: external_limits + - module_id: evse_manager_1_ocpp_sink + implementation_id: external_limits + - module_id: evse_manager_2_ocpp_sink + implementation_id: external_limits evse_security: module: EvseSecurity config_module: @@ -163,30 +178,94 @@ active_modules: energy_trunk: - module_id: grid_connection_point implementation_id: energy_grid - grid_connection_point: + evse_manager_1_ocpp_sink: module: EnergyNode + mapping: + module: + evse: 1 config_module: - fuse_limit_A: 40.0 + fuse_limit_A: 32.0 phase_count: 3 connections: - price_information: [] energy_consumer: - module_id: evse_manager_1 implementation_id: energy_grid + evse_manager_2_ocpp_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: - module_id: evse_manager_2 implementation_id: energy_grid + evse_manager_1_api_sink: + module: EnergyNode + mapping: + module: + evse: 1 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_1_ocpp_sink + implementation_id: energy_grid powermeter: - module_id: yeti_driver_1 implementation_id: powermeter + evse_manager_2_api_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_2_ocpp_sink + implementation_id: energy_grid + powermeter: + - module_id: yeti_driver_2 + implementation_id: powermeter + grid_connection_point: + module: EnergyNode + mapping: + module: + evse: 0 + config_module: + fuse_limit_A: 40.0 + phase_count: 3 + connections: + price_information: [] + energy_consumer: + - module_id: evse_manager_1_api_sink + implementation_id: energy_grid + - module_id: evse_manager_2_api_sink + implementation_id: energy_grid api: module: API connections: evse_manager: - module_id: evse_manager_1 implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse + ocpp: + - module_id: ocpp + implementation_id: ocpp_generic error_history: - module_id: error_history implementation_id: error_history + evse_energy_sink: + - module_id: evse_manager_1_api_sink + implementation_id: external_limits + - module_id: evse_manager_2_api_sink + implementation_id: external_limits error_history: module: ErrorHistory config_implementation: diff --git a/config/config-sil-ocpp201.yaml b/config/config-sil-ocpp201.yaml index 93ba79bb1..2ae3bcfdf 100644 --- a/config/config-sil-ocpp201.yaml +++ b/config/config-sil-ocpp201.yaml @@ -15,7 +15,9 @@ active_modules: supported_ISO15118_2: true evse_manager_1: module: EvseManager - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 evse_id: "1" @@ -41,7 +43,9 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -67,12 +71,16 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator - evse: 1 + mapping: + module: + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator - evse: 2 + mapping: + module: + evse: 2 config_module: connector_id: 2 slac: @@ -127,9 +135,13 @@ active_modules: security: - module_id: evse_security implementation_id: main - connector_zero_sink: + evse_energy_sink: - module_id: grid_connection_point implementation_id: external_limits + - module_id: evse_manager_1_ocpp_sink + implementation_id: external_limits + - module_id: evse_manager_2_ocpp_sink + implementation_id: external_limits persistent_store: module: PersistentStore evse_security: @@ -163,30 +175,94 @@ active_modules: energy_trunk: - module_id: grid_connection_point implementation_id: energy_grid - grid_connection_point: + evse_manager_1_ocpp_sink: module: EnergyNode + mapping: + module: + evse: 1 config_module: - fuse_limit_A: 40.0 + fuse_limit_A: 32.0 phase_count: 3 connections: - price_information: [] energy_consumer: - module_id: evse_manager_1 implementation_id: energy_grid + evse_manager_2_ocpp_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: - module_id: evse_manager_2 implementation_id: energy_grid + evse_manager_1_api_sink: + module: EnergyNode + mapping: + module: + evse: 1 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_1_ocpp_sink + implementation_id: energy_grid powermeter: - module_id: yeti_driver_1 implementation_id: powermeter + evse_manager_2_api_sink: + module: EnergyNode + mapping: + module: + evse: 2 + config_module: + fuse_limit_A: 32.0 + phase_count: 3 + connections: + energy_consumer: + - module_id: evse_manager_2_ocpp_sink + implementation_id: energy_grid + powermeter: + - module_id: yeti_driver_2 + implementation_id: powermeter + grid_connection_point: + module: EnergyNode + mapping: + module: + evse: 0 + config_module: + fuse_limit_A: 40.0 + phase_count: 3 + connections: + price_information: [] + energy_consumer: + - module_id: evse_manager_1_api_sink + implementation_id: energy_grid + - module_id: evse_manager_2_api_sink + implementation_id: energy_grid api: module: API connections: evse_manager: - module_id: evse_manager_1 implementation_id: evse + - module_id: evse_manager_2 + implementation_id: evse + ocpp: + - module_id: ocpp + implementation_id: ocpp_generic error_history: - module_id: error_history implementation_id: error_history + evse_energy_sink: + - module_id: evse_manager_1_api_sink + implementation_id: external_limits + - module_id: evse_manager_2_api_sink + implementation_id: external_limits error_history: module: ErrorHistory config_implementation: diff --git a/dependencies.yaml b/dependencies.yaml index 7ed370979..7454fdafc 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -4,7 +4,7 @@ --- everest-framework: git: https://github.com/EVerest/everest-framework.git - git_tag: v0.17.2 + git_tag: v0.18.0 options: [ "BUILD_TESTING OFF", "everest-framework_USE_PYTHON_VENV ${PROJECT_NAME}_USE_PYTHON_VENV", @@ -90,7 +90,7 @@ ext-mbedtls: # everest-testing and ev-dev-tools everest-utils: git: https://github.com/EVerest/everest-utils.git - git_tag: 3eb4c1b90c14f35a0cc19a96b850c095166c3177 + git_tag: v0.4.2 # unit testing gtest: diff --git a/interfaces/evse_manager.yaml b/interfaces/evse_manager.yaml index 2f4a439e2..784e9f37c 100644 --- a/interfaces/evse_manager.yaml +++ b/interfaces/evse_manager.yaml @@ -101,13 +101,6 @@ cmds: result: description: Returns true if unlocking sequence was successfully executed type: boolean - set_external_limits: - description: Set additional external energy flow limits at this node. - arguments: - value: - description: UUID of node that this limit applies to - type: object - $ref: /energy#/ExternalLimits set_get_certificate_response: description: >- CertificateInstallationRes/CertificateUpdateRes - Set the new/updated Contract Certificate (including the certificate chain) @@ -184,4 +177,4 @@ vars: Contains the selected protocol used for charging for informative purposes type: string errors: - - reference: /errors/evse_manager \ No newline at end of file + - reference: /errors/evse_manager diff --git a/lib/staging/CMakeLists.txt b/lib/staging/CMakeLists.txt index 7258b449d..f4eaf5560 100644 --- a/lib/staging/CMakeLists.txt +++ b/lib/staging/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(can_dpm1000) +add_subdirectory(external_energy_limits) add_subdirectory(util) if(EVEREST_DEPENDENCY_ENABLED_LIBEVSE_SECURITY) diff --git a/lib/staging/external_energy_limits/CMakeLists.txt b/lib/staging/external_energy_limits/CMakeLists.txt new file mode 100644 index 000000000..cdf7f3fac --- /dev/null +++ b/lib/staging/external_energy_limits/CMakeLists.txt @@ -0,0 +1,22 @@ +# External Energy Limits + +add_library(external_energy_limits STATIC) +add_library(everest::external_energy_limits ALIAS external_energy_limits) + +target_sources(external_energy_limits + PRIVATE + external_energy_limits.cpp +) + +target_include_directories(external_energy_limits + PUBLIC + $ + "$" +) + +add_dependencies(external_energy_limits generate_cpp_files) + +target_link_libraries(external_energy_limits + PRIVATE + everest::framework +) \ No newline at end of file diff --git a/lib/staging/external_energy_limits/external_energy_limits.cpp b/lib/staging/external_energy_limits/external_energy_limits.cpp new file mode 100644 index 000000000..b16a0f756 --- /dev/null +++ b/lib/staging/external_energy_limits/external_energy_limits.cpp @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Pionix GmbH and Contributors to EVerest + +#include + +namespace external_energy_limits { + +bool is_evse_sink_configured(const std::vector>& r_evse_energy_sink, + const int32_t evse_id) { + for (const auto& evse_sink : r_evse_energy_sink) { + if (not evse_sink->get_mapping().has_value()) { + EVLOG_critical << "Please configure an evse mapping your configuration file for the connected " + "r_evse_energy_sink with module_id: " + << evse_sink->module_id; + throw std::runtime_error("No mapping configured for evse_id: " + evse_id); + } + if (evse_sink->get_mapping().value().evse == evse_id) { + return true; + } + } + return false; +} + +external_energy_limitsIntf& +get_evse_sink_by_evse_id(const std::vector>& r_evse_energy_sink, + const int32_t evse_id) { + for (const auto& evse_sink : r_evse_energy_sink) { + if (not evse_sink->get_mapping().has_value()) { + EVLOG_critical << "Please configure an evse mapping your configuration file for the connected " + "r_evse_energy_sink with module_id: " + << evse_sink->module_id; + throw std::runtime_error("No mapping configured for evse_id: " + evse_id); + } + if (evse_sink->get_mapping().value().evse == evse_id) { + return *evse_sink; + } + } + throw std::runtime_error("No mapping configured for evse"); +} + +} // namespace external_energy_limits \ No newline at end of file diff --git a/lib/staging/external_energy_limits/external_energy_limits.hpp b/lib/staging/external_energy_limits/external_energy_limits.hpp new file mode 100644 index 000000000..73792d81a --- /dev/null +++ b/lib/staging/external_energy_limits/external_energy_limits.hpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Pionix GmbH and Contributors to EVerest + +#pragma once + +#include + +namespace external_energy_limits { + +/// \brief Checks if \p r_evse_energy_sink vector contains an element that has a mapping to the given \p evse_id +/// \param r_evse_energy_sink +/// \param evse_id +/// \return +bool is_evse_sink_configured(const std::vector>& r_evse_energy_sink, + const int32_t evse_id); + +/// \brief Returns the reference of external_energy_limitsIntf in \p r_evse_energy_sink that maps to the given \p +/// evse_id \param r_evse_energy_sink \param evse_id \return +external_energy_limitsIntf& +get_evse_sink_by_evse_id(const std::vector>& r_evse_energy_sink, + const int32_t evse_id); + +} // namespace external_energy_limits diff --git a/lib/staging/ocpp/CMakeLists.txt b/lib/staging/ocpp/CMakeLists.txt index 70aa3ad41..591e0f234 100644 --- a/lib/staging/ocpp/CMakeLists.txt +++ b/lib/staging/ocpp/CMakeLists.txt @@ -58,3 +58,4 @@ target_link_libraries(ocpp_conversions everest::ocpp everest::framework ) + diff --git a/modules/API/API.cpp b/modules/API/API.cpp index 68c4900df..fd56c74b6 100644 --- a/modules/API/API.cpp +++ b/modules/API/API.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest #include "API.hpp" +#include #include #include @@ -265,12 +266,24 @@ SessionInfo::operator std::string() { void API::init() { invoke_init(*p_main); + + // ensure all evse_energy_sink(s) that are connected have an evse id mapping + for (const auto& evse_sink : this->r_evse_energy_sink) { + if (not evse_sink->get_mapping().has_value()) { + EVLOG_critical << "Please configure an evse mapping your configuration file for the connected " + "r_evse_energy_sink with module_id: " + << evse_sink->module_id; + throw std::runtime_error("At least one connected evse_energy_sink misses a mapping to an evse."); + } + } + this->limit_decimal_places = std::make_unique(this->config); std::vector connectors; std::string var_connectors = this->api_base + "connectors"; evse_manager_check.set_total(r_evse_manager.size()); + int evse_id = 1; for (auto& evse : this->r_evse_manager) { auto& session_info = this->info.emplace_back(std::make_unique()); auto& hw_caps = this->hw_capabilities_str.emplace_back(""); @@ -487,30 +500,39 @@ void API::init() { }); std::string cmd_set_limit = cmd_base + "set_limit_amps"; - this->mqtt.subscribe(cmd_set_limit, [this, &evse](const std::string& data) { - try { - const auto external_limits = get_external_limits(data, false); - this->evse_manager_check.wait_ready(); - evse->call_set_external_limits(external_limits); - } catch (const std::invalid_argument& e) { - EVLOG_warning << "Invalid limit: No conversion of given input could be performed."; - } catch (const std::out_of_range& e) { - EVLOG_warning << "Invalid limit: Out of range."; - } - }); - std::string cmd_set_limit_watts = cmd_base + "set_limit_watts"; - this->mqtt.subscribe(cmd_set_limit_watts, [this, &evse](const std::string& data) { - try { - const auto external_limits = get_external_limits(data, true); - this->evse_manager_check.wait_ready(); - evse->call_set_external_limits(external_limits); - } catch (const std::invalid_argument& e) { - EVLOG_warning << "Invalid limit: No conversion of given input could be performed."; - } catch (const std::out_of_range& e) { - EVLOG_warning << "Invalid limit: Out of range."; - } - }); + if (external_energy_limits::is_evse_sink_configured(this->r_evse_energy_sink, evse_id)) { + auto& evse_energy_sink = + external_energy_limits::get_evse_sink_by_evse_id(this->r_evse_energy_sink, evse_id); + + this->mqtt.subscribe(cmd_set_limit, [&evse_manager_check = this->evse_manager_check, + &evse_energy_sink = evse_energy_sink](const std::string& data) { + try { + const auto external_limits = get_external_limits(data, false); + evse_manager_check.wait_ready(); + evse_energy_sink.call_set_external_limits(external_limits); + } catch (const std::invalid_argument& e) { + EVLOG_warning << "Invalid limit: No conversion of given input could be performed."; + } + }); + + std::string cmd_set_limit_watts = cmd_base + "set_limit_watts"; + + this->mqtt.subscribe(cmd_set_limit_watts, [&evse_manager_check = this->evse_manager_check, + &evse_energy_sink = evse_energy_sink](const std::string& data) { + try { + const auto external_limits = get_external_limits(data, true); + evse_manager_check.wait_ready(); + evse_energy_sink.call_set_external_limits(external_limits); + } catch (const std::invalid_argument& e) { + EVLOG_warning << "Invalid limit: No conversion of given input could be performed."; + } + }); + } else { + EVLOG_warning << "No evse energy sink configured for evse_id: " << evse_id + << ". API module does therefore not allow control of amps or power limits for this EVSE"; + } + std::string cmd_force_unlock = cmd_base + "force_unlock"; this->mqtt.subscribe(cmd_force_unlock, [this, &evse](const std::string& data) { int connector_id = 1; @@ -565,6 +587,7 @@ void API::init() { }); } } + evse_id++; } std::string var_ocpp_connection_status = this->api_base + "ocpp/var/connection_status"; diff --git a/modules/API/API.hpp b/modules/API/API.hpp index 7f83c9ec3..d4ea8880d 100644 --- a/modules/API/API.hpp +++ b/modules/API/API.hpp @@ -16,6 +16,7 @@ // headers for required interface implementations #include #include +#include #include #include @@ -157,7 +158,8 @@ class API : public Everest::ModuleBase { API(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider, std::unique_ptr p_main, std::vector> r_evse_manager, std::vector> r_ocpp, std::vector> r_random_delay, - std::vector> r_error_history, Conf& config) : + std::vector> r_error_history, + std::vector> r_evse_energy_sink, Conf& config) : ModuleBase(info), mqtt(mqtt_provider), p_main(std::move(p_main)), @@ -165,6 +167,7 @@ class API : public Everest::ModuleBase { r_ocpp(std::move(r_ocpp)), r_random_delay(std::move(r_random_delay)), r_error_history(std::move(r_error_history)), + r_evse_energy_sink(std::move(r_evse_energy_sink)), config(config){}; Everest::MqttProvider& mqtt; @@ -173,6 +176,7 @@ class API : public Everest::ModuleBase { const std::vector> r_ocpp; const std::vector> r_random_delay; const std::vector> r_error_history; + const std::vector> r_evse_energy_sink; const Conf& config; // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 diff --git a/modules/API/CMakeLists.txt b/modules/API/CMakeLists.txt index 6f0333fb6..d1da6d21d 100644 --- a/modules/API/CMakeLists.txt +++ b/modules/API/CMakeLists.txt @@ -12,6 +12,7 @@ ev_setup_cpp_module() target_link_libraries(${MODULE_NAME} PRIVATE ryml::ryml + everest::external_energy_limits ) target_sources(${MODULE_NAME} PRIVATE diff --git a/modules/API/README.md b/modules/API/README.md index 24d6a2c42..afb4c211b 100644 --- a/modules/API/README.md +++ b/modules/API/README.md @@ -244,9 +244,13 @@ If any arbitrary payload is published to this topic charging will be paused by t ### everest_api/evse_manager/cmd/set_limit_amps Command to set an amps limit for this EVSE that will be considered within the EnergyManager. This does not automatically imply that this limit will be set by the EVSE because the energymanagement might consider limitations from other sources, too. The payload can be a positive or negative number. +📌 **Note:** You have to configure one evse_energy_sink connection per EVSE within the configuration file in order to use this topic! + ### everest_api/evse_manager/cmd/set_limit_watts Command to set a watt limit for this EVSE that will be considered within the EnergyManager. This does not automatically imply that this limit will be set by the EVSE because the energymanagement might consider limitations from other sources, too. The payload can be a positive or negative number. +📌 **Note:** You have to configure one evse_energy_sink connection per EVSE within the configuration file in order to use this topic! + ### everest_api/evse_manager/cmd/force_unlock Command to force unlock a connector on the EVSE. They payload should be a positive integer identifying the connector that should be unlocked. If the payload is empty or cannot be converted to an integer connector 1 is assumed. diff --git a/modules/API/manifest.yaml b/modules/API/manifest.yaml index ec1b0e9fb..ff5ad813d 100644 --- a/modules/API/manifest.yaml +++ b/modules/API/manifest.yaml @@ -189,6 +189,10 @@ requires: interface: error_history min_connections: 0 max_connections: 1 + evse_energy_sink: + interface: external_energy_limits + min_connections: 0 + max_connections: 128 enable_external_mqtt: true metadata: license: https://opensource.org/licenses/Apache-2.0 diff --git a/modules/Cargo.lock b/modules/Cargo.lock index a298e2e5b..6dff048a6 100644 --- a/modules/Cargo.lock +++ b/modules/Cargo.lock @@ -401,7 +401,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "everestrs" version = "0.1.0" -source = "git+https://github.com/everest/everest-framework.git?rev=3e767e2a5652d3acb97d01fc88aae2f04f3f5282#3e767e2a5652d3acb97d01fc88aae2f04f3f5282" +source = "git+https://github.com/everest/everest-framework.git?tag=v0.18.0#bb3d3a91bb50031d21aa3d43220801a9eb69a6bd" dependencies = [ "argh", "cxx", @@ -415,7 +415,7 @@ dependencies = [ [[package]] name = "everestrs-build" version = "0.1.0" -source = "git+https://github.com/everest/everest-framework.git?rev=3e767e2a5652d3acb97d01fc88aae2f04f3f5282#3e767e2a5652d3acb97d01fc88aae2f04f3f5282" +source = "git+https://github.com/everest/everest-framework.git?tag=v0.18.0#bb3d3a91bb50031d21aa3d43220801a9eb69a6bd" dependencies = [ "anyhow", "argh", diff --git a/modules/Cargo.toml b/modules/Cargo.toml index f3f573b8f..a6415fe40 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -8,5 +8,5 @@ members = [ ] [workspace.dependencies] -everestrs = { git = "https://github.com/everest/everest-framework.git", rev = "3e767e2a5652d3acb97d01fc88aae2f04f3f5282" } -everestrs-build = { git = "https://github.com/everest/everest-framework.git", rev = "3e767e2a5652d3acb97d01fc88aae2f04f3f5282" } +everestrs = { git = "https://github.com/everest/everest-framework.git", tag = "v0.18.0" } +everestrs-build = { git = "https://github.com/everest/everest-framework.git", tag = "v0.18.0" } diff --git a/modules/EvseManager/evse/evse_managerImpl.cpp b/modules/EvseManager/evse/evse_managerImpl.cpp index a6b205615..103266bbb 100644 --- a/modules/EvseManager/evse/evse_managerImpl.cpp +++ b/modules/EvseManager/evse/evse_managerImpl.cpp @@ -421,10 +421,6 @@ bool evse_managerImpl::handle_stop_transaction(types::evse_manager::StopTransact return mod->charger->cancel_transaction(request); }; -void evse_managerImpl::handle_set_external_limits(types::energy::ExternalLimits& value) { - mod->update_local_energy_limit(value); -} - void evse_managerImpl::handle_set_get_certificate_response( types::iso15118_charger::ResponseExiStreamStatus& certificate_reponse) { mod->r_hlc[0]->call_certificate_response(certificate_reponse); diff --git a/modules/EvseManager/evse/evse_managerImpl.hpp b/modules/EvseManager/evse/evse_managerImpl.hpp index f9db90dad..58ac2e3fb 100644 --- a/modules/EvseManager/evse/evse_managerImpl.hpp +++ b/modules/EvseManager/evse/evse_managerImpl.hpp @@ -47,7 +47,6 @@ class evse_managerImpl : public evse_managerImplBase { virtual bool handle_resume_charging() override; virtual bool handle_stop_transaction(types::evse_manager::StopTransactionRequest& request) override; virtual bool handle_force_unlock(int& connector_id) override; - virtual void handle_set_external_limits(types::energy::ExternalLimits& value) override; virtual void handle_set_get_certificate_response( types::iso15118_charger::ResponseExiStreamStatus& certificate_response) override; virtual bool handle_external_ready_to_start_charging() override; diff --git a/modules/EvseManager/tests/evse_board_supportIntfStub.hpp b/modules/EvseManager/tests/evse_board_supportIntfStub.hpp index 9ed3c0364..c64342848 100644 --- a/modules/EvseManager/tests/evse_board_supportIntfStub.hpp +++ b/modules/EvseManager/tests/evse_board_supportIntfStub.hpp @@ -12,7 +12,7 @@ namespace module::stub { struct evse_board_supportIntfStub : public evse_board_supportIntf { explicit evse_board_supportIntfStub(ModuleAdapterStub& adapter) : - evse_board_supportIntf(&adapter, Requirement("requirement", 1), "EvseManager") { + evse_board_supportIntf(&adapter, Requirement{"requirement", 1}, "EvseManager", std::nullopt) { } }; diff --git a/modules/EvseV2G/tests/evse_securityIntfStub.hpp b/modules/EvseV2G/tests/evse_securityIntfStub.hpp index d2a388e0a..5768457c6 100644 --- a/modules/EvseV2G/tests/evse_securityIntfStub.hpp +++ b/modules/EvseV2G/tests/evse_securityIntfStub.hpp @@ -23,7 +23,7 @@ class evse_securityIntfStub : public ModuleAdapterStub, public evse_securityIntf functions; public: - evse_securityIntfStub() : evse_securityIntf(this, Requirement("", 0), "EvseSecurity") { + evse_securityIntfStub() : evse_securityIntf(this, Requirement{"", 0}, "EvseSecurity", std::nullopt) { functions["get_verify_file"] = &evse_securityIntfStub::get_verify_file; functions["get_leaf_certificate_info"] = &evse_securityIntfStub::get_leaf_certificate_info; } diff --git a/modules/EvseV2G/tests/requirement.cpp b/modules/EvseV2G/tests/requirement.cpp index cec9c44a7..8bbd5db72 100644 --- a/modules/EvseV2G/tests/requirement.cpp +++ b/modules/EvseV2G/tests/requirement.cpp @@ -5,8 +5,6 @@ #include "utils/types.hpp" -Requirement::Requirement(const std::string& requirement_id_, size_t index_) { -} -bool Requirement::operator<(const Requirement& rhs) const { +bool operator<(const Requirement& lhs, const Requirement& rhs) { return true; } diff --git a/modules/OCPP/CMakeLists.txt b/modules/OCPP/CMakeLists.txt index 6b7b946ae..86f4fdae7 100644 --- a/modules/OCPP/CMakeLists.txt +++ b/modules/OCPP/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(${MODULE_NAME} everest::ocpp everest::ocpp_evse_security everest::ocpp_conversions + everest::external_energy_limits ) target_compile_options(${MODULE_NAME} diff --git a/modules/OCPP/OCPP.cpp b/modules/OCPP/OCPP.cpp index 5125a23ff..e6ba0a6bc 100644 --- a/modules/OCPP/OCPP.cpp +++ b/modules/OCPP/OCPP.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace module { @@ -77,6 +78,13 @@ void OCPP::set_external_limits(const std::mapr_evse_energy_sink, connector_id)) { + EVLOG_warning << "Can not apply external limits! No evse energy sink configured for evse_id: " + << connector_id; + continue; + } + types::energy::ExternalLimits limits; std::vector schedule_import; for (const auto period : schedule.chargingSchedulePeriod) { @@ -99,19 +107,8 @@ void OCPP::set_external_limits(const std::mapr_connector_zero_sink.empty()) { - EVLOG_debug << "OCPP sets the following external limits for connector 0: \n" << limits; - this->r_connector_zero_sink.at(0)->call_set_external_limits(limits); - } else { - EVLOG_debug << "OCPP cannot set external limits for connector 0. No " - "sink is configured."; - } - } else { - EVLOG_debug << "OCPP sets the following external limits for connector " << connector_id << ": \n" << limits; - this->r_evse_manager.at(connector_id - 1)->call_set_external_limits(limits); - } + auto& evse_sink = external_energy_limits::get_evse_sink_by_evse_id(this->r_evse_energy_sink, connector_id); + evse_sink.call_set_external_limits(limits); } } @@ -347,6 +344,16 @@ void OCPP::init() { invoke_init(*p_auth_provider); invoke_init(*p_data_transfer); + // ensure all evse_energy_sink(s) that are connected have an evse id mapping + for (const auto& evse_sink : this->r_evse_energy_sink) { + if (not evse_sink->get_mapping().has_value()) { + EVLOG_critical << "Please configure an evse mapping your configuration file for the connected " + "r_evse_energy_sink with module_id: " + << evse_sink->module_id; + throw std::runtime_error("At least one connected evse_energy_sink misses a mapping to an evse."); + } + } + const auto error_handler = [this](const Everest::error::Error& error) { const auto evse_id = error.origin.mapping.has_value() ? error.origin.mapping.value().evse : 0; const auto error_info = get_error_info(error); diff --git a/modules/OCPP/OCPP.hpp b/modules/OCPP/OCPP.hpp index 631584657..cc47f165f 100644 --- a/modules/OCPP/OCPP.hpp +++ b/modules/OCPP/OCPP.hpp @@ -76,7 +76,7 @@ class OCPP : public Everest::ModuleBase { std::unique_ptr p_data_transfer, std::unique_ptr p_ocpp_generic, std::unique_ptr p_session_cost, std::vector> r_evse_manager, - std::vector> r_connector_zero_sink, + std::vector> r_evse_energy_sink, std::unique_ptr r_reservation, std::unique_ptr r_auth, std::unique_ptr r_system, std::unique_ptr r_security, std::vector> r_data_transfer, @@ -90,7 +90,7 @@ class OCPP : public Everest::ModuleBase { p_ocpp_generic(std::move(p_ocpp_generic)), p_session_cost(std::move(p_session_cost)), r_evse_manager(std::move(r_evse_manager)), - r_connector_zero_sink(std::move(r_connector_zero_sink)), + r_evse_energy_sink(std::move(r_evse_energy_sink)), r_reservation(std::move(r_reservation)), r_auth(std::move(r_auth)), r_system(std::move(r_system)), @@ -107,7 +107,7 @@ class OCPP : public Everest::ModuleBase { const std::unique_ptr p_ocpp_generic; const std::unique_ptr p_session_cost; const std::vector> r_evse_manager; - const std::vector> r_connector_zero_sink; + const std::vector> r_evse_energy_sink; const std::unique_ptr r_reservation; const std::unique_ptr r_auth; const std::unique_ptr r_system; diff --git a/modules/OCPP/doc.rst b/modules/OCPP/doc.rst index bfa555f05..4611bcdc3 100644 --- a/modules/OCPP/doc.rst +++ b/modules/OCPP/doc.rst @@ -298,6 +298,31 @@ This module currently deviates from the MREC specification in the following poin **Faulted** value as follows: "When a Charge Point or connector has reported an error and is not available for energy delivery. (Inoperative)." This module, therefore, only reports **Faulted** when the Charge Point is not available for energy delivery. +Energy Management and Smart Charging Integration +------------------------------------------------ + +OCPP1.6 defines the SmartCharging feature profile to allow the CSMS to control or influence the power consumption of the charging station. +This module integrates the composite schedule(s) within EVerest's energy management. For further information about smart charging and the +composite schedule calculation please refer to the OCPP1.6 specification. + +The integration of the composite schedules is implemented through the optional requirement(s) `evse_energy_sink` (interface: `external_energy_limits`) +of this module. Depending on the number of EVSEs configured, each composite limit is communicated via a seperate sink, including the composite schedule +for EVSE with id 0 (representing the whole charging station). The easiest way to explain this is with an example. If your charging station +has two EVSEs you need to connect three modules that implement the `external_energy_limits` interface: One representing evse id 0 and +two representing your actual EVSEs. + +📌 **Note:** You have to configure an evse mapping for each module connected via the evse_energy_sink connection. This allows the module to identify +which requirement to use when communicating the limits for the EVSEs. For more information about the module mapping please see +`3-tier module mappings `_. + +This module defines a callback that gets executed every time charging profiles are changed, added or removed by the CSMS. The callback retrieves +the composite schedules for all EVSEs (including evse id 0) and calls the `set_external_limits` command of the respective requirement that implements +the `external_energy_limits` interface. In addition, the config parameter `PublishChargingScheduleIntervalS` defines a periodic interval to retrieve +the composite schedule also in case no charging profiles have been changed. The configuration parameter `PublishChargingScheduleDurationS` defines +the duration in seconds of the requested composite schedules starting now. The value configured for `PublishChargingScheduleDurationS` shall be greater +than the value configured for `PublishChargingScheduleIntervalS` because otherwise time periods could be missed by the application. + + Certificate Management ---------------------- diff --git a/modules/OCPP/manifest.yaml b/modules/OCPP/manifest.yaml index d158c8542..46fbbac5c 100644 --- a/modules/OCPP/manifest.yaml +++ b/modules/OCPP/manifest.yaml @@ -77,10 +77,10 @@ requires: interface: evse_manager min_connections: 1 max_connections: 128 - connector_zero_sink: + evse_energy_sink: interface: external_energy_limits min_connections: 0 - max_connections: 1 + max_connections: 129 reservation: interface: reservation min_connections: 1 diff --git a/modules/OCPP201/CMakeLists.txt b/modules/OCPP201/CMakeLists.txt index d9b0bf101..9779599f4 100644 --- a/modules/OCPP201/CMakeLists.txt +++ b/modules/OCPP201/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(${MODULE_NAME} everest::ocpp everest::ocpp_evse_security everest::ocpp_conversions + everest::external_energy_limits ) target_compile_options(${MODULE_NAME} diff --git a/modules/OCPP201/OCPP201.cpp b/modules/OCPP201/OCPP201.cpp index b9ddffd2f..1a6772577 100644 --- a/modules/OCPP201/OCPP201.cpp +++ b/modules/OCPP201/OCPP201.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace module { @@ -275,6 +276,16 @@ void OCPP201::init() { invoke_init(*p_auth_provider); invoke_init(*p_auth_validator); + // ensure all evse_energy_sink(s) that are connected have an evse id mapping + for (const auto& evse_sink : this->r_evse_energy_sink) { + if (not evse_sink->get_mapping().has_value()) { + EVLOG_critical << "Please configure an evse mapping your configuration file for the connected " + "r_evse_energy_sink with module_id: " + << evse_sink->module_id; + throw std::runtime_error("At least one connected evse_energy_sink misses a mapping to an evse."); + } + } + this->init_evse_maps(); for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) { @@ -662,8 +673,8 @@ void OCPP201::ready() { const auto composite_schedule_unit = get_unit_or_default(this->config.RequestCompositeScheduleUnit); - // this callback publishes the schedules within EVerest and applies the schedules for the individual evse_manager(s) - // and the connector_zero_sink + // this callback publishes the schedules within EVerest and applies the schedules for the individual + // r_evse_energy_sink const auto charging_schedules_callback = [this, composite_schedule_unit]() { const auto composite_schedules = this->charge_point->get_all_composite_schedules( this->config.RequestCompositeScheduleDurationS, composite_schedule_unit); @@ -1201,6 +1212,12 @@ void OCPP201::set_external_limits(const std::vectorr_evse_energy_sink, evse_id)) { + EVLOG_warning << "Can not apply external limits! No evse energy sink configured for evse_id: " << evse_id; + continue; + } + types::energy::ExternalLimits limits; std::vector schedule_import; @@ -1222,19 +1239,8 @@ void OCPP201::set_external_limits(const std::vectorr_connector_zero_sink.empty()) { - EVLOG_debug << "OCPP sets the following external limits for evse 0: \n" << limits; - this->r_connector_zero_sink.at(0)->call_set_external_limits(limits); - } else { - EVLOG_debug << "OCPP cannot set external limits for evse 0. No " - "sink is configured."; - } - } else { - EVLOG_debug << "OCPP sets the following external limits for evse " << composite_schedule.evseId << ": \n" - << limits; - this->r_evse_manager.at(composite_schedule.evseId - 1)->call_set_external_limits(limits); - } + auto& evse_sink = external_energy_limits::get_evse_sink_by_evse_id(this->r_evse_energy_sink, evse_id); + evse_sink.call_set_external_limits(limits); } } diff --git a/modules/OCPP201/OCPP201.hpp b/modules/OCPP201/OCPP201.hpp index dea85f514..ddff7352f 100644 --- a/modules/OCPP201/OCPP201.hpp +++ b/modules/OCPP201/OCPP201.hpp @@ -60,7 +60,7 @@ class OCPP201 : public Everest::ModuleBase { std::vector> r_evse_manager, std::unique_ptr r_system, std::unique_ptr r_security, std::vector> r_data_transfer, std::unique_ptr r_auth, - std::vector> r_connector_zero_sink, + std::vector> r_evse_energy_sink, std::vector> r_display_message, Conf& config) : ModuleBase(info), mqtt(mqtt_provider), @@ -74,7 +74,7 @@ class OCPP201 : public Everest::ModuleBase { r_security(std::move(r_security)), r_data_transfer(std::move(r_data_transfer)), r_auth(std::move(r_auth)), - r_connector_zero_sink(std::move(r_connector_zero_sink)), + r_evse_energy_sink(std::move(r_evse_energy_sink)), r_display_message(std::move(r_display_message)), config(config) { } @@ -90,7 +90,7 @@ class OCPP201 : public Everest::ModuleBase { const std::unique_ptr r_security; const std::vector> r_data_transfer; const std::unique_ptr r_auth; - const std::vector> r_connector_zero_sink; + const std::vector> r_evse_energy_sink; const std::vector> r_display_message; const Conf& config; @@ -157,8 +157,9 @@ class OCPP201 : public Everest::ModuleBase { /// \brief This function publishes the given \p composite_schedules via the ocpp interface void publish_charging_schedules(const std::vector& composite_schedules); - /// \brief This function applies given \p composite_schedules for each evse_manager and the connector_zero_sink + /// \brief This function applies given \p composite_schedules for each connected evse_energy_sink void set_external_limits(const std::vector& composite_schedules); + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 }; diff --git a/modules/OCPP201/doc.rst b/modules/OCPP201/doc.rst index 8d7df4918..812026e71 100644 --- a/modules/OCPP201/doc.rst +++ b/modules/OCPP201/doc.rst @@ -310,3 +310,27 @@ In addition to that, the charging station periodically updates the OCSP response The OCSP response is cached and can be used as part of the ISO15118 TLS handshake with EVs. The OCSP update is by default performed every seven days. The timestamp of the last update is stored persistently, so that this process is not necessarily performed at every start up. + +Energy Management and Smart Charging Integration +------------------------------------------------ + +OCPP2.0.1 defines the SmartCharging feature profile to allow the CSMS to control or influence the power consumption of the charging station. +This module integrates the composite schedule(s) within EVerest's energy management. For further information about smart charging and the +composite schedule calculation please refer to the OCPP2.0.1 specification. + +The integration of the composite schedules is implemented through the optional requirement(s) `evse_energy_sink` (interface: `external_energy_limits`) +of this module. Depending on the number of EVSEs configured, each composite limit is communicated via a seperate sink, including the composite schedule +for EVSE with id 0 (representing the whole charging station). The easiest way to explain this is with an example. If your charging station +has two EVSEs you need to connect three modules that implement the `external_energy_limits` interface: One representing evse id 0 and +two representing your actual EVSEs. + +📌 **Note:** You have to configure an evse mapping for each module connected via the evse_energy_sink connection. This allows the module to identify +which requirement to use when communicating the limits for the EVSEs. For more information about the module mapping please see +`3-tier module mappings `_. + +This module defines a callback that gets executed every time charging profiles are changed, added or removed by the CSMS. The callback retrieves +the composite schedules for all EVSEs (including evse id 0) and calls the `set_external_limits` command of the respective requirement that implements +the `external_energy_limits` interface. In addition, the config parameter `CompositeScheduleIntervalS` defines a periodic interval to retrieve +the composite schedule also in case no charging profiles have been changed. The configuration parameter `RequestCompositeScheduleDurationS` defines +the duration in seconds of the requested composite schedules starting now. The value configured for `RequestCompositeScheduleDurationS` shall be greater +than the value configured for `CompositeScheduleIntervalS` because otherwise time periods could be missed by the application. diff --git a/modules/OCPP201/manifest.yaml b/modules/OCPP201/manifest.yaml index bdc641913..6023fc249 100644 --- a/modules/OCPP201/manifest.yaml +++ b/modules/OCPP201/manifest.yaml @@ -90,10 +90,10 @@ requires: interface: auth min_connections: 1 max_connections: 1 - connector_zero_sink: + evse_energy_sink: interface: external_energy_limits min_connections: 0 - max_connections: 1 + max_connections: 129 display_message: interface: display_message min_connections: 0