Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,138 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
...

View File

@@ -0,0 +1,5 @@
* @a-w50 @SebaLukas @james-ctc @mlitre
# CI
.ci/ @andistorm @SebaLukas
.github/workflows/ @andistorm @SebaLukas

View File

@@ -0,0 +1,5 @@
*build
.vscode
/pki
test/sample_data
.cache/

View File

@@ -0,0 +1,93 @@
cmake_minimum_required(VERSION 3.14)
project(iso15118
VERSION 0.9.1
DESCRIPTION "iso15118 library suite"
LANGUAGES CXX C
)
find_package(everest-cmake 0.5
PATHS ../everest-cmake
NO_DEFAULT_PATH
)
find_package(everest-cmake 0.5)
find_package(OpenSSL 3 REQUIRED)
if (NOT everest-cmake_FOUND)
message(STATUS "Retrieving everest-cmake using FetchContent")
include(FetchContent)
FetchContent_Declare(
everest-cmake
GIT_REPOSITORY https://github.com/EVerest/everest-cmake.git
GIT_TAG v0.5.3
)
FetchContent_MakeAvailable(everest-cmake)
set(everest-cmake_DIR "${everest-cmake_SOURCE_DIR}")
include("${everest-cmake_SOURCE_DIR}/everest-cmake-config.cmake")
endif()
# options
option(${PROJECT_NAME}_BUILD_TESTING "Build unit tests, used if included as dependency" OFF)
option(BUILD_TESTING "Build unit tests, used if standalone project" OFF)
option(OPT_AUTODOWNLOAD_ISO20_SCHEMAS "\
Automatically download ISO15118-20 schemas. Note: by setting this option to \
true and hence downloading the schema files, YOU accept the ISO Customer \
Licence Agreement (“Licence Agreement”), clauses 1. ISOs Copyright, \
7. Termination, 8. Limitations, and 9. Governing Law." OFF)
option(ISO15118_INSTALL "Enable install target" ${EVC_MAIN_PROJECT})
option(DISABLE_ISO15118_LOCAL_DEPENDENCIES "Disable local dependency lookup for libiso15118" OFF)
option(ISO15118_USE_EXPORTED_BUILD "Use (experimental) exported build of libiso15118" OFF)
option(${PROJECT_NAME}_USE_PYTHON_VENV "Use python venv for pip install targets" ON)
if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
if(ISO15118_USE_EXPORTED_BUILD)
include(cmake/exported-build.cmake)
else()
include(cmake/local-build.cmake)
endif()
endif()
# list of compile options
set(ISO15118_COMPILE_OPTIONS_WARNING "-Wall;-Wextra;-Wno-unused-function;-Werror" CACHE STRING "A list of compile options used")
message(STATUS "Building libiso15118 with the following compile options: ${ISO15118_COMPILE_OPTIONS_WARNING}")
if((${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME} OR ${PROJECT_NAME}_BUILD_TESTING) AND BUILD_TESTING)
set(ISO15118_BUILD_TESTING ON)
endif()
# dependencies
if (NOT DISABLE_EDM)
evc_setup_edm()
# In EDM mode, we can't install exports (because the dependencies usually do not install their exports)
set(ISO15118_INSTALL OFF)
endif()
add_subdirectory(input)
add_subdirectory(src)
if (ISO15118_BUILD_TESTING)
include(CTest)
add_subdirectory(test)
endif()
if (ISO15118_INSTALL)
install(
TARGETS
iso15118
EXPORT iso15118-targets
)
install(
DIRECTORY include/
TYPE INCLUDE
PATTERN "detail" EXCLUDE
)
evc_setup_package(
NAME iso15118
EXPORT iso15118-targets
NAMESPACE iso15118
)
endif()

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,144 @@
ISO 15118 library suite
=======================
This is a C++ library implementation of ISO 15118-20, ISO15118-2 and DIN70121. The implementation of ISO15118-20 is currently under heavy development. DIN70121 and ISO15118-2 will follow and are currently covered by the [EvseV2G module](https://github.com/EVerest/EVerest/tree/main/modules/EvseV2G) of EVerest.
ISO 15118-20 Support
--------------------
The following table shows the current support for the listed EVSE ISO15118-20 features.
| Feature | Supported |
|------------------------------------|--------------------|
| TCP, TLS 1.2 & 1.3 | :heavy_check_mark: |
| DC, DC_BPT | :heavy_check_mark: |
| AC, AC_BPT | :heavy_check_mark: |
| MCS (Amd.) | :heavy_check_mark: |
| AC DER (Amd.) | |
| WPT | |
| ACDP | |
| ExternalPayment | :heavy_check_mark: |
| Plug&Charge | WIP |
| CertificateInstallation | |
| Scheduled Mode | :heavy_check_mark: |
| Dynamic Mode (+ MobilityNeedsMode) | :heavy_check_mark: |
| Private Env | |
| Pause/Resume | :heavy_check_mark: (dynamic mode) |
| Standby | |
| Schedule Renegotation | |
| Smart Charging | |
| Multiplex messages | |
| Internet Service | |
| Parking Status Service | |
ISO 15118 Support
-----------------
ISO15118 support is distributed accross multiple repositories and modules in EVerest. Please see the following references of other ISO15118 related development:
- Some functionality of part 2 of ISO 15118 is integrated in the
[EvseManager module in the EVerest repository](https://github.com/EVerest/EVerest/tree/main/modules/EVSE/EvseManager).
- Current development for an EXI code generator (as used in the
ISO 15118 protocol suite) is ongoing in the
[cbexigen repository](https://github.com/EVerest/cbexigen).
- The [repository libSlac](https://github.com/EVerest/libslac) contains
definitions of SLAC messages that are used for ISO 15118 communication.
- DIN70121 & ISO15118-2 functionality can be found in
[EVerest module EvseV2G](https://github.com/EVerest/EVerest/tree/main/modules/EVSE/EvseV2G)
Dependencies
------------
To build this library you need [everest-cmake](https://github.com/EVerest/everest-cmake) checkout in the same directory as libiso15118. If no `everest-cmake` is available, it is retrieved via FetchContent.
For Debian GNU/Linux 12 you will need the following dependencies:
```bash
sudo apt update
sudo apt install build-essential cmake libssl-dev
```
For Fedora 41+ you will need the following dependencies:
```bash
sudo dnf update
sudo dnf install gcc gcc-c++ git make cmake openssl-devel
```
OpenSSL version 3.0 or above is required. The build system `ninja` is optional.
Getting started
---------------
```
# Run cmake (BUILD_TESTING to enable/disable unit tests)
cmake -S . -B build -G Ninja -DBUILD_TESTING=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# Run cmake with disabled compiler warnings
cmake -S . -B build -G Ninja -DBUILD_TESTING=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DISO15118_COMPILE_OPTIONS_WARNING=""
# Build
ninja -C build
# Running tests
ninja -C build test
# Generating a code coverage (BUILD_TESTING should be enabled)
ninja -C build iso15118_gcovr_coverage
```
The coverage report will be available in the index.html file in the `build/iso15118_gcovr_coverage` directory.
Version 8.2 or higher of gcovr is required for the coverage report. Install gcovr release from PyPI:
```
pip install gcovr
```
GDB Debugging (VS Code)
-----------------------
Run the cmake (from the build dir) with the debug commands on:
```
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON
```
Run the GDB debugger with the following configuration:
```
{
"name": "(gdb) Launch LIBISO TESTS",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/libiso15118/build/test/iso15118/fsm/test_d20_transitions",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/libiso15118/build/test/iso15118/fsm/",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
```
Replace the `program` path to any test executable you are debugging.
Acknowledgment
--------------
This library has thankfully received support from the German Federal Ministry
for Economic Affairs and Climate Action.
Information on the corresponding research project can be found here (in
German only):
[InterBDL research project](https://www.thu.de/de/org/iea/smartgrids/Seiten/InterBDL.aspx).
![Logo of funding by Federal Ministry of Economic Affairs and Climate Action](https://raw.githubusercontent.com/EVerest/EVerest/main/docs/images/bmwk-logo-incl-supporting.png)

View File

@@ -0,0 +1 @@
_Use this file to list out any third-party dependencies used by this project. You may choose to point to a Gemfile or other language specific packaging file for details._

View File

@@ -0,0 +1,57 @@
# download everest-core (source of util and libcbv2g)
include(ExternalProject)
ExternalProject_Add(
everest-core-src
DOWNLOAD_DIR "everest-core/src"
GIT_REPOSITORY "https://github.com/EVerest/everest-core.git"
GIT_TAG "61e97863fd2fc9f429f9cd2e4b689139e7d46981"
TIMEOUT 30
LOG_DOWNLOAD ON
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
# util is header-only
ExternalProject_Get_Property(everest-core-src SOURCE_DIR)
set(UTIL_INCLUDE_DIR "${SOURCE_DIR}/lib/everest/util/include")
# workaround for https://gitlab.kitware.com/cmake/cmake/-/issues/15052
file(MAKE_DIRECTORY ${UTIL_INCLUDE_DIR})
add_library(everest_util INTERFACE IMPORTED GLOBAL)
add_library(everest::util ALIAS everest_util)
set_property(TARGET everest_util PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${UTIL_INCLUDE_DIR})
add_dependencies(everest_util everest-core-src)
# build everest-core/lib/everest/cbv2g
ExternalProject_Add(
cbv2g-src
DOWNLOAD_COMMAND ""
SOURCE_DIR "everest-core-src-prefix/src/everest-core-src/lib/everest/cbv2g"
PREFIX "everest-core"
INSTALL_COMMAND ""
LOG_CONFIGURE ON
LOG_BUILD ON
DEPENDS everest-core-src
)
ExternalProject_Get_Property(cbv2g-src SOURCE_DIR)
ExternalProject_Get_Property(cbv2g-src BINARY_DIR)
set(CBV2G_INCLUDE_DIR "${SOURCE_DIR}/include")
set(CBV2G_LIB_DIR "${BINARY_DIR}/lib/cbv2g")
# workaround for https://gitlab.kitware.com/cmake/cmake/-/issues/15052
# create CBV2G_INCLUDE_DIR since it will not exist in configure step
file(MAKE_DIRECTORY ${CBV2G_INCLUDE_DIR})
add_library(cbv2g_tp STATIC IMPORTED)
add_library(cbv2g::tp ALIAS cbv2g_tp)
set_property(TARGET cbv2g_tp PROPERTY IMPORTED_LOCATION ${CBV2G_LIB_DIR}/libcbv2g_tp.a)
set_property(TARGET cbv2g_tp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CBV2G_INCLUDE_DIR})
add_dependencies(cbv2g_tp cbv2g-src)
add_library(cbv2g_iso20 STATIC IMPORTED)
add_library(cbv2g::iso20 ALIAS cbv2g_iso20)
set_property(TARGET cbv2g_iso20 PROPERTY IMPORTED_LOCATION ${CBV2G_LIB_DIR}/libcbv2g_iso20.a)
set_property(TARGET cbv2g_iso20 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CBV2G_INCLUDE_DIR})
add_dependencies(cbv2g_iso20 cbv2g-src)

View File

@@ -0,0 +1,40 @@
# detect if we try to build inside everest-core and libcbv2g is available
message(STATUS "Attempting build with autodetected local dependencies")
get_filename_component(EVC_EVEREST_LIB_DIR ${PROJECT_SOURCE_DIR} DIRECTORY)
set(EVC_CBV2G_DIR "${EVC_EVEREST_LIB_DIR}/cbv2g")
if (EVC_CBV2G_DIR AND NOT DISABLE_ISO15118_LOCAL_DEPENDENCIES)
message(STATUS "Detected libcbv2g in ${EVC_CBV2G_DIR}, if you do not want this set -DDISABLE_ISO15118_LOCAL_DEPENDENCIES=ON")
add_subdirectory("${EVC_CBV2G_DIR}" libcbv2g)
endif()
function(ev_register_library_target NAME)
endfunction()
set(EVC_UTIL_DIR "${EVC_EVEREST_LIB_DIR}/util")
if (EVC_UTIL_DIR AND NOT DISABLE_ISO15118_LOCAL_DEPENDENCIES)
message(STATUS "Detected util in ${EVC_UTIL_DIR}, if you do not want this set -DDISABLE_ISO15118_LOCAL_DEPENDENCIES=ON")
if (BUILD_TESTING)
message(STATUS "Setting BUILD_TESTING temporary to false")
set(CACHE_BUILD_TESTING ON)
set(BUILD_TESTING OFF)
endif()
add_subdirectory("${EVC_UTIL_DIR}" util)
if (CACHE_BUILD_TESTING)
set(BUILD_TESTING ON)
endif()
endif()
# set venv location
set(${PROJECT_NAME}_PYTHON_VENV_PATH "${CMAKE_BINARY_DIR}/venv" CACHE PATH "Path to python venv")
ev_setup_python_executable(
USE_PYTHON_VENV ${${PROJECT_NAME}_USE_PYTHON_VENV}
PYTHON_VENV_PATH ${${PROJECT_NAME}_PYTHON_VENV_PATH}
)
get_filename_component(EVC_LIB_DIR ${EVC_EVEREST_LIB_DIR} DIRECTORY)
get_filename_component(EVC_DIR ${EVC_LIB_DIR} DIRECTORY)
set(EVC_EDM_DIR "${EVC_DIR}/applications/dependency_manager")
# use edm from everest-core
add_subdirectory("${EVC_EDM_DIR}" edm_tool)

View File

@@ -0,0 +1,5 @@
---
catch2:
git: https://github.com/catchorg/Catch2.git
git_tag: v3.10.0
cmake_condition: "ISO15118_BUILD_TESTING"

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <filesystem>
#include <optional>
#include <string>
namespace iso15118::config {
enum class TlsNegotiationStrategy {
ACCEPT_CLIENT_OFFER,
ENFORCE_TLS,
ENFORCE_NO_TLS,
};
enum class CertificateBackend {
EVEREST_LAYOUT,
JOSEPPA_LAYOUT,
};
struct SSLConfig {
CertificateBackend backend{CertificateBackend::EVEREST_LAYOUT};
// Used by the JOSEPPA_LAYOUT
std::string config_string;
// Used by the EVEREST_LAYOUT
std::string path_certificate_chain;
std::string path_certificate_key;
std::optional<std::string> private_key_password{};
std::string path_certificate_v2g_root;
std::string path_certificate_mo_root;
bool enable_ssl_logging{false};
bool enable_tls_key_logging{false};
bool enforce_tls_1_3{false};
std::filesystem::path tls_key_logging_path{};
};
} // namespace iso15118::config

View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
namespace dt = message_20::datatypes;
struct AcTargetPower {
std::optional<dt::RationalNumber> target_active_power;
std::optional<dt::RationalNumber> target_active_power_L2;
std::optional<dt::RationalNumber> target_active_power_L3;
std::optional<dt::RationalNumber> target_reactive_power;
std::optional<dt::RationalNumber> target_reactive_power_L2;
std::optional<dt::RationalNumber> target_reactive_power_L3;
std::optional<dt::RationalNumber> target_frequency;
};
struct AcPresentPower {
std::optional<dt::RationalNumber> present_active_power;
std::optional<dt::RationalNumber> present_active_power_L2;
std::optional<dt::RationalNumber> present_active_power_L3;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <optional>
#include <vector>
#include <iso15118/d20/limits.hpp>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
struct ControlMobilityNeedsModes {
message_20::datatypes::ControlMode control_mode;
message_20::datatypes::MobilityNeedsMode mobility_mode;
};
struct AcSetupConfig {
uint32_t voltage;
std::vector<message_20::datatypes::AcConnector> connectors;
};
struct BptSetupConfig {
message_20::datatypes::BptChannel bpt_channel;
message_20::datatypes::GeneratorMode generator_mode;
std::optional<message_20::datatypes::GridCodeIslandingDetectionMethod> grid_code_detection_method;
};
struct EvseSetupConfig {
std::string evse_id;
std::vector<message_20::datatypes::ServiceCategory> supported_energy_services;
std::vector<message_20::datatypes::Authorization> authorization_services;
std::vector<uint16_t> supported_vas_services;
bool enable_certificate_install_service;
d20::DcTransferLimits dc_limits;
d20::AcTransferLimits ac_limits;
std::vector<ControlMobilityNeedsModes> control_mobility_modes;
std::optional<std::string> custom_protocol{std::nullopt};
std::optional<AcSetupConfig> ac_setup_config{std::nullopt};
std::optional<BptSetupConfig> bpt_setup_config{std::nullopt};
d20::DcTransferLimits powersupply_limits;
};
// This should only have EVSE information
struct SessionConfig {
explicit SessionConfig(EvseSetupConfig);
std::string evse_id;
bool cert_install_service;
std::vector<message_20::datatypes::Authorization> authorization_services;
std::vector<message_20::datatypes::ServiceCategory> supported_energy_transfer_services;
std::vector<std::uint16_t> supported_vas_services;
std::vector<message_20::datatypes::AcParameterList> ac_parameter_list;
std::vector<message_20::datatypes::AcBptParameterList> ac_bpt_parameter_list;
std::vector<message_20::datatypes::DcParameterList> dc_parameter_list;
std::vector<message_20::datatypes::DcBptParameterList> dc_bpt_parameter_list;
std::vector<message_20::datatypes::McsParameterList> mcs_parameter_list;
std::vector<message_20::datatypes::McsBptParameterList> mcs_bpt_parameter_list;
std::vector<message_20::datatypes::InternetParameterList> internet_parameter_list;
std::vector<message_20::datatypes::ParkingParameterList> parking_parameter_list;
DcTransferLimits dc_limits;
AcTransferLimits ac_limits;
DcTransferLimits powersupply_limits;
std::vector<ControlMobilityNeedsModes> supported_control_mobility_modes;
std::optional<std::string> custom_protocol{std::nullopt};
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,175 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <any>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <iso15118/d20/timeout.hpp>
#include <iso15118/message/payload_type.hpp>
#include <iso15118/message/variant.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/session/logger.hpp>
#include "config.hpp"
#include "control_event.hpp"
#include "ev_information.hpp"
#include "ev_session_info.hpp"
#include "session.hpp"
namespace iso15118::d20 {
// forward declare
class ControlEventQueue;
class MessageExchange {
public:
MessageExchange(io::StreamOutputView);
void set_request(std::unique_ptr<message_20::Variant> new_request);
std::unique_ptr<message_20::Variant> pull_request();
message_20::Type peek_request_type() const;
template <typename MessageType> void set_response(const MessageType& msg) {
response_size = message_20::serialize(msg, response);
response_available = true;
payload_type = message_20::PayloadTypeTrait<MessageType>::type;
response_type = message_20::TypeTrait<MessageType>::type;
response_message = msg;
}
template <typename Msg> std::optional<Msg> get_response() {
static_assert(message_20::TypeTrait<Msg>::type != message_20::Type::None, "Unhandled type!");
if (message_20::TypeTrait<Msg>::type != response_type) {
return std::nullopt;
}
try {
return std::any_cast<Msg>(response_message);
} catch (const std::bad_any_cast& ex) {
return std::nullopt;
}
}
std::tuple<bool, size_t, io::v2gtp::PayloadType, message_20::Type> check_and_clear_response();
bool has_response() const {
return response_available;
}
private:
// input
std::unique_ptr<message_20::Variant> request{nullptr};
// output
const io::StreamOutputView response;
size_t response_size{0};
bool response_available{false};
io::v2gtp::PayloadType payload_type;
message_20::Type response_type;
std::any response_message;
};
std::unique_ptr<MessageExchange> create_message_exchange(uint8_t* buf, const size_t len);
struct StateBase;
using BasePointerType = std::unique_ptr<StateBase>;
class Context {
public:
// FIXME (aw): bundle arguments
Context(session::feedback::Callbacks, session::SessionLogger&, d20::SessionConfig, std::optional<PauseContext>&,
const std::optional<ControlEvent>&, MessageExchange&, Timeouts&);
template <typename StateType, typename... Args> BasePointerType create_state(Args&&... args) {
return std::make_unique<StateType>(*this, std::forward<Args>(args)...);
}
std::unique_ptr<message_20::Variant> pull_request();
message_20::Type peek_request_type() const;
template <typename MessageType> void respond(const MessageType& msg) {
message_exchange.set_response(msg);
}
template <typename Msg> std::optional<Msg> get_response() {
return message_exchange.get_response<Msg>();
}
const auto& get_control_event() {
return current_control_event;
}
template <typename T> T const* get_control_event() {
if (not current_control_event.has_value()) {
return nullptr;
}
if (not std::holds_alternative<T>(*current_control_event)) {
return nullptr;
}
return &std::get<T>(*current_control_event);
}
void set_new_vehicle_cert_hash(std::optional<io::sha512_hash_t> hash) {
vehicle_cert_hash = hash;
}
auto get_new_vehicle_cert_hash() const {
return vehicle_cert_hash;
}
void start_timeout(d20::TimeoutType type, uint32_t time_ms) {
timeouts.start_timeout(type, time_ms);
}
void stop_timeout(d20::TimeoutType type) {
timeouts.stop_timeout(type);
}
d20::TimeoutType const* get_active_timeout() {
if (not current_timeout.has_value()) {
return nullptr;
}
return &current_timeout.value();
}
void set_active_timeout(TimeoutType timeout) {
current_timeout = timeout;
}
const session::Feedback feedback;
session::SessionLogger& log;
Session session;
SessionConfig session_config;
// Contains the EV received data
EVSessionInfo session_ev_info;
EVInformation ev_info;
std::optional<d20::PauseContext>& pause_ctx;
bool session_stopped{false};
bool session_paused{false};
std::optional<UpdateDynamicModeParameters> cache_dynamic_mode_parameters;
std::optional<AcTargetPower> cache_ac_target_power;
std::optional<AcPresentPower> cache_ac_present_power;
private:
const std::optional<ControlEvent>& current_control_event;
MessageExchange& message_exchange;
std::optional<io::sha512_hash_t> vehicle_cert_hash{std::nullopt};
Timeouts& timeouts;
std::optional<TimeoutType> current_timeout{std::nullopt};
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <optional>
#include <variant>
#include <vector>
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
#include <iso15118/d20/limits.hpp>
namespace iso15118::d20 {
class CableCheckFinished {
public:
explicit CableCheckFinished(bool success_) : success(success_) {
}
operator bool() const {
return success;
}
private:
bool success;
};
struct PresentVoltageCurrent {
float voltage;
float current;
};
class AuthorizationResponse {
public:
explicit AuthorizationResponse(bool authorized_) : authorized(authorized_) {
}
operator bool() const {
return authorized;
}
private:
bool authorized;
};
class StopCharging {
public:
explicit StopCharging(bool stop_) : stop(stop_) {
}
operator bool() const {
return stop;
}
private:
bool stop;
};
class PauseCharging {
public:
explicit PauseCharging(bool pause_) : pause(pause_) {
}
operator bool() const {
return pause;
}
private:
bool pause;
};
using EnergyServices = std::vector<message_20::datatypes::ServiceCategory>;
class ClosedContactor {
public:
explicit ClosedContactor(bool closed_) : closed(closed_) {
}
operator bool() const {
return closed;
}
private:
bool closed;
};
// TODO(SL): Define this globally for message and states
using SupportedVASs = std::vector<uint16_t>;
using ControlEvent = std::variant<CableCheckFinished, PresentVoltageCurrent, AuthorizationResponse, StopCharging,
PauseCharging, DcTransferLimits, AcTransferLimits, UpdateDynamicModeParameters,
ClosedContactor, AcTargetPower, AcPresentPower, EnergyServices, SupportedVASs>;
} // namespace iso15118::d20

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <mutex>
#include <optional>
#include <queue>
#include "control_event.hpp"
namespace iso15118::d20 {
class ControlEventQueue {
public:
std::optional<ControlEvent> pop();
void push(ControlEvent);
private:
std::queue<ControlEvent> queue;
std::mutex mutex;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <ctime>
#include <optional>
namespace iso15118::d20 {
struct UpdateDynamicModeParameters {
std::optional<std::time_t> departure_time;
std::optional<std::uint8_t> target_soc;
std::optional<std::uint8_t> min_soc;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/message/supported_app_protocol.hpp>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::d20 {
using EVSupportedAppProtocols = everest::lib::util::fixed_vector<message_20::SupportedAppProtocol, 20>;
// Holds information about the EV
struct EVInformation {
EVSupportedAppProtocols ev_supported_app_protocols;
message_20::SupportedAppProtocol selected_app_protocol;
std::string evcc_id;
std::optional<std::string> ev_tls_leaf_cert;
std::optional<std::string> ev_tls_sub_ca_1_cert;
std::optional<std::string> ev_tls_sub_ca_2_cert;
std::optional<std::string> ev_tls_root_cert;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/message/common_types.hpp>
#include <iso15118/session/feedback.hpp>
namespace iso15118::d20 {
// Holds information reported by the EV
struct EVSessionInfo {
session::feedback::EvTransferLimits ev_transfer_limits;
session::feedback::EvSEControlMode ev_control_mode;
std::vector<message_20::datatypes::ServiceCategory> ev_energy_services;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
namespace dt = message_20::datatypes;
template <typename T> struct Limit {
T max;
T min;
};
struct Limits {
Limit<message_20::datatypes::RationalNumber> power;
Limit<message_20::datatypes::RationalNumber> current;
};
struct DcTransferLimits {
Limits charge_limits;
std::optional<Limits> discharge_limits;
Limit<message_20::datatypes::RationalNumber> voltage;
std::optional<message_20::datatypes::RationalNumber> power_ramp_limit;
};
struct AcTransferLimits {
Limit<dt::RationalNumber> charge_power;
std::optional<Limit<dt::RationalNumber>> charge_power_L2;
std::optional<Limit<dt::RationalNumber>> charge_power_L3;
dt::RationalNumber nominal_frequency;
std::optional<dt::RationalNumber> max_power_asymmetry;
std::optional<dt::RationalNumber> power_ramp_limitation;
std::optional<Limit<dt::RationalNumber>> discharge_power;
std::optional<Limit<dt::RationalNumber>> discharge_power_L2;
std::optional<Limit<dt::RationalNumber>> discharge_power_L3;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,152 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
#include <map>
#include <optional>
#include <variant>
#include <vector>
#include <iso15118/io/sha_hash.hpp>
#include <iso15118/message/common_types.hpp>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::d20 {
namespace dt = message_20::datatypes;
// Key: service IDs, value: vector with the parameter set ids
using CustomVasList = std::map<std::uint16_t, std::vector<uint16_t>>;
struct OfferedServices {
everest::lib::util::fixed_vector<dt::Authorization, 2> auth_services;
std::vector<dt::ServiceCategory> energy_services;
std::vector<uint16_t> vas_services;
std::map<uint8_t, dt::AcParameterList> ac_parameter_list;
std::map<uint8_t, dt::AcBptParameterList> ac_bpt_parameter_list;
std::map<uint8_t, dt::DcParameterList> dc_parameter_list;
std::map<uint8_t, dt::DcBptParameterList> dc_bpt_parameter_list;
std::map<uint8_t, dt::McsParameterList> mcs_parameter_list;
std::map<uint8_t, dt::McsBptParameterList> mcs_bpt_parameter_list;
std::map<uint8_t, dt::InternetParameterList> internet_parameter_list;
std::map<uint8_t, dt::ParkingParameterList> parking_parameter_list;
CustomVasList custom_vas_list;
};
struct SelectedServiceParameters {
dt::ServiceCategory selected_energy_service;
std::variant<dt::AcConnector, dt::DcConnector, dt::McsConnector> selected_connector;
dt::ControlMode selected_control_mode;
dt::MobilityNeedsMode selected_mobility_needs_mode;
dt::Pricing selected_pricing;
// BPT
std::optional<dt::BptChannel> selected_bpt_channel;
std::optional<dt::GeneratorMode> selected_generator_mode;
// AC specific
std::optional<float> evse_nominal_voltage;
std::optional<dt::GridCodeIslandingDetectionMethod> selected_grid_code_method;
SelectedServiceParameters() = default;
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::DcConnector dc_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::DcConnector dc_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
dt::BptChannel channel_, dt::GeneratorMode generator_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::McsConnector mcs_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::McsConnector mcs_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
dt::BptChannel channel_, dt::GeneratorMode generator_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::AcConnector ac_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
float nominal_voltage_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::AcConnector ac_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
dt::BptChannel channel_, dt::GeneratorMode generator_, float nominal_voltage_,
dt::GridCodeIslandingDetectionMethod grid_code_method_);
};
// Todo(sl): missing services
// WPT -> ControlMode, Pricing
// DC_ACDP -> ControlMode, MobilityNeedsMode
// DC_ACDP_BPT -> ControlMode, MobilityNeedsMode, BPTChannel
struct SelectedVasParameter {
std::vector<dt::ServiceCategory> vas_services;
dt::Protocol internet_protocol;
dt::Port internet_port;
dt::IntendedService parking_intended_service;
dt::ParkingStatus parking_status;
};
// TODO(SL): How to handle d2 pause? Move Struct to a seperate header file?
// TODO(SL): Missing handling scheduletuple in schedule mode [V2G20-1058]
struct PauseContext {
io::sha512_hash_t vehicle_cert_session_id_hash{};
std::array<uint8_t, 8> old_session_id{};
SelectedServiceParameters selected_service_parameters{};
};
class Session {
// TODO(sl): move to a common defs file
static constexpr auto ID_LENGTH = 8;
public:
Session();
Session(const PauseContext& pause_ctx);
Session(SelectedServiceParameters);
Session(OfferedServices);
std::array<uint8_t, ID_LENGTH> get_id() const {
return id;
}
bool find_energy_parameter_set_id(const dt::ServiceCategory service, int16_t id);
bool find_vas_parameter_set_id(const uint16_t vas_service, int16_t id);
void selected_service_parameters(const dt::ServiceCategory service, const uint16_t id);
void selected_service_parameters(const uint16_t vas_service, const uint16_t id);
auto get_selected_services() const& {
return selected_services;
}
bool is_ac_charger() const {
return selected_services.selected_energy_service == dt::ServiceCategory::AC or
selected_services.selected_energy_service == dt::ServiceCategory::AC_BPT;
}
bool is_dc_charger() const {
return selected_services.selected_energy_service == dt::ServiceCategory::DC or
selected_services.selected_energy_service == dt::ServiceCategory::DC_BPT or
selected_services.selected_energy_service == dt::ServiceCategory::MCS or
selected_services.selected_energy_service == dt::ServiceCategory::MCS_BPT;
}
~Session();
OfferedServices offered_services;
bool service_renegotiation_supported{false};
private:
// NOTE (aw): could be const
std::array<uint8_t, ID_LENGTH> id{};
SelectedServiceParameters selected_services{};
SelectedVasParameter selected_vas_services{};
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
struct AC_ChargeLoop : public StateBase {
AC_ChargeLoop(Context& ctx) : StateBase(ctx, StateID::AC_ChargeLoop) {
}
void enter() final;
Result feed(Event) final;
private:
float target_frequency{0};
bool stop{false};
bool pause{false};
UpdateDynamicModeParameters dynamic_parameters{};
AcTargetPower target_powers{};
AcPresentPower present_powers{};
bool first_entry_in_charge_loop{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/d20/ac_powers.hpp>
namespace iso15118::d20::state {
struct AC_ChargeParameterDiscovery : public StateBase {
AC_ChargeParameterDiscovery(Context& ctx) : StateBase(ctx, StateID::AC_ChargeParameterDiscovery) {
}
void enter() final;
Result feed(Event) final;
private:
AcPresentPower present_powers;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/message/authorization.hpp>
namespace iso15118::d20::state {
struct Authorization : public StateBase {
public:
Authorization(Context& ctx) : StateBase(ctx, StateID::Authorization){};
void enter() final;
Result feed(Event) final;
private:
message_20::datatypes::AuthStatus authorization_status{message_20::datatypes::AuthStatus::Pending};
bool first_req_msg{true};
bool timeout_ongoing_reached{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct AuthorizationSetup : public StateBase {
AuthorizationSetup(Context& ctx) : StateBase(ctx, StateID::AuthorizationSetup) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_CableCheck : public StateBase {
DC_CableCheck(Context& ctx) : StateBase(ctx, StateID::DC_CableCheck) {
}
void enter() final;
Result feed(Event) final;
private:
bool cable_check_initiated{false};
bool cable_check_done{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <cstdint>
#include <optional>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
struct DC_ChargeLoop : public StateBase {
DC_ChargeLoop(Context& ctx) : StateBase(ctx, StateID::DC_ChargeLoop) {
}
void enter() final;
Result feed(Event) final;
private:
float present_voltage{0};
float present_current{0};
bool stop{false};
bool pause{false};
UpdateDynamicModeParameters dynamic_parameters;
bool first_entry_in_charge_loop{true};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_ChargeParameterDiscovery : public StateBase {
DC_ChargeParameterDiscovery(Context& ctx) : StateBase(ctx, StateID::DC_ChargeParameterDiscovery) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_PreCharge : public StateBase {
DC_PreCharge(Context& ctx) : StateBase(ctx, StateID::DC_PreCharge) {
}
void enter() final;
Result feed(Event) final;
private:
bool pre_charge_initiated{false};
float present_voltage{0};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_WeldingDetection : public StateBase {
DC_WeldingDetection(Context& ctx) : StateBase(ctx, StateID::DC_WeldingDetection) {
}
void enter() final;
Result feed(Event) final;
private:
float present_voltage{0};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/message/power_delivery.hpp>
#include <optional>
namespace iso15118::d20::state {
struct PowerDelivery : public StateBase {
PowerDelivery(Context& ctx) : StateBase(ctx, StateID::PowerDelivery) {
}
void enter() final;
Result feed(Event) final;
private:
float present_voltage{0};
bool ac_connector_closed{false};
std::optional<message_20::PowerDeliveryRequest> previous_req;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <cstdint>
#include <optional>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
struct ScheduleExchange : public StateBase {
ScheduleExchange(Context& ctx) : StateBase(ctx, StateID::ScheduleExchange) {
}
void enter() final;
Result feed(Event) final;
private:
UpdateDynamicModeParameters dynamic_parameters;
bool first_req_msg{true};
bool timeout_ongoing_reached{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct ServiceDetail : public StateBase {
public:
ServiceDetail(Context& ctx) : StateBase(ctx, StateID::ServiceDetail) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct ServiceDiscovery : public StateBase {
public:
ServiceDiscovery(Context& ctx) : StateBase(ctx, StateID::ServiceDiscovery) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct ServiceSelection : public StateBase {
ServiceSelection(Context& ctx) : StateBase(ctx, StateID::ServiceSelection) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
#include "../states.hpp"
namespace iso15118::d20::state {
struct SessionSetup : public StateBase {
SessionSetup(Context& ctx) : StateBase(ctx, StateID::SessionSetup) {
}
void enter() final;
Result feed(Event) final;
private:
std::string evse_id;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct SessionStop : public StateBase {
SessionStop(Context& ctx) : StateBase(ctx, StateID::SessionStop) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct SupportedAppProtocol : public StateBase {
public:
SupportedAppProtocol(Context& ctx) : StateBase(ctx, StateID::SupportedAppProtocol) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include "context.hpp"
namespace iso15118::d20 {
class Context;
enum class Event {
RESET,
V2GTP_MESSAGE,
CONTROL_MESSAGE,
TIMEOUT,
// internal events
FAILED,
};
enum class StateID {
SupportedAppProtocol,
SessionSetup,
AuthorizationSetup,
Authorization,
ServiceDetail,
ServiceDiscovery,
ServiceSelection,
AC_ChargeParameterDiscovery,
AC_ChargeLoop,
DC_ChargeParameterDiscovery,
DC_PreCharge,
DC_ChargeLoop,
DC_WeldingDetection,
DC_CableCheck,
PowerDelivery,
ScheduleExchange,
SessionStop
};
struct Result {
constexpr Result() = default;
Result(BasePointerType result_state) : unhandled(false), new_state(std::move(result_state)) {
}
bool unhandled{true};
BasePointerType new_state{nullptr};
};
struct StateBase {
using ContainerType = BasePointerType;
using EventType = Event;
StateBase(Context& ctx, StateID id) : m_ctx(ctx), m_id(id){};
virtual ~StateBase() = default;
StateID get_id() const {
return m_id;
}
virtual void enter(){};
virtual Result feed(Event) = 0;
virtual void leave(){};
protected:
Context& m_ctx;
private:
StateID m_id;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <optional>
#include <vector>
#include <iso15118/io/time.hpp>
namespace iso15118::d20 {
template <typename T> constexpr auto to_underlying_value(T t) {
return static_cast<std::underlying_type_t<T>>(t);
}
enum class TimeoutType : uint8_t {
SEQUENCE = 0,
PERFORMANCE,
ONGOING,
CONTACTOR,
};
constexpr uint8_t TIMEOUT_TYPE_SIZE = 4;
static_assert(TIMEOUT_TYPE_SIZE == to_underlying_value(TimeoutType::CONTACTOR) + 1,
"TIMEOUT_TYPE_SIZE should be in sync with the TimeoutType enum definition");
constexpr auto TIMEOUT_ONGOING = 1000 * 55;
constexpr auto TIMEOUT_SEQUENCE = 1000 * 60;
constexpr auto TIMEOUT_EIM_ONGOING = 1000 * 60 * 3;
class Timeouts {
public:
explicit Timeouts() = default;
~Timeouts() = default;
void start_timeout(TimeoutType type, uint32_t timeout_ms);
void stop_timeout(TimeoutType type);
void reset_timeout(TimeoutType type);
std::optional<std::vector<TimeoutType>> check();
private:
std::array<std::optional<Timeout>, TIMEOUT_TYPE_SIZE> timeouts;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,99 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <stdexcept>
#include <cbv2g/common/exi_bitstream.h>
#include <iso15118/io/stream_view.hpp>
#define CB2CPP_STRING(property) (std::string(property.characters, property.charactersLen))
#define CPP2CB_STRING(in, out) \
if (in.length() > sizeof(out.characters)) { \
throw std::runtime_error("String too long"); \
} \
in.copy(out.characters, in.length()); \
out.charactersLen = in.length()
#define CPP2CB_BYTES(in, out) \
if (in.size() > sizeof(out.bytes)) { \
throw std::runtime_error("Byte vector too long"); \
} \
std::copy(in.begin(), in.end(), out.bytes); \
out.bytesLen = in.size()
#define CB2CPP_BYTES(in, out) \
if (in.bytesLen > out.size()) { \
throw std::runtime_error("Byte array too long"); \
} \
std::copy(std::begin(in.bytes), std::end(in.bytes), out.begin());
#define CB_SET_USED(property) (property##_isUsed = 1)
#define CB2CPP_ASSIGN_IF_USED(in, out) \
if (in##_isUsed) { \
out = in; \
}
#define CPP2CB_ASSIGN_IF_USED(in, out) \
if (in) { \
out = in.value(); \
} \
\
out##_isUsed = static_cast<bool>(in);
#define CB2CPP_CONVERT_IF_USED(in, out) \
if (in##_isUsed) { \
convert(in, out.emplace()); \
}
#define CPP2CB_CONVERT_IF_USED(in, out) \
if (in) { \
convert(in.value(), out); \
} \
\
out##_isUsed = static_cast<bool>(in);
#define CPP2CB_STRING_IF_USED(in, out) \
if (in) { \
CPP2CB_STRING(in.value(), out); \
} \
\
out##_isUsed = static_cast<bool>(in);
#define CB2CPP_STRING_IF_USED(in, out) \
if (in##_isUsed) { \
out = CB2CPP_STRING(in); \
}
#define CB2CPP_BYTES_IF_USED(in, out) \
if (in##_isUsed) { \
CB2CPP_BYTES(in, out.emplace()); \
}
template <typename T1, typename T2> void cb_convert_enum(const T1& in, T2& out) {
out = static_cast<T2>(in);
}
exi_bitstream_t get_exi_input_stream(const iso15118::io::StreamInputView&);
exi_bitstream_t get_exi_output_stream(const iso15118::io::StreamOutputView&);
namespace iso15118::message_20 {
template <typename MessageType> int serialize_to_exi(const MessageType& in, exi_bitstream_t& out);
template <typename MessageType>
size_t serialize_helper(const MessageType& in, const io::StreamOutputView& stream_view) {
auto out = get_exi_output_stream(stream_view);
const auto error = serialize_to_exi(in, out);
if (error != 0) {
throw std::runtime_error("Could not encode exi: " + std::to_string(error));
}
return exi_bitstream_get_length(&out);
}
} // namespace iso15118::message_20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/context.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
// FIXME (aw): not sure about correct signature here for RVO
template <typename Response, typename ResponseCode> Response& response_with_code(Response& res, ResponseCode code) {
// FIXME (aw): could add an static_assert here that ResponseCode is an enum?
res.response_code = code;
return res;
}
bool validate_and_setup_header(message_20::Header&, const Session&, const decltype(message_20::Header::session_id)&);
void setup_header(message_20::Header&, const Session&);
void send_sequence_error(const message_20::Type, d20::Context&);
} // namespace iso15118::d20

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 205 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/ac_charge_loop.hpp>
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
#include <iso15118/d20/limits.hpp>
namespace iso15118::d20::state {
message_20::AC_ChargeLoopResponse handle_request(const message_20::AC_ChargeLoopRequest& req,
const d20::Session& session, bool stop, bool pause,
float target_frequency, const AcTargetPower& target_powers,
const AcPresentPower& present_powers,
const UpdateDynamicModeParameters& dynamic_parameters);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/ac_charge_parameter_discovery.hpp>
namespace iso15118::d20::state {
message_20::AC_ChargeParameterDiscoveryResponse
handle_request(const message_20::AC_ChargeParameterDiscoveryRequest& req, const d20::Session& session,
const d20::AcTransferLimits& limits, const d20::AcPresentPower& powers);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/authorization.hpp>
namespace iso15118::d20::state {
message_20::AuthorizationResponse handle_request(const message_20::AuthorizationRequest& req,
const d20::Session& session,
const message_20::datatypes::AuthStatus& authorization_status,
bool timeout_reached);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/authorization_setup.hpp>
namespace iso15118::d20::state {
message_20::AuthorizationSetupResponse
handle_request(const message_20::AuthorizationSetupRequest& req, d20::Session& session, bool cert_install_service,
const std::vector<message_20::datatypes::Authorization>& authorization_services);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_cable_check.hpp>
namespace iso15118::d20::state {
message_20::DC_CableCheckResponse handle_request(const message_20::DC_CableCheckRequest& req,
const d20::Session& session, bool cable_check_done);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <ctime>
#include <optional>
#include <tuple>
#include <variant>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_charge_loop.hpp>
#include <iso15118/session/feedback.hpp>
namespace iso15118::d20::state {
message_20::DC_ChargeLoopResponse handle_request(const message_20::DC_ChargeLoopRequest& req,
const d20::Session& session, const float present_voltage,
const float present_current, const bool stop, const bool pause,
const DcTransferLimits& dc_limits,
const UpdateDynamicModeParameters& dynamic_parameters);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_charge_parameter_discovery.hpp>
namespace iso15118::d20::state {
message_20::DC_ChargeParameterDiscoveryResponse
handle_request(const message_20::DC_ChargeParameterDiscoveryRequest& req, const d20::Session& session,
const d20::DcTransferLimits& dc_limits);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <tuple>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_pre_charge.hpp>
#include <iso15118/session/feedback.hpp>
namespace iso15118::d20::state {
message_20::DC_PreChargeResponse handle_request(const message_20::DC_PreChargeRequest& req, const d20::Session& session,
const float present_voltage);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_welding_detection.hpp>
namespace iso15118::d20::state {
message_20::DC_WeldingDetectionResponse handle_request(const message_20::DC_WeldingDetectionRequest& req,
const d20::Session& session, const float present_voltage);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/power_delivery.hpp>
namespace iso15118::d20::state {
message_20::PowerDeliveryResponse handle_request(const message_20::PowerDeliveryRequest& req,
const d20::Session& session, bool contactor_error);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/schedule_exchange.hpp>
#include <cstdint>
#include <ctime>
#include <optional>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
message_20::ScheduleExchangeResponse handle_request(const message_20::ScheduleExchangeRequest& req,
const d20::Session& session,
const message_20::datatypes::RationalNumber& max_power,
const UpdateDynamicModeParameters& dynamic_parameters,
bool timeout_reached);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/service_detail.hpp>
namespace iso15118::d20::state {
message_20::ServiceDetailResponse handle_request(const message_20::ServiceDetailRequest& req, d20::Session& session,
const d20::SessionConfig& config,
const std::optional<dt::ServiceParameterList>& custom_vas_parameters);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <vector>
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/common_types.hpp>
#include <iso15118/message/service_discovery.hpp>
namespace iso15118::d20::state {
message_20::ServiceDiscoveryResponse
handle_request(const message_20::ServiceDiscoveryRequest& req, d20::Session& session,
const std::vector<message_20::datatypes::ServiceCategory>& energy_services,
const std::vector<uint16_t>& vas_services,
std::vector<message_20::datatypes::ServiceCategory>& ev_energy_services);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/service_selection.hpp>
namespace iso15118::d20::state {
message_20::ServiceSelectionResponse handle_request(const message_20::ServiceSelectionRequest& req,
d20::Session& session);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/session_setup.hpp>
namespace iso15118::d20::state {
message_20::SessionSetupResponse handle_request(const message_20::SessionSetupRequest& req, const d20::Session& session,
const std::string& evse_id, bool new_session);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/session_stop.hpp>
namespace iso15118::d20::state {
message_20::SessionStopResponse handle_request(const message_20::SessionStopRequest& req, const d20::Session& session);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/io/log_levels.hpp>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <string>
namespace iso15118 {
void logf(const char* fmt, ...);
void logf(const LogLevel&, const char* fmt, ...);
void logf_error(const char* fmt, ...);
void logf_warning(const char* fmt, ...);
void logf_info(const char* fmt, ...);
void logf_debug(const char* fmt, ...);
void logf_trace(const char* fmt, ...);
void vlogf(const char* fmt, va_list ap);
void vlogf(const LogLevel&, const char* fmt, va_list ap);
void log(const LogLevel&, const std::string&);
void log_and_throw(const char* msg);
std::string adding_err_msg(const std::string& msg);
template <typename CallbackType, typename... Args> bool call_if_available(const CallbackType& callback, Args... args) {
if (not callback) {
return false;
}
std::invoke(callback, std::forward<Args>(args)...);
return true;
}
// Note(sl): Copied from https://en.cppreference.com/w/cpp/utility/intcmp because libiso15118 uses C++17
template <class T, class U> constexpr bool cmp_equal(T t, U u) noexcept {
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
return t == u;
else if constexpr (std::is_signed_v<T>)
return t >= 0 && std::make_unsigned_t<T>(t) == u;
else
return u >= 0 && std::make_unsigned_t<U>(u) == t;
}
} // namespace iso15118

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
namespace iso15118::io {
std::string log_openssl_error(const std::string& error_msg);
void log_and_raise_openssl_error(const std::string& error_msg);
} // namespace iso15118::io

View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <memory>
#include <string>
#include <netinet/in.h>
namespace iso15118::io {
bool check_and_update_interface(std::string& interface_name);
bool get_first_sockaddr_in6_for_interface(const std::string& interface_name, sockaddr_in6& address);
std::unique_ptr<char[]> sockaddr_in6_to_name(const sockaddr_in6&);
} // namespace iso15118::io

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cassert>
#include <iso15118/message/variant.hpp>
#include "cb_exi.hpp"
namespace iso15118::message_20 {
struct VariantAccess {
// input
exi_bitstream_t input_stream;
// output
void*& data;
iso15118::message_20::Type& type;
iso15118::message_20::Variant::CustomDeleter& custom_deleter;
std::string& error;
template <typename MessageType, typename CbExiMessageType> void insert_type(const CbExiMessageType& in) {
assert(data == nullptr);
data = new MessageType;
type = iso15118::message_20::TypeTrait<MessageType>::type;
custom_deleter = [](void* ptr) { delete static_cast<MessageType*>(ptr); };
convert(in, *static_cast<MessageType*>(data));
};
};
template <typename CbExiMessageType> void insert_type(VariantAccess& va, const CbExiMessageType&);
} // namespace iso15118::message_20

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <functional>
#include <optional>
#include "ipv6_endpoint.hpp"
#include <iso15118/io/sha_hash.hpp>
namespace iso15118::io {
enum class ConnectionEvent {
ACCEPTED,
OPEN,
NEW_DATA,
CLOSED,
};
using ConnectionEventCallback = std::function<void(ConnectionEvent)>;
struct ReadResult {
bool would_block{true};
size_t bytes_read{0};
};
struct IConnection {
virtual void set_event_callback(const ConnectionEventCallback&) = 0;
virtual Ipv6EndPoint get_public_endpoint() const = 0;
virtual void write(const uint8_t* buf, size_t len) = 0;
virtual ReadResult read(uint8_t* buf, size_t len) = 0;
virtual void close() = 0;
virtual std::optional<sha512_hash_t> get_vehicle_cert_hash() const = 0;
virtual ~IConnection() = default;
};
} // namespace iso15118::io

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "connection_abstract.hpp"
#include <iso15118/config.hpp>
#include <iso15118/io/poll_manager.hpp>
namespace iso15118::io {
class ConnectionPlain : public IConnection {
public:
ConnectionPlain(PollManager&, const std::string& interface_name);
void set_event_callback(const ConnectionEventCallback&) final;
Ipv6EndPoint get_public_endpoint() const final;
void write(const uint8_t* buf, size_t len) final;
ReadResult read(uint8_t* buf, size_t len) final;
void close() final;
std::optional<sha512_hash_t> get_vehicle_cert_hash() const final {
return std::nullopt;
}
~ConnectionPlain();
private:
PollManager& poll_manager;
Ipv6EndPoint end_point;
int fd{-1};
bool connection_open{false};
ConnectionEventCallback event_callback{nullptr};
void handle_connect();
void handle_data();
};
} // namespace iso15118::io

View File

@@ -0,0 +1,46 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include "connection_abstract.hpp"
#include <memory>
#include <optional>
#include <iso15118/config.hpp>
#include <iso15118/io/poll_manager.hpp>
#include <iso15118/io/sha_hash.hpp>
namespace iso15118::io {
// forward declaration
struct SSLContext;
class ConnectionSSL : public IConnection {
public:
ConnectionSSL(PollManager&, const std::string& interface_name, const config::SSLConfig&);
void set_event_callback(const ConnectionEventCallback&) final;
Ipv6EndPoint get_public_endpoint() const final;
void write(const uint8_t* buf, size_t len) final;
ReadResult read(uint8_t* buf, size_t len) final;
void close() final;
std::optional<sha512_hash_t> get_vehicle_cert_hash() const final;
~ConnectionSSL();
private:
PollManager& poll_manager;
std::unique_ptr<SSLContext> ssl;
Ipv6EndPoint end_point;
ConnectionEventCallback event_callback{nullptr};
bool handshake_complete{false};
void handle_connect();
void handle_data();
};
} // namespace iso15118::io

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
namespace iso15118::io {
struct Ipv6EndPoint {
uint16_t port;
uint16_t address[8];
};
} // namespace iso15118::io

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
namespace iso15118 {
enum class LogLevel {
Error,
Warning,
Info,
Debug,
Trace,
};
} // namespace iso15118

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <functional>
#include <string>
#include "log_levels.hpp"
namespace iso15118::io {
void set_logging_callback(const std::function<void(LogLevel, std::string)>&);
} // namespace iso15118::io

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <functional>
#include <map>
#include <vector>
#include <poll.h>
namespace iso15118::io {
using PollCallback = const std::function<void()>;
struct PollSet {
std::vector<struct pollfd> fds;
std::vector<PollCallback*> callbacks;
};
class PollManager {
public:
PollManager();
void register_fd(int fd, PollCallback& poll_callback);
void unregister_fd(int fd);
void poll(int timeout_ms);
void abort();
private:
std::map<int, PollCallback> registered_fds;
PollSet poll_set;
int event_fd{-1};
};
} // namespace iso15118::io

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
namespace iso15118::io::v2gtp {
enum class Security : uint8_t {
TLS = 0x00,
NO_TRANSPORT_SECURITY = 0x10,
};
enum class TransportProtocol : uint8_t {
TCP = 0x00,
RESERVED_FOR_UDP = 0x10,
};
static constexpr auto SDP_SERVER_PORT = 15118;
enum class PayloadType : uint16_t {
SAP = 0x8001,
Part20Main = 0x8002,
Part20AC = 0x8003,
Part20DC = 0x8004,
};
} // namespace iso15118::io::v2gtp

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <cstdint>
#include "sdp.hpp"
namespace iso15118::io {
// FIXME (aw): these shouldn't be necessary public - but nice to have
static constexpr uint8_t SDP_PROTOCOL_VERSION = 0x01;
static constexpr uint8_t SDP_INVERSE_PROTOCOL_VERSION = 0xFE;
// FIXME (aw): should be called V2GTP or SDP buffer
class SdpPacket {
public:
static constexpr auto V2GTP_HEADER_SIZE = 8;
enum class State {
BUFFER_EMPTY,
HEADER_READ,
COMPLETE,
// failed states
INVALID_HEADER,
PAYLOAD_TOO_LONG,
};
auto get_state() const {
return state;
}
auto is_complete() const {
return state == State::COMPLETE;
}
auto get_payload_length() const {
return (state == State::COMPLETE) ? (length - V2GTP_HEADER_SIZE) : 0;
}
v2gtp::PayloadType get_payload_type() const;
uint8_t const* get_payload_buffer() const {
return buffer + V2GTP_HEADER_SIZE;
}
uint8_t const* get_buffer() const {
return buffer;
}
uint8_t* get_current_buffer_pos() {
return buffer + bytes_read;
}
size_t get_remaining_buffer_capacity() const {
return sizeof(buffer) - bytes_read;
}
size_t get_remaining_bytes_to_read() const;
void update_read_bytes(size_t len);
private:
void parse_header();
State state{State::BUFFER_EMPTY};
uint8_t buffer[2048];
size_t bytes_read{0};
size_t length; // length includes V2GTP_HEADER_SIZE
};
} // namespace iso15118::io

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <atomic>
#include <cstdint>
#include <string>
#include <netinet/in.h>
#include "ipv6_endpoint.hpp"
#include "sdp.hpp"
namespace iso15118::io {
struct PeerRequestContext {
explicit PeerRequestContext(bool valid_) : valid(valid_){};
v2gtp::Security security;
v2gtp::TransportProtocol transport_protocol;
struct sockaddr_in6 address;
operator bool() const {
return valid;
}
private:
const bool valid;
};
class SdpServer {
public:
explicit SdpServer(const std::string& interface_name);
~SdpServer();
PeerRequestContext get_peer_request();
void send_response(const PeerRequestContext&, const Ipv6EndPoint&);
auto get_fd() const {
return fd;
}
void set_dlink_ready(bool ready);
bool is_dlink_ready() const;
private:
int fd{-1};
uint8_t udp_buffer[2048];
std::atomic<bool> dlink_ready_{false};
};
class TlsKeyLoggingServer {
public:
TlsKeyLoggingServer(const std::string& interface_name, uint16_t port);
~TlsKeyLoggingServer();
ssize_t send(const char* line);
auto get_fd() const {
return fd;
}
auto get_port() const {
return port;
}
private:
int fd{-1};
uint16_t port{0};
sockaddr_in6 destination_address{};
};
} // namespace iso15118::io

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
namespace iso15118::io {
constexpr std::size_t sha_512_hash_size = 64;
using sha512_hash_t = std::array<uint8_t, sha_512_hash_size>;
} // namespace iso15118::io

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <cstdint>
namespace iso15118::io {
struct StreamInputView {
uint8_t const* payload;
size_t payload_len;
};
struct StreamOutputView {
uint8_t* payload;
size_t payload_len;
operator StreamInputView() const {
return {payload, payload_len};
}
};
} // namespace iso15118::io

View File

@@ -0,0 +1,51 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
namespace iso15118 {
using TimePoint = std::chrono::steady_clock::time_point;
inline TimePoint get_current_time_point() {
return std::chrono::steady_clock::now();
}
inline TimePoint offset_time_point_by_ms(const TimePoint& time_point, int32_t offset) {
return time_point + std::chrono::milliseconds(offset);
}
inline int32_t get_timeout_ms_until(const TimePoint& until, int32_t max_timeout_ms) {
const auto duration =
std::chrono::duration_cast<std::chrono::duration<int32_t, std::milli>>(until - get_current_time_point());
const auto timeout_ms = duration.count();
// if the timeout is already reached, return 0 to trigger an immediate timeout event
if (timeout_ms <= 0) {
return 0;
}
return timeout_ms < max_timeout_ms ? timeout_ms : max_timeout_ms;
}
class Timeout {
public:
explicit Timeout(uint32_t timeout_ms) {
timeout_point = get_current_time_point() + std::chrono::milliseconds(timeout_ms);
};
~Timeout() = default;
bool is_reached() {
return get_current_time_point() >= timeout_point;
}
TimePoint get_timeout_point() const {
return timeout_point;
}
private:
TimePoint timeout_point{};
};
} // namespace iso15118

View File

@@ -0,0 +1,122 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <variant>
#include <vector>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct Scheduled_AC_CLReqControlMode : Scheduled_CLReqControlMode {
std::optional<RationalNumber> max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
std::optional<RationalNumber> min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
RationalNumber present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
std::optional<RationalNumber> present_reactive_power;
std::optional<RationalNumber> present_reactive_power_L2;
std::optional<RationalNumber> present_reactive_power_L3;
};
struct BPT_Scheduled_AC_CLReqControlMode : Scheduled_AC_CLReqControlMode {
std::optional<RationalNumber> max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
std::optional<RationalNumber> min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
};
struct Dynamic_AC_CLReqControlMode : Dynamic_CLReqControlMode {
RationalNumber max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
RationalNumber min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
RationalNumber present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
RationalNumber present_reactive_power;
std::optional<RationalNumber> present_reactive_power_L2;
std::optional<RationalNumber> present_reactive_power_L3;
};
struct BPT_Dynamic_AC_CLReqControlMode : Dynamic_AC_CLReqControlMode {
RationalNumber max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
RationalNumber min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
std::optional<RationalNumber> max_v2x_energy_request;
std::optional<RationalNumber> min_v2x_energy_request;
};
struct Scheduled_AC_CLResControlMode : Scheduled_CLResControlMode {
std::optional<RationalNumber> target_active_power;
std::optional<RationalNumber> target_active_power_L2;
std::optional<RationalNumber> target_active_power_L3;
std::optional<RationalNumber> target_reactive_power;
std::optional<RationalNumber> target_reactive_power_L2;
std::optional<RationalNumber> target_reactive_power_L3;
std::optional<RationalNumber> present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
};
struct BPT_Scheduled_AC_CLResControlMode : Scheduled_AC_CLResControlMode {};
struct Dynamic_AC_CLResControlMode : Dynamic_CLResControlMode {
RationalNumber target_active_power;
std::optional<RationalNumber> target_active_power_L2;
std::optional<RationalNumber> target_active_power_L3;
std::optional<RationalNumber> target_reactive_power;
std::optional<RationalNumber> target_reactive_power_L2;
std::optional<RationalNumber> target_reactive_power_L3;
std::optional<RationalNumber> present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
};
struct BPT_Dynamic_AC_CLResControlMode : Dynamic_AC_CLResControlMode {};
} // namespace datatypes
struct AC_ChargeLoopRequest {
Header header;
// the following 2 are inherited from ChargeLoopReq
std::optional<datatypes::DisplayParameters> display_parameters;
bool meter_info_requested;
std::variant<datatypes::Scheduled_AC_CLReqControlMode, datatypes::BPT_Scheduled_AC_CLReqControlMode,
datatypes::Dynamic_AC_CLReqControlMode, datatypes::BPT_Dynamic_AC_CLReqControlMode>
control_mode;
};
struct AC_ChargeLoopResponse {
Header header;
datatypes::ResponseCode response_code;
// the following 3 are inherited from ChargeLoopRes
std::optional<datatypes::EvseStatus> status;
std::optional<datatypes::MeterInfo> meter_info;
std::optional<datatypes::Receipt> receipt;
std::optional<datatypes::RationalNumber> target_frequency;
std::variant<datatypes::Scheduled_AC_CLResControlMode, datatypes::BPT_Scheduled_AC_CLResControlMode,
datatypes::Dynamic_AC_CLResControlMode, datatypes::BPT_Dynamic_AC_CLResControlMode>
control_mode = datatypes::Scheduled_AC_CLResControlMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <variant>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct AC_CPDReqEnergyTransferMode {
RationalNumber max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
RationalNumber min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
};
struct BPT_AC_CPDReqEnergyTransferMode : AC_CPDReqEnergyTransferMode {
RationalNumber max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
RationalNumber min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
};
struct AC_CPDResEnergyTransferMode {
RationalNumber max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
RationalNumber min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
RationalNumber nominal_frequency;
std::optional<RationalNumber> max_power_asymmetry;
std::optional<RationalNumber> power_ramp_limitation;
std::optional<RationalNumber> present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
};
struct BPT_AC_CPDResEnergyTransferMode : AC_CPDResEnergyTransferMode {
RationalNumber max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
RationalNumber min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
};
} // namespace datatypes
struct AC_ChargeParameterDiscoveryRequest {
Header header;
std::variant<datatypes::AC_CPDReqEnergyTransferMode, datatypes::BPT_AC_CPDReqEnergyTransferMode> transfer_mode;
};
struct AC_ChargeParameterDiscoveryResponse {
Header header;
datatypes::ResponseCode response_code;
std::variant<datatypes::AC_CPDResEnergyTransferMode, datatypes::BPT_AC_CPDResEnergyTransferMode> transfer_mode =
datatypes::AC_CPDResEnergyTransferMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
#include <variant>
#include <vector>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
enum class AuthStatus {
Accepted = 0,
Pending = 1,
Rejected = 2,
};
struct EIM_ASReqAuthorizationMode {};
struct PnC_ASReqAuthorizationMode {
std::string id;
GenChallenge gen_challenge;
ContractCertificateChain contract_certificate_chain;
};
} // namespace datatypes
struct AuthorizationRequest {
Header header;
datatypes::Authorization selected_authorization_service;
std::variant<datatypes::EIM_ASReqAuthorizationMode, datatypes::PnC_ASReqAuthorizationMode> authorization_mode;
};
struct AuthorizationResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Processing evse_processing{datatypes::Processing::Finished};
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
using SupportedProvidersList = everest::lib::util::fixed_vector<Name, 128>; // [0 - 128]
struct EIM_ASResAuthorizationMode {};
struct PnC_ASResAuthorizationMode {
GenChallenge gen_challenge;
std::optional<datatypes::SupportedProvidersList> supported_providers;
};
} // namespace datatypes
struct AuthorizationSetupRequest {
Header header;
};
struct AuthorizationSetupResponse {
Header header;
datatypes::ResponseCode response_code;
everest::lib::util::fixed_vector<datatypes::Authorization, 2> authorization_services{datatypes::Authorization::EIM};
bool certificate_installation_service{false};
std::variant<datatypes::EIM_ASResAuthorizationMode, datatypes::PnC_ASResAuthorizationMode> authorization_mode =
datatypes::EIM_ASResAuthorizationMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,378 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
template <typename InType, typename OutType> void convert(const InType&, OutType&);
template <typename T> constexpr auto to_underlying_value(T t) {
return static_cast<std::underlying_type_t<T>>(t);
}
namespace datatypes {
using PercentValue = uint8_t; // [0 - 100]
using NumericId = uint32_t; // [1 - 4294967295]
using Identifier = std::string; // MaxLength: 255
using Name = std::string; // MaxLength: 80
using Description = std::string; // MaxLength: 160
static constexpr auto SESSION_ID_LENGTH = 8;
using SessionId = std::array<uint8_t, SESSION_ID_LENGTH>;
using MeterId = std::string; // MaxLength: 32
using MeterSignature = std::string; // Base64 encoded, MaxLength: 64
static constexpr auto GEN_CHALLENGE_LENGTH = 16;
using GenChallenge = std::array<uint8_t, GEN_CHALLENGE_LENGTH>; // Base64 encoded, MaxLength: 16
using Certificate = std::string; // Base64 encoded, MaxLength: 1600
using SubCertificate = everest::lib::util::fixed_vector<Certificate, 3>; // Max: 3
enum class ResponseCode {
OK = 0,
OK_CertificateExpiresSoon = 1,
OK_NewSessionEstablished = 2,
OK_OldSessionJoined = 3,
OK_PowerToleranceConfirmed = 4,
WARNING_AuthorizationSelectionInvalid = 5,
WARNING_CertificateExpired = 6,
WARNING_CertificateNotYetValid = 7,
WARNING_CertificateRevoked = 8,
WARNING_CertificateValidationError = 9,
WARNING_ChallengeInvalid = 10,
WARNING_EIMAuthorizationFailure = 11,
WARNING_eMSPUnknown = 12,
WARNING_EVPowerProfileViolation = 13,
WARNING_GeneralPnCAuthorizationError = 14,
WARNING_NoCertificateAvailable = 15,
WARNING_NoContractMatchingPCIDFound = 16,
WARNING_PowerToleranceNotConfirmed = 17,
WARNING_ScheduleRenegotiationFailed = 18,
WARNING_StandbyNotAllowed = 19,
WARNING_WPT = 20,
FAILED = 21,
FAILED_AssociationError = 22,
FAILED_ContactorError = 23,
FAILED_EVPowerProfileInvalid = 24,
FAILED_EVPowerProfileViolation = 25,
FAILED_MeteringSignatureNotValid = 26,
FAILED_NoEnergyTransferServiceSelected = 27,
FAILED_NoServiceRenegotiationSupported = 28,
FAILED_PauseNotAllowed = 29,
FAILED_PowerDeliveryNotApplied = 30,
FAILED_PowerToleranceNotConfirmed = 31,
FAILED_ScheduleRenegotiation = 32,
FAILED_ScheduleSelectionInvalid = 33,
FAILED_SequenceError = 34,
FAILED_ServiceIDInvalid = 35,
FAILED_ServiceSelectionInvalid = 36,
FAILED_SignatureError = 37,
FAILED_UnknownSession = 38,
FAILED_WrongChargeParameter = 39
};
enum class Processing {
Finished = 0,
Ongoing = 1,
Ongoing_WaitingForCustomerInteraction = 2,
};
enum class Authorization {
EIM = 0,
PnC = 1,
};
enum class ServiceCategory : uint16_t {
AC = 1,
DC = 2,
WPT = 3,
DC_ACDP = 4,
AC_BPT = 5,
DC_BPT = 6,
DC_ACDP_BPT = 7,
MCS = 8,
MCS_BPT = 9,
AC_DER = 10,
Internet = 65,
ParkingStatus = 66,
};
enum class EvseNotification {
Pause,
ExitStandby,
Terminate,
ScheduleRenegotiation,
ServiceRenegotiation,
MeteringConfirmation,
};
enum class ChargingSession {
Pause,
Terminate,
ServiceRenegotiation,
};
enum class AcConnector {
SinglePhase = 1,
ThreePhase = 3,
};
enum class DcConnector {
Core = 1,
Extended = 2,
Dual2 = 3,
Dual4 = 4,
};
enum class McsConnector {
Mcs = 1,
Chaoji = 2,
UltraChaoji = 3,
rMcs = 4,
xMcs = 5,
Aviation = 6,
Marine = 7,
};
enum class ControlMode {
Scheduled = 1,
Dynamic = 2,
};
enum class MobilityNeedsMode {
ProvidedByEvcc = 1,
ProvidedBySecc = 2,
};
enum class Pricing {
NoPricing = 0,
AbsolutePricing = 1,
PriceLevels = 2,
};
enum class BptChannel {
Unified = 1,
Separated = 2,
};
enum class GeneratorMode {
GridFollowing = 1,
GridForming = 2,
};
enum class GridCodeIslandingDetectionMethod {
Active = 1,
Passive = 2,
};
enum class Protocol {
Ftp,
Http,
Https,
};
enum class Port {
Port20 = 20,
Port21 = 21,
Port80 = 80,
Port443 = 443,
};
enum class IntendedService {
VehicleCheckIn = 1,
VehicleCheckOut = 2,
};
enum class ParkingStatus {
AutoInternal = 1,
AutoExternal = 2,
ManualInternal = 3,
ManualExternal = 4,
};
struct RationalNumber {
int16_t value{0};
int8_t exponent{0};
};
struct EvseStatus {
uint16_t notification_max_delay;
EvseNotification notification;
};
struct AcParameterList {
AcConnector connector;
ControlMode control_mode;
MobilityNeedsMode mobility_needs_mode;
uint32_t evse_nominal_voltage;
Pricing pricing;
};
struct AcBptParameterList : AcParameterList {
BptChannel bpt_channel;
GeneratorMode generator_mode;
GridCodeIslandingDetectionMethod grid_code_detection_method;
};
struct DcParameterList {
DcConnector connector;
ControlMode control_mode;
MobilityNeedsMode mobility_needs_mode;
Pricing pricing;
};
struct DcBptParameterList : DcParameterList {
BptChannel bpt_channel;
GeneratorMode generator_mode;
};
struct McsParameterList {
McsConnector connector;
ControlMode control_mode;
MobilityNeedsMode mobility_needs_mode;
Pricing pricing;
};
struct McsBptParameterList : McsParameterList {
BptChannel bpt_channel;
GeneratorMode generator_mode;
};
struct InternetParameterList {
Protocol protocol;
Port port;
};
struct ParkingParameterList {
IntendedService intended_service;
ParkingStatus parking_status;
};
struct Scheduled_CLReqControlMode {
std::optional<RationalNumber> target_energy_request;
std::optional<RationalNumber> max_energy_request;
std::optional<RationalNumber> min_energy_request;
};
struct Dynamic_CLReqControlMode {
std::optional<uint32_t> departure_time;
RationalNumber target_energy_request;
RationalNumber max_energy_request;
RationalNumber min_energy_request;
};
struct Scheduled_CLResControlMode {};
struct Dynamic_CLResControlMode {
std::optional<uint32_t> departure_time;
std::optional<uint8_t> minimum_soc;
std::optional<uint8_t> target_soc;
std::optional<uint16_t> ack_max_delay;
};
struct PowerScheduleEntry {
uint32_t duration;
RationalNumber power;
std::optional<RationalNumber> power_l2;
std::optional<RationalNumber> power_l3;
};
struct MeterInfo {
MeterId meter_id;
uint64_t charged_energy_reading_wh;
std::optional<uint64_t> bpt_discharged_energy_reading_wh;
std::optional<uint64_t> capacitive_energy_reading_varh;
std::optional<uint64_t> bpt_inductive_energy_reading_varh;
std::optional<MeterSignature> meter_signature;
std::optional<int16_t> meter_status;
std::optional<uint64_t> meter_timestamp;
};
struct DisplayParameters {
std::optional<PercentValue> present_soc;
std::optional<PercentValue> min_soc;
std::optional<PercentValue> target_soc;
std::optional<PercentValue> max_soc;
std::optional<uint32_t> remaining_time_to_min_soc;
std::optional<uint32_t> remaining_time_to_target_soc;
std::optional<uint32_t> remaining_time_to_max_soc;
std::optional<bool> charging_complete;
std::optional<RationalNumber> battery_energy_capacity;
std::optional<bool> inlet_hot;
};
struct DetailedCost {
RationalNumber amount;
RationalNumber cost_per_unit;
};
struct DetailedTax {
NumericId tax_rule_id;
RationalNumber amount;
};
struct Receipt {
uint64_t time_anchor;
std::optional<DetailedCost> energy_costs;
std::optional<DetailedCost> occupancy_costs;
std::optional<DetailedCost> additional_service_costs;
std::optional<DetailedCost> overstay_costs;
everest::lib::util::fixed_vector<DetailedTax, 10> tax_costs; // 0 to 10 elements! // FIXME(sl): optional?
};
struct X509IssuerSerial {
std::string issuer_name;
int64_t serial_number;
};
struct ListOfRootCertificateIDs {
everest::lib::util::fixed_vector<X509IssuerSerial, 20> root_certificate_id;
};
// TODO(sl): Adding content to following structs
struct SignedInfo {};
struct SignatureValue {};
struct KeyInfo {};
struct Object {};
struct Signature {
SignedInfo signed_info;
SignatureValue signature;
std::optional<KeyInfo> key_info;
std::optional<Object> object;
std::optional<std::string> id;
};
struct ContractCertificateChain {
Certificate certificate;
SubCertificate sub_certificates;
};
float from_RationalNumber(const RationalNumber& in);
RationalNumber from_float(float in);
std::string from_Protocol(const Protocol& in);
std::string from_control_mode(const ControlMode& in);
std::string from_mobility_needs_mode(const MobilityNeedsMode& in);
} // namespace datatypes
struct Header {
datatypes::SessionId session_id{};
uint64_t timestamp;
// std::optional<datatypes::Signature> signature;
};
template <typename cb_HeaderType> void convert(const cb_HeaderType& in, Header& out);
template <typename cb_RationalNumberType> void convert(const cb_RationalNumberType& in, datatypes::RationalNumber& out);
template <typename cb_RationalNumberType> void convert(const datatypes::RationalNumber& in, cb_RationalNumberType& out);
} // namespace iso15118::message_20

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "common_types.hpp"
namespace iso15118::message_20 {
struct DC_CableCheckRequest {
Header header;
};
struct DC_CableCheckResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Processing processing{datatypes::Processing::Ongoing};
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,110 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <variant>
#include <vector>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct Scheduled_DC_CLReqControlMode : Scheduled_CLReqControlMode {
RationalNumber target_current;
RationalNumber target_voltage;
std::optional<RationalNumber> max_charge_power;
std::optional<RationalNumber> min_charge_power;
std::optional<RationalNumber> max_charge_current;
std::optional<RationalNumber> max_voltage;
std::optional<RationalNumber> min_voltage;
};
struct BPT_Scheduled_DC_CLReqControlMode : Scheduled_DC_CLReqControlMode {
std::optional<RationalNumber> max_discharge_power;
std::optional<RationalNumber> min_discharge_power;
std::optional<RationalNumber> max_discharge_current;
};
struct Dynamic_DC_CLReqControlMode : Dynamic_CLReqControlMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber max_voltage;
RationalNumber min_voltage;
};
struct BPT_Dynamic_DC_CLReqControlMode : Dynamic_DC_CLReqControlMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
std::optional<RationalNumber> max_v2x_energy_request;
std::optional<RationalNumber> min_v2x_energy_request;
};
struct Scheduled_DC_CLResControlMode : Scheduled_CLResControlMode {
std::optional<RationalNumber> max_charge_power;
std::optional<RationalNumber> min_charge_power;
std::optional<RationalNumber> max_charge_current;
std::optional<RationalNumber> max_voltage;
};
struct BPT_Scheduled_DC_CLResControlMode : Scheduled_DC_CLResControlMode {
std::optional<RationalNumber> max_discharge_power;
std::optional<RationalNumber> min_discharge_power;
std::optional<RationalNumber> max_discharge_current;
std::optional<RationalNumber> min_voltage;
};
struct Dynamic_DC_CLResControlMode : Dynamic_CLResControlMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber max_voltage;
};
struct BPT_Dynamic_DC_CLResControlMode : Dynamic_DC_CLResControlMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
RationalNumber min_voltage;
};
} // namespace datatypes
struct DC_ChargeLoopRequest {
Header header;
// the following 2 are inherited from ChargeLoopReq
std::optional<datatypes::DisplayParameters> display_parameters;
bool meter_info_requested;
datatypes::RationalNumber present_voltage;
std::variant<datatypes::Scheduled_DC_CLReqControlMode, datatypes::BPT_Scheduled_DC_CLReqControlMode,
datatypes::Dynamic_DC_CLReqControlMode, datatypes::BPT_Dynamic_DC_CLReqControlMode>
control_mode;
};
struct DC_ChargeLoopResponse {
Header header;
datatypes::ResponseCode response_code;
// the following 3 are inherited from ChargeLoopRes
std::optional<datatypes::EvseStatus> status;
std::optional<datatypes::MeterInfo> meter_info;
std::optional<datatypes::Receipt> receipt;
datatypes::RationalNumber present_current{0, 0};
datatypes::RationalNumber present_voltage{0, 0};
bool power_limit_achieved{false};
bool current_limit_achieved{false};
bool voltage_limit_achieved{false};
std::variant<datatypes::Scheduled_DC_CLResControlMode, datatypes::BPT_Scheduled_DC_CLResControlMode,
datatypes::Dynamic_DC_CLResControlMode, datatypes::BPT_Dynamic_DC_CLResControlMode>
control_mode = datatypes::Scheduled_DC_CLResControlMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <variant>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct DC_CPDReqEnergyTransferMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber min_charge_current;
RationalNumber max_voltage;
RationalNumber min_voltage;
std::optional<uint8_t> target_soc;
};
struct BPT_DC_CPDReqEnergyTransferMode : DC_CPDReqEnergyTransferMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
RationalNumber min_discharge_current;
};
struct DC_CPDResEnergyTransferMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber min_charge_current;
RationalNumber max_voltage;
RationalNumber min_voltage;
std::optional<RationalNumber> power_ramp_limit;
};
struct BPT_DC_CPDResEnergyTransferMode : DC_CPDResEnergyTransferMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
RationalNumber min_discharge_current;
};
} // namespace datatypes
struct DC_ChargeParameterDiscoveryRequest {
Header header;
std::variant<datatypes::DC_CPDReqEnergyTransferMode, datatypes::BPT_DC_CPDReqEnergyTransferMode> transfer_mode;
};
struct DC_ChargeParameterDiscoveryResponse {
Header header;
datatypes::ResponseCode response_code;
std::variant<datatypes::DC_CPDResEnergyTransferMode, datatypes::BPT_DC_CPDResEnergyTransferMode> transfer_mode =
datatypes::DC_CPDResEnergyTransferMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "common_types.hpp"
namespace iso15118::message_20 {
struct DC_PreChargeRequest {
Header header;
datatypes::Processing processing;
datatypes::RationalNumber present_voltage;
datatypes::RationalNumber target_voltage;
};
struct DC_PreChargeResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::RationalNumber present_voltage;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "common_types.hpp"
namespace iso15118::message_20 {
struct DC_WeldingDetectionRequest {
Header header;
datatypes::Processing processing;
};
struct DC_WeldingDetectionResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::RationalNumber present_voltage;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
// FIXME (aw): how to streamline this with type.hpp?
#include <iso15118/io/sdp.hpp>
namespace iso15118::message_20 {
template <typename T> struct PayloadTypeTrait;
//
// definitions of type traits
//
#ifdef CREATE_TYPE_TRAIT
#define CREATE_TYPE_TRAIT_PUSHED CREATE_TYPE_TRAIT
#endif
#define CREATE_TYPE_TRAIT(struct_name, payload_type) \
struct struct_name; \
template <> struct PayloadTypeTrait<struct_name> { \
static const io::v2gtp::PayloadType type = io::v2gtp::PayloadType::payload_type; \
}
CREATE_TYPE_TRAIT(SupportedAppProtocolResponse, SAP);
CREATE_TYPE_TRAIT(SessionSetupResponse, Part20Main);
CREATE_TYPE_TRAIT(AuthorizationSetupResponse, Part20Main);
CREATE_TYPE_TRAIT(AuthorizationResponse, Part20Main);
CREATE_TYPE_TRAIT(ServiceDiscoveryResponse, Part20Main);
CREATE_TYPE_TRAIT(ServiceDetailResponse, Part20Main);
CREATE_TYPE_TRAIT(ServiceSelectionResponse, Part20Main);
CREATE_TYPE_TRAIT(DC_ChargeParameterDiscoveryResponse, Part20DC);
CREATE_TYPE_TRAIT(ScheduleExchangeResponse, Part20Main);
CREATE_TYPE_TRAIT(DC_CableCheckResponse, Part20DC);
CREATE_TYPE_TRAIT(DC_PreChargeResponse, Part20DC);
CREATE_TYPE_TRAIT(PowerDeliveryResponse, Part20Main);
CREATE_TYPE_TRAIT(DC_ChargeLoopResponse, Part20DC);
CREATE_TYPE_TRAIT(DC_WeldingDetectionResponse, Part20DC);
CREATE_TYPE_TRAIT(SessionStopResponse, Part20Main);
CREATE_TYPE_TRAIT(AC_ChargeParameterDiscoveryResponse, Part20AC);
CREATE_TYPE_TRAIT(AC_ChargeLoopResponse, Part20AC);
#ifdef CREATE_TYPE_TRAIT_PUSHED
#define CREATE_TYPE_TRAIT CREATE_TYPE_TRAIT_PUSHED
#else
#undef CREATE_TYPE_TRAIT
#endif
} // namespace iso15118::message_20

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <variant>
#include <vector>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
enum class Progress {
Start,
Stop,
Standby,
ScheduleRenegotiation,
};
struct Dynamic_EVPPTControlMode {
// intentionally left blank
};
enum class PowerToleranceAcceptance : uint8_t {
NotConfirmed,
Confirmed,
};
struct Scheduled_EVPPTControlMode {
NumericId selected_schedule;
std::optional<PowerToleranceAcceptance> power_tolerance_acceptance;
};
struct PowerProfile {
uint64_t time_anchor;
std::variant<Dynamic_EVPPTControlMode, Scheduled_EVPPTControlMode> control_mode;
everest::lib::util::fixed_vector<PowerScheduleEntry, 2048> entries; // maximum 2048
};
enum class ChannelSelection : uint8_t {
Charge,
Discharge,
};
}; // namespace datatypes
struct PowerDeliveryRequest {
Header header;
datatypes::Processing processing;
datatypes::Progress charge_progress;
std::optional<datatypes::PowerProfile> power_profile;
std::optional<datatypes::ChannelSelection> channel_selection;
};
struct PowerDeliveryResponse {
Header header;
datatypes::ResponseCode response_code;
std::optional<datatypes::EvseStatus> status;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,206 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <optional>
#include <string>
#include <variant>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
static constexpr auto TAX_RULE_LENGTH = 10;
static constexpr auto PRICE_RULE_STACK_LENGTH = 1024;
static constexpr auto PRICE_RULE_LENGTH = 8;
static constexpr auto OVERSTAY_RULE_LENGTH = 5;
static constexpr auto ADDITIONAL_SERVICE_LENGTH = 5;
static constexpr auto PRICE_LEVEL_SCHEDULE_LENGTH = 1024;
static constexpr auto SCHEDULED_POWER_DURATION_S = 86400;
using MaxSupportingPointsScheduleTuple = uint16_t; // needs to be [12 - 1024]
using Curreny = std::string; // MaxLength: 3
using Language = std::string; // MaxLength: 3
struct TaxRule {
NumericId tax_rule_id;
std::optional<Name> tax_rule_name;
RationalNumber tax_rate;
std::optional<bool> tax_included_in_price;
bool applies_to_energy_fee;
bool applies_to_parking_fee;
bool applies_to_overstay_fee;
bool applies_to_minimum_maximum_cost;
};
struct PriceRule {
RationalNumber energy_fee;
std::optional<RationalNumber> parking_fee;
std::optional<uint32_t> parking_fee_period;
std::optional<uint16_t> carbon_dioxide_emission;
std::optional<uint8_t> renewable_generation_percentage;
RationalNumber power_range_start;
};
struct PriceRuleStack {
uint32_t duration;
std::array<PriceRule, PRICE_RULE_LENGTH> price_rule;
};
struct AdditionalService {
Name service_name;
RationalNumber service_fee;
};
using TaxRuleList = std::array<TaxRule, TAX_RULE_LENGTH>;
using PriceRuleStackList = std::array<PriceRuleStack, PRICE_RULE_STACK_LENGTH>;
using AdditionalServiceList = std::array<AdditionalService, ADDITIONAL_SERVICE_LENGTH>;
struct Dynamic_SEReqControlMode {
uint32_t departure_time;
std::optional<PercentValue> minimum_soc;
std::optional<PercentValue> target_soc;
RationalNumber target_energy;
RationalNumber max_energy;
RationalNumber min_energy;
std::optional<RationalNumber> max_v2x_energy;
std::optional<RationalNumber> min_v2x_energy;
};
struct EVPowerScheduleEntry {
uint32_t duration;
RationalNumber power;
};
struct EVPowerSchedule {
uint64_t time_anchor;
everest::lib::util::fixed_vector<EVPowerScheduleEntry, 1024> entries; // max 1024
};
struct EVPriceRule {
RationalNumber energy_fee;
RationalNumber power_range_start;
};
struct EVPriceRuleStack {
uint32_t duration;
everest::lib::util::fixed_vector<EVPriceRule, 8> price_rules; // max 8
};
struct EVAbsolutePriceSchedule {
uint64_t time_anchor;
Curreny currency;
Identifier price_algorithm;
everest::lib::util::fixed_vector<EVPriceRuleStack, 1024> price_rule_stacks; // max 1024
};
struct EVEnergyOffer {
EVPowerSchedule power_schedule;
EVAbsolutePriceSchedule absolute_price_schedule;
};
struct Scheduled_SEReqControlMode {
std::optional<uint32_t> departure_time;
std::optional<RationalNumber> target_energy;
std::optional<RationalNumber> max_energy;
std::optional<RationalNumber> min_energy;
std::optional<EVEnergyOffer> energy_offer;
};
struct OverstayRule {
std::optional<Description> overstay_rule_description;
uint32_t start_time;
RationalNumber overstay_fee;
uint32_t overstay_fee_period;
};
struct OverstayRulesList {
std::optional<uint32_t> overstay_time_threshold;
std::optional<RationalNumber> overstay_power_threshold;
everest::lib::util::fixed_vector<OverstayRule, 5> overstay_rule; // 5
};
struct AbsolutePriceSchedule {
std::optional<std::string> id;
uint64_t time_anchor;
NumericId price_schedule_id;
std::optional<Description> price_schedule_description;
Curreny currency;
Language language;
Identifier price_algorithm;
std::optional<RationalNumber> minimum_cost;
std::optional<RationalNumber> maximum_cost;
std::optional<TaxRuleList> tax_rules;
PriceRuleStackList price_rule_stacks;
std::optional<OverstayRulesList> overstay_rules;
std::optional<AdditionalServiceList> additional_selected_services;
};
struct PriceLevelScheduleEntry {
uint32_t duration;
uint8_t price_level;
};
struct PriceLevelSchedule {
std::optional<std::string> id;
uint64_t time_anchor;
NumericId price_schedule_id;
std::optional<Description> price_schedule_description;
uint8_t number_of_price_levels;
everest::lib::util::fixed_vector<PriceLevelScheduleEntry, 1024> price_level_schedule_entries; // 1024
};
struct Dynamic_SEResControlMode {
std::optional<uint32_t> departure_time;
std::optional<PercentValue> minimum_soc;
std::optional<PercentValue> target_soc;
std::variant<std::monostate, AbsolutePriceSchedule, PriceLevelSchedule> price_schedule;
};
struct PowerSchedule {
uint64_t time_anchor;
std::optional<RationalNumber> available_energy;
std::optional<RationalNumber> power_tolerance;
everest::lib::util::fixed_vector<PowerScheduleEntry, 1024> entries; // 1024
};
struct ChargingSchedule {
PowerSchedule power_schedule;
std::variant<std::monostate, AbsolutePriceSchedule, PriceLevelSchedule> price_schedule;
};
struct ScheduleTuple {
NumericId schedule_tuple_id; // 1 - 255
ChargingSchedule charging_schedule;
std::optional<ChargingSchedule> discharging_schedule;
};
struct Scheduled_SEResControlMode {
everest::lib::util::fixed_vector<ScheduleTuple, 3> schedule_tuple; // 3
};
}; // namespace datatypes
struct ScheduleExchangeRequest {
Header header;
datatypes::MaxSupportingPointsScheduleTuple max_supporting_points;
std::variant<datatypes::Dynamic_SEReqControlMode, datatypes::Scheduled_SEReqControlMode> control_mode;
};
struct ScheduleExchangeResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Processing processing{datatypes::Processing::Finished};
std::optional<bool> go_to_pause;
std::variant<datatypes::Dynamic_SEResControlMode, datatypes::Scheduled_SEResControlMode> control_mode =
datatypes::Dynamic_SEResControlMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <variant>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
struct Parameter {
Name name;
std::variant<bool, int8_t, int16_t, int32_t, Name, RationalNumber> value;
};
struct ParameterSet {
uint16_t id;
everest::lib::util::fixed_vector<Parameter, 32> parameter;
ParameterSet() = default;
ParameterSet(uint16_t _id);
ParameterSet(uint16_t _id, const AcParameterList& list);
ParameterSet(uint16_t _id, const AcBptParameterList& list);
ParameterSet(uint16_t _id, const DcParameterList& list);
ParameterSet(uint16_t _id, const DcBptParameterList& list);
ParameterSet(uint16_t _id, const McsParameterList& list);
ParameterSet(uint16_t _id, const McsBptParameterList& list);
ParameterSet(uint16_t _id, const InternetParameterList& list);
ParameterSet(uint16_t _id, const ParkingParameterList& list);
};
using ServiceParameterList = everest::lib::util::fixed_vector<ParameterSet, 32>; // Max: 32
} // namespace datatypes
struct ServiceDetailRequest {
Header header;
uint16_t service;
};
struct ServiceDetailResponse {
Header header;
datatypes::ResponseCode response_code;
uint16_t service{to_underlying_value(datatypes::ServiceCategory::DC)};
datatypes::ServiceParameterList service_parameter_list{datatypes::ParameterSet(0)};
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <vector>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
using ServiceIdList = everest::lib::util::fixed_vector<std::uint16_t, 16>; // 16
struct Service {
ServiceCategory service_id;
bool free_service;
};
using ServiceList = everest::lib::util::fixed_vector<Service, 8>; // max: 8
struct VasService {
uint16_t service_id;
bool free_service;
};
using VasServiceList = everest::lib::util::fixed_vector<VasService, 8>; // max: 8
} // namespace datatypes
struct ServiceDiscoveryRequest {
Header header;
std::optional<datatypes::ServiceIdList> supported_service_ids;
};
struct ServiceDiscoveryResponse {
Header header;
datatypes::ResponseCode response_code;
bool service_renegotiation_supported = false;
datatypes::ServiceList energy_transfer_service_list = {{
datatypes::ServiceCategory::AC, // service_id
false // free_service
}};
std::optional<datatypes::VasServiceList> vas_list;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
struct SelectedService {
ServiceCategory service_id;
uint16_t parameter_set_id;
};
struct VasSelectedService {
uint16_t service_id;
uint16_t parameter_set_id;
};
using VasSelectedServiceList = everest::lib::util::fixed_vector<VasSelectedService, 16>; // Max: 16
} // namespace datatypes
struct ServiceSelectionRequest {
Header header;
datatypes::SelectedService selected_energy_transfer_service;
std::optional<datatypes::VasSelectedServiceList> selected_vas_list;
};
struct ServiceSelectionResponse {
Header header;
datatypes::ResponseCode response_code;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
#include "common_types.hpp"
namespace iso15118::message_20 {
struct SessionSetupRequest {
Header header;
datatypes::Identifier evccid;
};
struct SessionSetupResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Identifier evseid;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <string>
#include "common_types.hpp"
namespace iso15118::message_20 {
struct SessionStopRequest {
Header header;
datatypes::ChargingSession charging_session;
std::optional<datatypes::Name> ev_termination_code;
std::optional<datatypes::Description> ev_termination_explanation;
};
struct SessionStopResponse {
Header header;
datatypes::ResponseCode response_code;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <optional>
#include <string>
#include <vector>
#include <iso15118/io/stream_view.hpp>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
struct SupportedAppProtocol {
std::string protocol_namespace;
uint32_t version_number_major;
uint32_t version_number_minor;
uint8_t schema_id;
uint8_t priority;
bool operator==(const iso15118::message_20::SupportedAppProtocol& other) const {
return this->protocol_namespace == other.protocol_namespace and this->priority == other.priority and
this->schema_id == other.schema_id and this->version_number_major == other.version_number_major and
this->version_number_minor == other.version_number_minor;
}
};
struct SupportedAppProtocolRequest {
everest::lib::util::fixed_vector<SupportedAppProtocol, 20> app_protocol;
};
struct SupportedAppProtocolResponse {
enum class ResponseCode {
OK_SuccessfulNegotiation = 0,
OK_SuccessfulNegotiationWithMinorDeviation = 1,
Failed_NoNegotiation = 2,
};
ResponseCode response_code;
std::optional<uint8_t> schema_id;
};
size_t encode_supported_app_protocol_response(const io::StreamOutputView&, const SupportedAppProtocolResponse&);
} // namespace iso15118::message_20

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/io/stream_view.hpp>
namespace iso15118::message_20 {
enum class Type {
None,
SupportedAppProtocolReq,
SupportedAppProtocolRes,
SessionSetupReq,
SessionSetupRes,
AuthorizationSetupReq,
AuthorizationSetupRes,
AuthorizationReq,
AuthorizationRes,
ServiceDiscoveryReq,
ServiceDiscoveryRes,
ServiceDetailReq,
ServiceDetailRes,
ServiceSelectionReq,
ServiceSelectionRes,
DC_ChargeParameterDiscoveryReq,
DC_ChargeParameterDiscoveryRes,
ScheduleExchangeReq,
ScheduleExchangeRes,
DC_CableCheckReq,
DC_CableCheckRes,
DC_PreChargeReq,
DC_PreChargeRes,
PowerDeliveryReq,
PowerDeliveryRes,
DC_ChargeLoopReq,
DC_ChargeLoopRes,
DC_WeldingDetectionReq,
DC_WeldingDetectionRes,
SessionStopReq,
SessionStopRes,
AC_ChargeParameterDiscoveryReq,
AC_ChargeParameterDiscoveryRes,
AC_ChargeLoopReq,
AC_ChargeLoopRes,
};
template <typename T> struct TypeTrait {
static const Type type = Type::None;
};
template <typename InType, typename OutType> void convert(const InType&, OutType&);
template <typename MessageType> size_t serialize(const MessageType&, const io::StreamOutputView&);
//
// definitions of type traits
//
#ifdef CREATE_TYPE_TRAIT
#define CREATE_TYPE_TRAIT_PUSHED CREATE_TYPE_TRAIT
#endif
#define CREATE_TYPE_TRAIT(struct_name, enum_name) \
struct struct_name; \
template <> struct TypeTrait<struct_name> { \
static const Type type = Type::enum_name; \
}
CREATE_TYPE_TRAIT(SupportedAppProtocolRequest, SupportedAppProtocolReq);
CREATE_TYPE_TRAIT(SupportedAppProtocolResponse, SupportedAppProtocolRes);
CREATE_TYPE_TRAIT(SessionSetupRequest, SessionSetupReq);
CREATE_TYPE_TRAIT(SessionSetupResponse, SessionSetupRes);
CREATE_TYPE_TRAIT(AuthorizationSetupRequest, AuthorizationSetupReq);
CREATE_TYPE_TRAIT(AuthorizationSetupResponse, AuthorizationSetupRes);
CREATE_TYPE_TRAIT(AuthorizationRequest, AuthorizationReq);
CREATE_TYPE_TRAIT(AuthorizationResponse, AuthorizationRes);
CREATE_TYPE_TRAIT(ServiceDiscoveryRequest, ServiceDiscoveryReq);
CREATE_TYPE_TRAIT(ServiceDiscoveryResponse, ServiceDiscoveryRes);
CREATE_TYPE_TRAIT(ServiceDetailRequest, ServiceDetailReq);
CREATE_TYPE_TRAIT(ServiceDetailResponse, ServiceDetailRes);
CREATE_TYPE_TRAIT(ServiceSelectionRequest, ServiceSelectionReq);
CREATE_TYPE_TRAIT(ServiceSelectionResponse, ServiceSelectionRes);
CREATE_TYPE_TRAIT(DC_ChargeParameterDiscoveryRequest, DC_ChargeParameterDiscoveryReq);
CREATE_TYPE_TRAIT(DC_ChargeParameterDiscoveryResponse, DC_ChargeParameterDiscoveryRes);
CREATE_TYPE_TRAIT(ScheduleExchangeRequest, ScheduleExchangeReq);
CREATE_TYPE_TRAIT(ScheduleExchangeResponse, ScheduleExchangeRes);
CREATE_TYPE_TRAIT(DC_CableCheckRequest, DC_CableCheckReq);
CREATE_TYPE_TRAIT(DC_CableCheckResponse, DC_CableCheckRes);
CREATE_TYPE_TRAIT(DC_PreChargeRequest, DC_PreChargeReq);
CREATE_TYPE_TRAIT(DC_PreChargeResponse, DC_PreChargeRes);
CREATE_TYPE_TRAIT(PowerDeliveryRequest, PowerDeliveryReq);
CREATE_TYPE_TRAIT(PowerDeliveryResponse, PowerDeliveryRes);
CREATE_TYPE_TRAIT(DC_ChargeLoopRequest, DC_ChargeLoopReq);
CREATE_TYPE_TRAIT(DC_ChargeLoopResponse, DC_ChargeLoopRes);
CREATE_TYPE_TRAIT(DC_WeldingDetectionRequest, DC_WeldingDetectionReq);
CREATE_TYPE_TRAIT(DC_WeldingDetectionResponse, DC_WeldingDetectionRes);
CREATE_TYPE_TRAIT(SessionStopRequest, SessionStopReq);
CREATE_TYPE_TRAIT(SessionStopResponse, SessionStopRes);
CREATE_TYPE_TRAIT(AC_ChargeParameterDiscoveryRequest, AC_ChargeParameterDiscoveryReq);
CREATE_TYPE_TRAIT(AC_ChargeParameterDiscoveryResponse, AC_ChargeParameterDiscoveryRes);
CREATE_TYPE_TRAIT(AC_ChargeLoopRequest, AC_ChargeLoopReq);
CREATE_TYPE_TRAIT(AC_ChargeLoopResponse, AC_ChargeLoopRes);
#ifdef CREATE_TYPE_TRAIT_PUSHED
#define CREATE_TYPE_TRAIT CREATE_TYPE_TRAIT_PUSHED
#else
#undef CREATE_TYPE_TRAIT
#endif
} // namespace iso15118::message_20

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <string>
// FIXME (aw): we only need the payload types from sdp.hpp, this could be shared in a separate header file
#include <iso15118/io/sdp.hpp>
#include <iso15118/io/stream_view.hpp>
#include "type.hpp"
namespace iso15118::message_20 {
class Variant {
public:
using CustomDeleter = void (*)(void*);
Variant(io::v2gtp::PayloadType, const io::StreamInputView&);
template <typename MessageType> Variant(const MessageType& in) {
static_assert(TypeTrait<MessageType>::type != Type::None, "Unhandled type!");
data = new MessageType;
*static_cast<MessageType*>(data) = in;
custom_deleter = [](void* ptr) { delete static_cast<MessageType*>(ptr); };
type = message_20::TypeTrait<MessageType>::type;
}
~Variant();
Type get_type() const;
const std::string& get_error() const;
template <typename T> const T& get() const {
static_assert(TypeTrait<T>::type != Type::None, "Unhandled type!");
if (TypeTrait<T>::type != type) {
throw std::runtime_error("Illegal message type access");
}
return *static_cast<T*>(data);
}
template <typename T> T const* get_if() const {
static_assert(TypeTrait<T>::type != Type::None, "Unhandled type!");
if (TypeTrait<T>::type != type) {
return nullptr;
}
return static_cast<T*>(data);
}
private:
CustomDeleter custom_deleter{nullptr};
void* data{nullptr};
Type type{Type::None};
std::string error;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,122 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cmath>
#include <functional>
#include <optional>
#include <string>
#include <variant>
#include <iso15118/d20/ev_information.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/ac_charge_loop.hpp>
#include <iso15118/message/ac_charge_parameter_discovery.hpp>
#include <iso15118/message/dc_charge_loop.hpp>
#include <iso15118/message/dc_charge_parameter_discovery.hpp>
#include <iso15118/message/schedule_exchange.hpp>
#include <iso15118/message/service_detail.hpp>
#include <iso15118/message/service_selection.hpp>
#include <iso15118/message/type.hpp>
namespace iso15118::session {
namespace dt = message_20::datatypes;
namespace feedback {
enum class Signal {
REQUIRE_AUTH_EIM,
START_CABLE_CHECK,
SETUP_FINISHED,
PRE_CHARGE_STARTED,
CHARGE_LOOP_STARTED,
CHARGE_LOOP_FINISHED,
DC_OPEN_CONTACTOR,
AC_CLOSE_CONTACTOR,
AC_OPEN_CONTACTOR,
DLINK_TERMINATE,
DLINK_ERROR,
DLINK_PAUSE,
};
struct DcMaximumLimits {
float voltage{NAN};
float current{NAN};
float power{NAN};
};
using PresentVoltage = dt::RationalNumber;
using MeterInfoRequested = bool;
using DcReqControlMode = std::variant<dt::Scheduled_DC_CLReqControlMode, dt::BPT_Scheduled_DC_CLReqControlMode,
dt::Dynamic_DC_CLReqControlMode, dt::BPT_Dynamic_DC_CLReqControlMode>;
using AcReqControlMode = std::variant<dt::Scheduled_AC_CLReqControlMode, dt::BPT_Scheduled_AC_CLReqControlMode,
dt::Dynamic_AC_CLReqControlMode, dt::BPT_Dynamic_AC_CLReqControlMode>;
using DcChargeLoopReq = std::variant<DcReqControlMode, dt::DisplayParameters, PresentVoltage, MeterInfoRequested>;
using EvseTransferLimits = std::variant<d20::DcTransferLimits, d20::AcTransferLimits>;
using EvTransferLimits = std::variant<dt::DC_CPDReqEnergyTransferMode, dt::BPT_DC_CPDReqEnergyTransferMode,
dt::AC_CPDReqEnergyTransferMode, dt::BPT_AC_CPDReqEnergyTransferMode>;
using EvSEControlMode = std::variant<dt::Dynamic_SEReqControlMode, dt::Scheduled_SEReqControlMode>;
using AcChargeLoopReq = std::variant<AcReqControlMode, dt::DisplayParameters, MeterInfoRequested>;
using AcLimits = std::variant<dt::AC_CPDReqEnergyTransferMode, dt::BPT_AC_CPDReqEnergyTransferMode>;
struct Callbacks {
std::function<void(Signal)> signal;
std::function<void(float)> dc_pre_charge_target_voltage;
std::function<void(const DcChargeLoopReq&)> dc_charge_loop_req;
std::function<void(const DcMaximumLimits&)> dc_max_limits;
std::function<void(const AcChargeLoopReq&)> ac_charge_loop_req;
std::function<void(const message_20::Type&)> v2g_message;
std::function<void(const std::string&)> evccid;
std::function<void(const std::string&)> selected_protocol;
std::function<void(const dt::ServiceCategory&, const std::optional<dt::AcConnector>&, const dt::ControlMode&,
const dt::MobilityNeedsMode&, const EvseTransferLimits&, const EvTransferLimits&,
const EvSEControlMode&, const std::vector<message_20::datatypes::ServiceCategory>&)>
notify_ev_charging_needs;
std::function<void(const d20::SelectedServiceParameters&)> selected_service_parameters;
std::function<void(const d20::EVInformation&)> ev_information;
std::function<std::optional<dt::ServiceParameterList>(uint16_t)> get_vas_parameters;
std::function<void(const dt::VasSelectedServiceList&)> selected_vas_services;
std::function<void(const AcLimits&)> ac_limits;
std::function<void(const std::string&, const std::string&)> ev_termination;
};
} // namespace feedback
class Feedback {
public:
Feedback(feedback::Callbacks);
void signal(feedback::Signal) const;
void dc_pre_charge_target_voltage(float) const;
void dc_charge_loop_req(const feedback::DcChargeLoopReq&) const;
void dc_max_limits(const feedback::DcMaximumLimits&) const;
void ac_charge_loop_req(const feedback::AcChargeLoopReq&) const;
void v2g_message(const message_20::Type&) const;
void evcc_id(const std::string&) const;
void selected_protocol(const std::string&) const;
void notify_ev_charging_needs(const dt::ServiceCategory&, const std::optional<dt::AcConnector>&,
const dt::ControlMode&, const dt::MobilityNeedsMode&,
const feedback::EvseTransferLimits&, const feedback::EvTransferLimits&,
const feedback::EvSEControlMode&,
const std::vector<message_20::datatypes::ServiceCategory>&) const;
void selected_service_parameters(const d20::SelectedServiceParameters&) const;
void ev_information(const d20::EVInformation&) const;
std::optional<dt::ServiceParameterList> get_vas_parameters(uint16_t) const;
void selected_vas_services(const dt::VasSelectedServiceList&) const;
void ac_limits(const feedback::AcLimits&) const;
void ev_termination(const std::string&, const std::string&) const;
private:
feedback::Callbacks callbacks;
};
} // namespace iso15118::session

View File

@@ -0,0 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <memory>
#include <optional>
#include <iso15118/config.hpp>
#include <everest/util/fsm/fsm.hpp>
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/context.hpp>
#include <iso15118/d20/control_event_queue.hpp>
#include <iso15118/d20/states.hpp>
#include <iso15118/io/connection_abstract.hpp>
#include <iso15118/io/poll_manager.hpp>
#include <iso15118/io/sdp_packet.hpp>
#include <iso15118/io/time.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/session/logger.hpp>
#include <iso15118/d20/timeout.hpp>
namespace iso15118 {
struct SessionState {
bool connected{false};
bool new_data{false};
bool fsm_needs_call{false};
};
class Session {
public:
Session(std::unique_ptr<io::IConnection>, d20::SessionConfig, const session::feedback::Callbacks&,
std::optional<d20::PauseContext>&);
~Session();
TimePoint const& poll();
void push_control_event(const d20::ControlEvent&);
bool is_finished() const {
return (ctx.session_stopped or ctx.session_paused) and not message_exchange.has_response();
}
void close();
private:
std::unique_ptr<io::IConnection> connection;
session::SessionLogger log;
SessionState state;
// input buffer
io::SdpPacket packet;
// output buffer
uint8_t response_buffer[1028];
d20::MessageExchange message_exchange{{response_buffer + io::SdpPacket::V2GTP_HEADER_SIZE,
sizeof(response_buffer) - io::SdpPacket::V2GTP_HEADER_SIZE}};
// control event buffer
d20::ControlEventQueue control_event_queue;
std::optional<d20::ControlEvent> active_control_event{std::nullopt};
d20::Context ctx;
fsm::v2::FSM<d20::StateBase> fsm;
TimePoint next_session_event;
d20::Timeouts timeouts;
void handle_connection_event(io::ConnectionEvent event);
void send_response();
std::optional<TimePoint> last_response_tx_time; // timestamp of the last response message sent
std::optional<TimePoint> response_send_after; // time point when the next response message can be sent
};
} // namespace iso15118

View File

@@ -0,0 +1,57 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <functional>
#include <string>
#include <variant>
namespace iso15118::session {
namespace logging {
using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
struct SimpleEvent {
TimePoint time_point;
std::string info;
};
enum class ExiMessageDirection {
FROM_EV,
TO_EV,
};
struct ExiMessageEvent {
TimePoint time_point;
uint16_t payload_type;
uint8_t const* data;
size_t len;
ExiMessageDirection direction;
};
using Event = std::variant<SimpleEvent, ExiMessageEvent>;
using Callback = std::function<void(std::size_t id, const Event&)>;
void set_session_log_callback(const Callback&);
} // namespace logging
class SessionLogger {
public:
SessionLogger(void*);
void enter_state(const std::string& new_state);
void event(const std::string& info) const;
void exi(uint16_t payload_type, uint8_t const* data, size_t len, logging::ExiMessageDirection direction) const;
void operator()(const std::string&) const;
void operator()(const char* format, ...) const;
private:
std::uintptr_t id;
std::string last_state_name;
};
} // namespace iso15118::session

View File

@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "config.hpp"
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/control_event.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/io/poll_manager.hpp>
#include <iso15118/io/sdp_server.hpp>
#include <iso15118/io/time.hpp>
#include <iso15118/message/common_types.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/session/iso.hpp>
namespace iso15118 {
struct TbdConfig {
config::SSLConfig ssl{config::CertificateBackend::EVEREST_LAYOUT, {}, {}, {}, {}, {}, {}};
std::string interface_name;
config::TlsNegotiationStrategy tls_negotiation_strategy{config::TlsNegotiationStrategy::ACCEPT_CLIENT_OFFER};
bool enable_sdp_server{true};
};
class TbdController {
public:
TbdController(TbdConfig, session::feedback::Callbacks, d20::EvseSetupConfig);
void loop();
void send_control_event(const d20::ControlEvent&);
void update_authorization_services(const std::vector<message_20::datatypes::Authorization>& services,
bool cert_install_service);
void update_dc_limits(const d20::DcTransferLimits&);
void update_powersupply_limits(const d20::DcTransferLimits&);
void update_energy_modes(const std::vector<message_20::datatypes::ServiceCategory>&);
void update_ac_limits(const d20::AcTransferLimits&);
void update_supported_vas_services(const std::vector<uint16_t>& vas_services);
void set_dlink_ready(bool ready);
private:
io::PollManager poll_manager;
std::unique_ptr<io::SdpServer> sdp_server;
std::unique_ptr<Session> session;
// callbacks for sdp server
void handle_sdp_server_input();
const TbdConfig config;
const session::feedback::Callbacks callbacks;
d20::EvseSetupConfig evse_setup;
std::string interface_name;
std::optional<d20::PauseContext> pause_ctx{std::nullopt};
static constexpr uint32_t V2G_COMMUNICATION_SETUP_TIMEOUT_MS{18000};
std::optional<Timeout> communication_setup_timeout;
};
} // namespace iso15118

Some files were not shown because too many files have changed in this diff Show More