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,38 @@
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/source/conf.py.in
${CMAKE_CURRENT_SOURCE_DIR}/source/conf.py
@ONLY
)
find_package(
trailbook
0.1.0
REQUIRED
PATHS "${CMAKE_SOURCE_DIR}/cmake"
)
find_package(
trailbook-ext-everest
0.1.0
REQUIRED
PATHS "${CMAKE_SOURCE_DIR}/cmake"
)
set(EVEREST_DOCS_REPO_URL "git@github.com:everest/everest.github.io" CACHE STRING "The git URL of the repository where the deployed docs are stored")
add_trailbook(
NAME "everest"
STEM_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source
DEPLOYED_DOCS_REPO_URL ${EVEREST_DOCS_REPO_URL}
DEPLOYED_DOCS_REPO_BRANCH "main"
)
get_target_property(
TRAILBOOK_INSTANCE_SOURCE_DIRECTORY
trailbook_everest
TRAILBOOK_INSTANCE_SOURCE_DIRECTORY
)
get_filename_component(EVEREST_WORKSPACE_DIRECTORY ${CMAKE_SOURCE_DIR} DIRECTORY)
trailbook_ev_create_snapshot(
EVEREST_WORKSPACE_DIRECTORY "${EVEREST_WORKSPACE_DIRECTORY}"
TRAILBOOK_NAME "everest"
OUTPUT_FILE "${TRAILBOOK_INSTANCE_SOURCE_DIRECTORY}/reference/assets/snapshot.yaml"
)

View File

@@ -0,0 +1,87 @@
# trailbook: EVerest
The EVerest documentation uses the CMake package `trailbook`
to build and manage its documentation.
Additional it uses the extension package `trailbook-ext-everest`
## Configure CMake
There are three CMake variables you need to know about.
### `EVEREST_BUILD_DOCS`
The CMake variable `EVEREST_BUILD_DOCS` enables or disables the building of the
EVerest documentation. It is disabled by default. To enable it, set the variable to `ON` when configuring CMake:
```bash
cmake -D EVEREST_BUILD_DOCS=ON <path_to_source>
```
### `TRAILBOOK_everest_DOWNLOAD_ALL_VERSIONS`
The CMake variable `TRAILBOOK_everest_DOWNLOAD_ALL_VERSIONS` controls whether
all available versions of the EVerest documentation are downloaded during the build process.
By default, this variable is set to `OFF`, meaning an empty multiversion skeleton is created
during the build.
To enable the downloading of all available versions, set the variable to `ON` when configuring CMake:
```bash
cmake -D EVEREST_BUILD_DOCS=ON -D TRAILBOOK_everest_DOWNLOAD_ALL_VERSIONS=ON <path_to_source>
```
With this the `everest.github.io` repository cloned and the new built documentation is embedded into
the exisiting multiversion structure.
### `TRAILBOOK_everest_IS_RELEASE`
If you don't want to deploy the documentation you probably don't need to care about this variable.
The CMake variable `TRAILBOOK_everest_IS_RELEASE` indicates whether the current build is a release build. It defaults to `ON` which means that files in the root of the multiversion structure
as `index.html` and `404.html` become updated. Additionally the `latest` symlink is updated to point to the current version.
In case of the need to build the documentation as nightly for example the variable can
be set to `OFF` when configuring CMake:
```bash
cmake -D EVEREST_BUILD_DOCS=ON -D TRAILBOOK_everest_DOWNLOAD_ALL_VERSIONS=ON -D TRAILBOOK_everest_IS_RELEASE=OFF <path_to_source>
```
**Note:** Building the documentation required `EDM` to be installed on the host system.
## Build
There are three targets available to work with the EVerest documentation:
```bash
cmake --build <build_directory> --target trailbook_everest
```
Builds the EVerest documentation.
```bash
cmake --build <build_directory> --target trailbook_everest_preview
```
Builds the EVerest documentation and serves it with a local web server
for previewing.
```bash
cmake --build <build_directory> --target trailbook_everest_live_preview
```
Builds the EVerest documentation and serves it with a local web server
for previewing. Additionally it watches for changes in the source files
and automatically rebuilds the documentation and refreshes the preview
in the browser.
## How things work
The trailbook is initialized in `${CMAKE_SOURCE_DIR}/docs/CMakeLists.txt`
with the `add_trailbook()` function call.
At the same file an edm snapshot yaml file is added to the documentation by
calling the function `trailbook_ev_create_snapshot()`.
Module explanations, modules references, type references and interface references are added automatically in `${CMAKE_SOURCE_DIR}/cmake/everest-generate.cmake`, by calling the functions
* `trailbook_ev_generate_rst_from_manifest()`,
* `trailbook_ev_generate_rst_from_types()`,
* `trailbook_ev_generate_rst_from_interface()`, and
* `trailbook_ev_add_module_explanation()`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1000.0001831 293.5350037" style="enable-background:new 0 0 1000.0001831 293.5350037;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.8;fill:#FFFFFF;}
.st1{fill:#FFFFFF;}
.st2{opacity:0.8;}
.st3{fill:#254C84;}
.st4{fill:#0C2351;}
.st5{fill:#BAF419;}
.st6{fill:#00CE7C;}
.st7{fill:none;}
.st8{fill:#2F96D0;}
.st9{fill:#1B3964;}
.st10{fill:none;stroke:#2F96D0;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;}
</style>
<polygon class="st3" points="229.9322662,125.096138 221.1637421,147.3405151 292.3886719,147.3405151 146.1943665,1.1462715
146.1943665,125.096138 "/>
<polygon class="st4" points="116.9741898,30.3663292 0,147.3405151 70.2879868,147.3405151 "/>
<polygon class="st5" points="174.8191528,264.9099731 292.3886719,147.3405151 221.1637421,147.3405151 "/>
<polygon class="st6" points="146.1943665,166.9629517 62.4563942,166.9629517 70.2879868,147.3405151 0,147.3405151
146.1943665,293.5350037 146.1943665,167.9319 "/>
<g>
<path class="st7" d="M751.2166748,31.4888992c0-4.816433-1.3758545-8.4856319-4.3571167-10.7788963
c-2.9812622-2.2933865-7.7976074-3.439537-14.6776733-3.439537h-10.7788696v28.6664124h12.3843384
c5.5032349,0,9.8613281-1.1471138,12.8425903-3.6690788C749.6112061,39.744751,751.2166748,36.3052139,751.2166748,31.4888992z"/>
<path class="st7" d="M606.2872925,167.1315002c-15.9805908,0-20.8442383,15.7489624-20.8442383,31.9611053v2.547821h40.0672607
v-6.715683C625.5103149,179.1748199,620.4150391,167.1315002,606.2872925,167.1315002z"/>
<path class="st7" d="M785.0786743,167.1315002c-15.9805908,0-20.8432617,15.7489624-20.8432617,31.9611053v2.547821h40.0672607
v-6.715683C804.3026733,179.1748199,799.2073975,167.1315002,785.0786743,167.1315002z"/>
<polygon class="st8" points="385.657135,64.7423248 359.9719543,64.7423248 359.9719543,38.8275414 346.8996582,38.8275414
346.8996582,77.584938 385.657135,77.584938 "/>
<polygon class="st9" points="359.7425232,25.7553768 398.7296448,25.7553768 398.7296448,64.7424393 392.3078003,64.7424393
392.3078003,77.5850601 411.5722656,77.5850601 411.5722656,12.9127579 346.8999023,12.9127579 346.8999023,32.1771698
359.7425232,32.1771698 "/>
<polygon class="st9" points="476.7032471,61.0734825 448.4949951,61.0734825 448.4949951,13.6009054 428.7723694,13.6009054
428.7723694,77.3556366 476.7032471,77.3556366 "/>
<polygon class="st9" points="504.9112244,53.0462151 530.3677979,53.0462151 530.3677979,37.9103279 504.9112244,37.9103279
504.9112244,29.8840237 534.7249146,29.8840237 534.7249146,13.6009054 485.1885681,13.6009054 485.1885681,77.3556366
504.9112244,77.3556366 "/>
<polygon class="st8" points="585.4077759,73.4572601 554.2182617,73.4572601 554.2182617,45.9369965 583.5726318,45.9369965
583.5726318,41.8084717 554.2182617,41.8084717 554.2182617,17.4991665 585.4077759,17.4991665 585.4077759,13.371603
549.6315918,13.371603 549.6315918,77.8143616 585.4077759,77.8143616 "/>
<path class="st8" d="M604.4425659,36.0756683c0-3.4404984,0-8.4856319-0.458252-15.3656654h0.458252l38.7574463,57.1043587
h4.586792V13.371603h-4.3571167v41.9684753c0,4.8153496,0.2286377,9.8613281,0.458252,15.1360016h-0.2296143L604.9008789,13.371603
h-4.586731v64.4427567h4.128418V36.0756683z"/>
<polygon class="st8" points="701.9096069,73.4572601 670.7200928,73.4572601 670.7200928,45.9369965 700.0744629,45.9369965
700.0744629,41.8084717 670.7200928,41.8084717 670.7200928,17.4991665 701.9096069,17.4991665 701.9096069,13.371603
666.1333618,13.371603 666.1333618,77.8143616 701.9096069,77.8143616 "/>
<path class="st8" d="M759.7021484,77.8142395l-17.6589355-29.3543129
c4.8153687-1.3758163,8.2558594-3.4404984,10.5492554-6.4219131c2.293396-2.9811707,3.4395142-6.6503716,3.4395142-11.0074787
c0-5.9635468-1.8340454-10.3205318-5.7329102-13.3018246c-3.8988037-2.9812927-9.8613281-4.3571072-17.8885498-4.3571072
h-15.5942993v64.4426346h4.586792V49.8357391h16.0534668l16.7414551,27.9785004H759.7021484z M733.7873535,45.9368782h-12.3843384
V17.2704659h10.7788696c6.8800659,0,11.6964111,1.1461506,14.6776733,3.439537
c2.9812622,2.2932644,4.3571167,5.9624634,4.3571167,10.7788963c0,4.8163147-1.6054688,8.2558517-4.586731,10.7789001
C743.6486816,44.7897644,739.2905884,45.9368782,733.7873535,45.9368782z"/>
<path class="st8" d="M798.9177246,77.3556366c7.7976074,0,14.6776123-1.3758163,20.869751-3.8988647V44.7902451h-23.3917847
v3.6691971h19.03479v22.2457047c-4.12854,1.6053543-8.9448242,2.5230484-14.9073486,2.5230484
c-9.1734619,0-16.0535278-2.2935028-20.6402588-7.1098175c-4.8153076-4.8163147-7.1087036-11.9250488-7.1087036-21.3281326
c0-8.4855118,2.5220337-15.3655491,7.3383789-20.6402264s11.4667358-7.7976055,19.9523315-7.7976055
c5.9625244,0,11.6954346,1.3768978,17.199646,3.8987427l1.835022-3.8987427
c-5.5042114-2.521965-11.9260254-3.8977795-18.8050537-3.8977795c-6.421814,0-12.1547241,1.3758144-17.2006836,4.1274433
c-5.0450439,2.7516289-8.7141724,6.4217911-11.4668579,11.4668045c-2.5219116,4.8163147-3.8987427,10.5492363-3.8987427,16.9710274
c0,10.3195686,2.7526245,18.1172943,8.0272217,23.8500938C781.0300903,74.6040115,788.8276978,77.3556366,798.9177246,77.3556366z"
/>
<polygon class="st8" points="848.4542236,77.3556366 853.0408936,77.3556366 853.0408936,52.588089 874.5986938,13.3713627
869.7823486,13.3713627 850.7476196,48.4595642 831.9424438,13.3713627 826.6677246,13.3713627 848.4542236,53.0463333 "/>
<polygon class="st6" points="373.3025818,202.7982483 432.5928345,202.7982483 432.5928345,180.5643463 373.3025818,180.5643463
373.3025818,134.7071075 436.2984924,134.7071075 436.2984924,112.4731979 346.8999023,112.4731979 346.8999023,277.838623
438.1513062,277.838623 438.1513062,255.6047058 373.3025818,255.6047058 "/>
<polygon class="st6" points="499.752594,244.4877472 499.2893982,244.4877472 471.2654724,112.4731979 443.2415771,112.4731979
482.1507874,277.838623 515.7332153,277.838623 557.4216309,112.4731979 529.3977661,112.4731979 "/>
<path class="st4" d="M606.5189209,149.9930115c-39.1408691,0-46.0889893,34.7401581-46.0889893,67.3963776
c0,57.6689606,25.2446899,63.6907501,45.6257324,63.6907501c27.0975342,0,43.7729492-17.3702087,43.7729492-44.9300537h-23.6235352
c0,7.4103394-2.0844116,27.7922668-19.9177856,27.7922668c-18.7598267,0-20.8442383-21.307312-20.8442383-37.9828644v-7.1795959
h65.0803223v-13.2023315C650.5233765,170.8371582,641.2592773,149.9930115,606.5189209,149.9930115z M625.5103149,201.6404266
h-40.0672607v-2.547821c0-16.2121429,4.8636475-31.9611053,20.8442383-31.9611053
c14.1277466,0,19.2230225,12.0433197,19.2230225,27.7932434V201.6404266z"/>
<path class="st4" d="M696.6103516,172.689743h-0.4632568v-19.4546204h-25.0130615v124.6032562h25.0130615V203.262146
c0-23.1602631,13.6646118-28.2554474,25.0130615-28.2554474c4.1688843,0,8.5693359,0.9253998,10.4221802,1.3888092v-25.4763794
c-1.6212769-0.4622192-3.2424316-0.9263611-6.0216675-0.9263611
C711.4328613,149.9927673,702.1688232,158.7945709,696.6103516,172.689743z"/>
<path class="st4" d="M785.3102417,149.9930115c-39.1398315,0-46.0888672,34.7401581-46.0888672,67.3963776
c0,57.6689606,25.2456055,63.6907501,45.6266479,63.6907501c27.0974731,0,43.7728882-17.3702087,43.7728882-44.9300537h-23.6234741
c0,7.4103394-2.0844116,27.7922668-19.9187622,27.7922668c-18.7598267,0-20.8432617-21.307312-20.8432617-37.9828644v-7.1795959
h65.0802612v-13.2023315C829.3156738,170.8371582,820.0516357,149.9930115,785.3102417,149.9930115z M804.3026733,201.6404266
h-40.0672607v-2.547821c0-16.2121429,4.8626709-31.9611053,20.8432617-31.9611053
c14.1287231,0,19.223999,12.0433197,19.223999,27.7932434V201.6404266z"/>
<path class="st4" d="M902.9628906,207.1990356l-15.9805298-5.5583649
c-13.201416-4.6320038-17.6027832-8.5693665-17.6027832-18.2967834c0-11.5800171,7.8745117-16.2121429,17.833374-16.2121429
c12.9707642,0,16.4448242,9.2632904,16.4448242,19.2231445v3.2424774h23.6234131v-5.5585938
c0-15.5172577-7.1796875-34.0464783-38.9102783-34.0464783c-25.7078857,0-42.6138306,10.8852386-42.6138306,34.278183
c0,19.9177856,7.8734131,29.4135132,26.6333008,35.8984528l20.382019,7.1795959
c8.8008423,3.0107727,13.6635742,9.032547,13.6635742,18.2965546c0,11.3484192-6.7165527,18.29776-20.380127,18.29776
c-14.3602295,0-19.4555664-7.8756714-19.4555664-22.2348785v-3.9373627h-22.2328491v4.6320038
c0,24.0878143,9.4957275,38.6777496,40.9936523,38.6777496c25.2437134,0,44.6983643-11.3474426,44.6983643-37.9828644
C930.0594482,223.4114075,921.4911499,213.4522705,902.9628906,207.1990356z"/>
<path class="st4" d="M1000.0001831,171.7643433v-18.5292206h-20.380127v-35.4351578h-25.0131226v35.4351578h-17.1384888v18.5292206
h17.1384888v77.3554077c0,25.4761353,8.3377686,30.1081543,25.2446899,30.1081543
c7.8735352,0,14.8226318-0.6948853,20.1485596-2.0844116v-18.5282593
c-2.3150635,0.4634094-5.3259277,0.6948853-9.0316162,0.6948853c-8.5692139,0-11.3485107-3.4749146-11.3485107-13.2023468
v-74.3434296H1000.0001831z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -0,0 +1,17 @@
# Main Sphinx library
sphinx
# The Furo theme
furo
# Provides modern design elements like grids, cards, and tabs
sphinx-design
# Adds a "copy" button to code blocks for a better user experience
sphinx-copybutton
# For rendering mermaid diagrams
sphinxcontrib-mermaid
# Required for processing templates
PyYAML

View File

@@ -0,0 +1,13 @@
:orphan:
##########################################
Page not found
##########################################
Your request could not be processed because the page you requested does not exist.
.. note::
Since the 2023.1.0 release of the documentation.
The structure of the documentation has changed.
You can find the latest build here: https://everest.github.io/latest

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Manuel Kaufmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1 @@
__version__ = "0.2.0"

View File

@@ -0,0 +1,329 @@
import docutils
import os
import sphinx
import warnings
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.errors import ExtensionError
from . import __version__
from .utils import replace_uris
class BaseURIError(ExtensionError):
"""Exception for malformed base URI."""
pass
# https://www.sphinx-doc.org/en/stable/extdev/appapi.html#event-html-collect-pages
def html_collect_pages(app):
"""
Create for each ``<pagename>`` a html page.
Uses ``<template>`` as a template to be rendered with
``<context>`` for its context. The resulting file generated is
``<pagename>.html``.
If the user already defined a page with pagename title
``<pagename>``, we don't generate this page.
:param app: Sphinx Application
:type app: sphinx.application.Sphinx
"""
result = []
for page in app.config.staticpages_pages:
if page['pagename'] in app.env.titles:
# There is already a ``<pagename>.rst`` file rendered.
# Skip generating our default one.
continue
result.append((
page['pagename'],
page['context'],
page['template'],
))
return result
# https://www.sphinx-doc.org/en/stable/extdev/appapi.html#event-html-page-context
def finalize_media(app, pagename, templatename, context, doctree):
"""
Point media files at our media server.
Generate absolute URLs for resources (js, images, css, etc) to point to the
right URL. For example, if a URL in the page is ``_static/js/custom.js`` it will
be replaced by ``<urls_prefix>/_static/js/custom.js``.
Also, all the links from the sidebar (toctree) are replaced with their
absolute version. For example, ``../section/pagename.html`` will be replaced
by ``/section/pagename.html``.
It handles a special case for Read the Docs and URLs starting with ``/_/``.
These URLs have a special meaning under Read the Docs and don't have to be changed.
(e.g. ``/_/static/javascript/readthedocs-doc-embed.js``)
:param app: Sphinx Application
:type app: sphinx.application.Sphinx
:param pagename: name of the page being rendered
:type pagename: str
:param templatename: template used to render the page
:type templatename: str
:param context: context used to render the page
:type context: dict
:param doctree: doctree of the page being rendered
:type doctree: docutils.nodes.document
"""
# https://github.com/sphinx-doc/sphinx/blob/7138d03ba033e384f1e7740f639849ba5f2cc71d/sphinx/builders/html.py#L1054-L1065
def pathto(otheruri, resource=False, baseuri=None):
"""
Hack pathto to display absolute URL's.
Instead of calling ``relative_url`` function, we call
``app.builder.get_target_uri`` to get the absolute URL.
.. note::
If ``otheruri`` is a external ``resource`` it does not modify it.
If ``otheruri`` is a static file on Read the Docs it does not modify it.
"""
current_page = {}
for page in app.config.staticpages_pages:
if page['pagename'] == pagename:
current_page = page
break
READTHEDOCS = os.environ.get('READTHEDOCS', False) == 'True'
if resource and '://' in otheruri:
# allow non-local resources given by scheme
return otheruri
if READTHEDOCS and otheruri.startswith('/_/'):
# special case on Read the Docs
return otheruri
if not resource:
otheruri = app.builder.get_target_uri(otheruri)
if baseuri is None:
if current_page['urls_prefix'] is None:
baseuri = '/'
else:
baseuri = '{prefix}'.format(
prefix=current_page['urls_prefix'] or '/',
)
if not baseuri.startswith('/'):
raise BaseURIError('"baseuri" must be absolute')
if otheruri and not otheruri.startswith('/'):
otheruri = '/' + otheruri
if otheruri:
if baseuri.endswith('/'):
baseuri = baseuri[:-1]
otheruri = baseuri + otheruri
uri = otheruri or '#'
return uri
# https://github.com/sphinx-doc/sphinx/blob/2adeb68af1763be46359d5e808dae59d708661b1/sphinx/builders/html.py#L1081
def toctree(*args, **kwargs):
current_page = {}
for page in app.config.staticpages_pages:
if page['pagename'] == pagename:
current_page = page
break
try:
# Sphinx >= 1.6
from sphinx.environment.adapters.toctree import TocTree
get_toctree_for = TocTree(app.env).get_toctree_for
except ImportError:
# Sphinx < 1.6
get_toctree_for = app.env.get_toctree_for
toc = get_toctree_for(
current_page['pagename'],
app.builder,
collapse=kwargs.pop('collapse', False),
includehidden=kwargs.pop('includehidden', False),
**kwargs # not using trailing comma here makes this compatible with
# Python2 syntax
)
# If no TOC is found, just return ``None`` instead of failing here
if not toc:
return None
replace_uris(app, toc, docutils.nodes.reference, 'refuri')
return app.builder.render_partial(toc)['fragment']
# Borrowed from Sphinx<4.x to backward compatibility
# https://github.com/sphinx-doc/sphinx/blob/v3.5.4/sphinx/builders/html/__init__.py#L1003-L1010
def css_tag(css):
attrs = []
for key in sorted(css.attributes):
value = css.attributes[key]
if value is not None:
if sphinx.version_info < (2, 0):
# https://github.com/sphinx-doc/sphinx/blob/v1.8.5/sphinx/builders/html.py#L1144
from sphinx.util.pycompat import htmlescape
attrs.append('%s="%s"' % (key, htmlescape(value, True)))
else:
import html
attrs.append('%s="%s"' % (key, html.escape(value, True)))
attrs.append('href="%s"' % pathto(css.filename, resource=True))
return '<link %s />' % ' '.join(attrs)
# Apply our custom manipulation for each <pagename>.html page only
pagenames = []
pagenames.extend(page['pagename'] for page in app.config.staticpages_pages)
if pagename in pagenames:
# Override the ``pathto`` helper function from the context to use a custom one
# https://www.sphinx-doc.org/en/master/templating.html#pathto
context['pathto'] = pathto
# Override the ``toctree`` helper function from context to use a custom
# one and generate valid links on not found page.
# https://www.sphinx-doc.org/en/master/templating.html#toctree
# NOTE: not used on ``singlehtml`` builder for RTD Sphinx theme
context['toctree'] = toctree
if sphinx.version_info < (4, 0) or sphinx.version_info >= (7, 0):
context['css_tag'] = css_tag
# https://www.sphinx-doc.org/en/stable/extdev/appapi.html#event-doctree-resolved
def doctree_resolved(app, doctree, docname):
"""
Generate and override URLs for ``.. image::`` Sphinx directive.
When ``.. image::`` is used in the ``<pagename>.rst`` file, this function will
override the URLs to point to the right place.
:param app: Sphinx Application
:type app: sphinx.application.Sphinx
:param doctree: doctree representing the document
:type doctree: docutils.nodes.document
:param docname: name of the document
:type docname: str
"""
pagenames = []
pagenames.extend(item['pagename'] for item in app.config.staticpages_pages)
if docname in pagenames:
# Replace image ``uri`` to its absolute version
replace_uris(app, doctree, docutils.nodes.image, 'uri')
class OrphanMetadataCollector(EnvironmentCollector):
"""
Force the staicpages pages to be ``orphan``.
This way we remove the WARNING that Sphinx raises saying the page is not
included in any toctree.
This collector has the same effect than ``:orphan:`` at the top of the page.
"""
def clear_doc(self, app, env, docname):
return None
def process_doc(self, app, doctree):
for page in app.config.staticpages_pages:
metadata = app.env.metadata[page['pagename']]
metadata.update({'orphan': True})
if sphinx.version_info >= (3, 0, 0):
metadata.update({'nosearch': True})
def merge_other(self, app, env, docnames, other):
"""Merge in specified data regarding docnames from a different `BuildEnvironment`
object which coming from a subprocess in parallel builds."""
# TODO: find an example about why this is strictly required for parallel read
# https://github.com/readthedocs/sphinx-notfound-page/pull/112/files#r498219556
env.metadata.update(other.metadata)
def validate_configs(app, *args, **kwargs):
"""
Validate configs.
Shows a warning if one of the configs is not valid.
"""
for page in app.config.staticpages_pages:
if 'pagename' not in page:
message = 'pagename is required for each page'
warnings.warn(message, UserWarning, stacklevel=2)
if 'template' not in page:
page.update({'template': 'page.html'})
message = 'template is required for each page, using default'
warnings.warn(message, UserWarning, stacklevel=2)
if 'context' not in page:
page.update({'context': {
'title': 'Page Title',
'body': "<h1>Page Title</h1>\n\nThis is a placeholder page.",
}})
message = 'context is required for each page, using default'
warnings.warn(message, UserWarning, stacklevel=2)
if 'urls_prefix' not in page:
message = 'urls_prefix is required for each page'
warnings.warn(message, UserWarning, stacklevel=2)
def setup(app):
default_pages = [{
'template': 'page.html',
'context': {
'title': 'Versions Index',
'body': "<h1>Versions Index</h1>\n\nThis is a placeholder page for versions index.",
},
'pagename': 'versions_index',
'urls_prefix': '/en/latest/',
}]
app.add_config_value('staticpages_pages', default_pages, 'html')
# FIXME this config shouldn't exist, see _ext/staticpages/utils.py
app.add_config_value('staticpages_urls_prefix', None, 'html')
if sphinx.version_info > (1, 8, 0):
app.connect('config-inited', validate_configs)
else:
app.connect('builder-inited', validate_configs)
app.connect('html-collect-pages', html_collect_pages)
if sphinx.version_info >= (3, 0, 0):
# Use ``priority=400`` argument here because we want to execute our function
# *before* Sphinx's ``setup_resource_paths`` where the ``logo_url`` and
# ``favicon_url`` are resolved.
# See https://github.com/readthedocs/sphinx-notfound-page/issues/180#issuecomment-959506037
app.connect('html-page-context', finalize_media, priority=400)
else:
app.connect('html-page-context', finalize_media)
app.connect('doctree-resolved', doctree_resolved)
# Sphinx injects some javascript files using ``add_js_file``. The path for
# this file is rendered in the template using ``js_tag`` instead of
# ``pathto``. The ``js_tag`` uses ``pathto`` internally to resolve these
# paths, we call again the setup function for this tag *after* the context
# was overridden by our extension with the patched ``pathto`` function.
if sphinx.version_info < (7, 0):
if sphinx.version_info >= (1, 8):
from sphinx.builders.html import setup_js_tag_helper
app.connect('html-page-context', setup_js_tag_helper)
if sphinx.version_info >= (4, 0):
# CSS are now added via a ``css_tag``
# https://github.com/sphinx-doc/sphinx/pull/8643
from sphinx.builders.html import setup_css_tag_helper
app.connect('html-page-context', setup_css_tag_helper)
app.add_env_collector(OrphanMetadataCollector)
return {
'version': __version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@@ -0,0 +1,79 @@
import docutils
import re
import sphinx
# Sphinx <2 Compatibility
if sphinx.version_info >= (2, 0):
from sphinx.builders.dirhtml import DirectoryHTMLBuilder
else:
from sphinx.builders.html import DirectoryHTMLBuilder
def replace_uris(app, doctree, nodetype, nodeattr):
"""
Replace ``nodetype`` URIs from ``doctree`` to the proper one.
If ``nodetype`` is an image (``docutils.nodes.image``), the URL is prefixed
with ``Builder.imagedir`` and the original image path is added to
``Builder.images`` so it's copied using Sphinx's internals before
finalizing the building.
:param app: Sphinx Application
:type app: sphinx.application.Sphinx
:param doctree: doctree representing the document
:type doctree: docutils.nodes.document
:param nodetype: type of node to replace URIs
:type nodetype: docutils.nodes.Node
:param nodeattr: node attribute to be replaced
:type nodeattr: str
"""
# https://github.com/sphinx-doc/sphinx/blob/2adeb68af1763be46359d5e808dae59d708661b1/sphinx/environment/adapters/toctree.py#L260-L266
for node in doctree.traverse(nodetype):
uri = olduri = node.attributes.get(nodeattr) # somepage.html (or ../sompage.html)
if isinstance(app.builder, DirectoryHTMLBuilder):
# When the builder is ``DirectoryHTMLBuilder``, refuri will be
# ``../somepage.html``. In that case, we want to remove the
# initial ``../`` to make valid links
if uri.startswith('../'):
uri = uri.replace('../', '')
if re.match('^https?://', uri):
# allow non-local URLs for resources
continue
imagedir = ''
if nodetype is docutils.nodes.image:
# Prefix the URL with ``Builder.imagedir`` to use the internal's
# Sphinx image handling if the node is an image
imagedir = '{imagedir}/'.format(
imagedir=app.builder.imagedir,
)
# The image is copied into ``app.builder.imagedir`` without keeping
# the directory structure, so we need only the filename for the
# correct link
uri = olduri.split('/')[-1]
uri = '{prefix}{imagedir}{filename}'.format(
prefix=app.config.staticpages_urls_prefix or '/',
imagedir=imagedir,
filename=uri,
)
node.replace_attr(nodeattr, uri)
# Force adding the image to the builder so it's copied at ``Builder.copy_image_files``
# https://github.com/sphinx-doc/sphinx/blob/5ce5c2c3156c53c1f1b758c38150e48080138b15/sphinx/builders/html.py#L721
# We need to do this at this point because ``Builder.post_process_images``
# does not add it automatically as the path does not match.
# https://github.com/sphinx-doc/sphinx/blob/5ce5c2c3156c53c1f1b758c38150e48080138b15/sphinx/builders/__init__.py#L189
if nodetype is docutils.nodes.image:
if all([
not olduri.startswith('data:'),
'://' not in olduri,
]):
app.builder.images[olduri] = olduri.split('/')[-1]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

View File

@@ -0,0 +1,9 @@
{% extends "!page.html" %}
{% block title %}Versions Index{% endblock %}
{% block content %}
{{ super() }}
<h1>Versions Index</h1>
{% for version in versions %}
<a href="/{{ version }}/index.html">{{ version }}</a> -> <a href="/{{ version }}/appendix/snapshot.html">snapshot.yaml</a></br>
{% endfor %}
{% endblock %}

View File

@@ -0,0 +1,102 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import os
import sys
sys.path.append(os.path.abspath("./_ext"))
import yaml
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here.
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'EVerest Documentation'
copyright = '2021-2025, EVerest community'
author = 'EVerest community'
# The full version, including alpha/beta/rc tags, taken from CMakeLists.txt
release = '@PROJECT_VERSION@'
# The short X.Y version.
version = '.'.join(release.split('.')[:2])
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.autodoc', # Core library for autodoc
'sphinx.ext.napoleon', # Support for Google and NumPy style docstrings
'sphinx.ext.viewcode', # Add links to highlighted source code
'sphinx_copybutton', # Add a "copy" button to code blocks
'sphinx_design', # For advanced design elements like cards, grids
'sphinx.ext.mathjax', # For rendering math equations
'sphinxcontrib.mermaid', # For rendering Mermaid diagrams
'staticpages.extension', # For static pages like versions_index.html
]
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [
'_build',
'Thumbs.db',
'.DS_Store',
'venv', # Exclude the virtual environment directory
'README.md',
]
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
# The theme to use for HTML and HTML Help pages.
# See the documentation for a list of builtin themes.
html_theme = 'furo'
# A shorter title for the navigation bar.
html_title = f"EVerest Manual {release}"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_logo = '_static/everest_icon-color.png'
html_favicon = '_static/favicon.png'
html_theme_options = {
"sidebar_hide_name": False,
}
metadata_yaml_path = os.getenv('EVEREST_METADATA_YAML_PATH', "metadata_everest.yaml")
with open(metadata_yaml_path, 'r') as f:
metadata = yaml.safe_load(f)
deployed_versions = metadata.get('versions', [])
versionsindex_page = {
'pagename': 'versions_index',
'urls_prefix': '/latest/',
'template': 'versions_index.html',
'context': {
'versions': deployed_versions,
},
}
notfound_page = {
'pagename': '404',
'urls_prefix': '/latest/',
}
staticpages_pages = [
versionsindex_page,
notfound_page,
]
staticpages_urls_prefix = '/latest/'

View File

@@ -0,0 +1,277 @@
############
EVerest APIs
############
.. tip::
You can find the API reference documentation here:
:doc:`EVerest API Reference </reference/api/autogenerated_api_index>` .
EVerest can be extended and adapted to specific needs via two sets of
APIs: The internal interfaces and the EVerest API.
The EVerest API are versioned and guaranteed to not introduce breaking
changes for the same major version of the API, while the internal
interfaces may change between versions. Therefore, the EVerest API
is the preferred way to implement custom integrations,
extensions and adaptations.
.. tip::
If you plan to integrate EVerest on your charging station hardware we
strongly recommend to use the EVerest APIs for this purpose, since these
interfaces are the ones that are supposed to be kept stable and maintained.
over time.
Overview
========
The EVerest API is the interface for hardware integration,
custom extensions and adaptations.
It is provided via a public MQTT interface, and the format for data
exchange is plain text JSON. The API is defined in AsyncAPI 3.
Custom client software using the EVerest API is therefore only loosely
coupled to the EVerest application and its internal interfaces.
There are no obligatory binary or link time dependencies of any kind.
As a result, the clients can be built individually and without
reference to the EVerest application at all.
This also implies that the programming language used for implementing
the client can be chosen freely. Typically a compiled language is
preferable for an embedded target, but it is not strictly required at
all.
The EVerest API is implemented in terms of EVerest modules, and every
API module implements one or more of EVerest's internal interfaces.
There are two distinct versions of API modules, either to implement
an interface (e.g. a driver for a DC power supply) or to consume it
(e.g. to check for the validation status of a token).
They can be included in the configuration file just like any other
module. E.g. - in this example -, a EVerest API module was loaded to
fulfill the power meter requirement of the EvseManager. The actual code
that talks to the power meter hardware to fetch the measurements can now
be implemented in a process running outside of EVerest (and is started
e.g. by a separate *systemd* unit). It just needs to feed the measured
values via MQTT into the
:ref:`Powermeter API module <everest_modules_powermeter_API>`.
.. figure:: images/everest-api-1.png
:alt: EVerest API - Image 1
:width: 450px
For a better understanding of how the EVerest API works, let us have an
exemplary closer look at the
:ref:`Power Supply DC API module <everest_modules_power_supply_DC_API>`.
The manifest can be reduced to this:
.. code-block:: yaml
config:
cfg_communication_check_to_s:
type: integer
default: 5
cfg_heartbeat_interval_ms:
type: integer
default: 1000
provides:
if_power_supply_DC:
interface: power_supply_DC
There are two configuration variables which are common for every API
module. Both are related to communication checks between EVerest and
the client module.
Below, there is the *provides* section, which states that this API
module *is* a :doc:`DC power supply </reference/interfaces/power_supply_DC>`.
This implies that it can be used in the configuration file for EVerest
wherever a DC power supply is expected.
In a product, this would be the :ref:`EvseManager <everest_modules_EvseManager>`
requiring a DC power supply. During development or validation it could also be a
BringUp module.
In contrast to an integrated driver for actual hardware, the API module
creates MQTT topics according to its specification and by this provides
hooks for the client to do the implementation work.
The documentation of the APIs can be found in the respective :doc:`reference
pages </reference/api/autogenerated_api_index>`. Each API module has its own
reference page describing the messages, topics and data structures used.
Let's take a look at an example configuration that uses the API module:
.. code-block:: yaml
active_modules:
ps_dc_1:
module: power_supply_DC_API
config_module:
cfg_communication_check_to_s: 60
cfg_heartbeat_interval_ms: 1000
cli:
module: BUPowerSupplyDC
standalone: true
connections:
psu:
- module_id: ps_dc_1
implementation_id: if_power_supply_DC
It loads two modules:
The :ref:`power_supply_DC_API <everest_modules_power_supply_DC_API>`
and the
:ref:`BringUp module for DC power supplies <everest_modules_BUPowerSupplyDC>`.
Starting EVerest with this configuration enables the API for DC power
supplies and a BringUp module, that can send to and receive messages from the
API. The actual topics on the MQTT will be available under
*everest_api/1/power_supply_DC/ps_dc_1/*.
It is as simple as this.
As explained previously, the client is only loosely coupled to EVerest.
As a consequence, EVerest cannot know by itself whether the client is
available and in good working conditions. For this reason, a
bidirectional communication check is available.
EVerest APIs sends *heartbeat* messages periodically
(*cfg_heartbeat_interval_ms* - with negative values disabling heartbeat
messages) and on the other hand requires the clients to send
*communication_check* messages within the timeout interval specified
(*cfg_communication_check_to_s* - with negative values disabling the
requirement for these messages).
In situations where a request/reply pattern is implemented, the timeout
for a response can be configured (*cfg_request_reply_to_s* - these timeouts
cannot be disabled since internal EVerest timeouts apply) . In general it
is advisable to respond as quickly as possible in to to prevent EVerest
from blocking internally.
AsyncAPI
========
The EVerest API is defined in terms of AsyncAPI 3.0.0.
For a thorough introduction and reference refer to
https://www.asyncapi.com .
All EVerest API modules are located in *EVerest/modules/API* and each
module contains the API definition in
*EVerest/docs/source/reference/EVerest_API/<name-of-api>.yaml* file.
These files are used to generate the
:doc:`HTML-based documentation </reference/api/autogenerated_api_index>`.
In order to build the documentation including the API reference pages,
run the following command in your *build* folder:
.. code-block:: bash
cmake -DEVEREST_BUILD_DOCS=ON .. && make trailbook_everest
Another possible way is to use the AsyncAPI yaml source file in
`AsyncAPI Studio <https://https://studio.asyncapi.com/>`_.
The *<name-of-api>.yaml* is defined for the client implementing the API and
reflects the client's point of view, when using the words *send* and
*receive* in the context of actions and operations.
The MQTT topics of the EVerest API follow a fixed pattern. All topics
are prefixed with *everest_api/1/{api_type}/{module_id}* - with *1*
being the version and *{api_type}* the type of the API.
*{module_id}* is the *module id* of the API module as configured in the
EVerest configuration file.
The prefix is followed by the direction of the message. There are two
options:
- *m2e* for messages from the module (client) to EVerest and
- *e2m* for messages from EVerest to the module (client).
This is finally followed by the name of the message. Here is a complete
example:
.. code-block:: text
everest_api/1/power_supply_DC/ps_dc_1/m2e/voltage_current
In the example, *power_supply_DC* is the API type, *ps_dc_1* is the
*module id* as configured in the EVerest configuration file, the
message (*m2e*) originates from the client and is directed towards
EVerest. The message name is *voltage_current*.
AsyncAPI defines channels which carry messages. A channel can be
addressed via the topic as defined above. Each channel can in principle
carry multiple messages, but concerning EVerest API, there is a
one-to-one mapping between a message and a channel. A message carries
content in the form of payload and possibly headers.
The content type for EVerest API is always JSON. The content is
individual for each message and defined in *components:schemas* within
the same file or sometimes in a referenced file.
AsyncAPI finally specifies operations on channels. The operations define
the action on a specific channel (for EVerest API always from the
client's point of view), which can be *send* or *receive*.
In many situations, the sender of a message is not interested in the
receive status of a message, e.g. in a situation where a meter publishes
its current values. Simple send and receive operations are used in this
case.
There are situations however, where this is not the case, e.g. remote
procedure calls or when a reply is requested. EVerest API handles this
situation with the request/reply pattern offered by AsyncAPI.
The operations are then augmented with a reply property holding the
reply channel and a dynamic reply address.
If the client receives a request, it has to reply to the topic provided
in the header's *replyTo* property within *cfg_communication_check_to_s*
seconds. If it does not, a default response is given by the API to
EVerest and an error is raised.
If the client sends a request, it has to specify the reply topic, where
it expects the answer. This information is communicated via the
*replyTo* property of the headers object. It is the client's
responsibility to ensure that the topic is unique in order to relate
replies to requests.
Using the EVerest API
======================
In order to use the EVerest API, load the required API modules in the
EVerest configuration file and connect its interfaces as presented in
the *Overview* section.
The chosen *module id* becomes part of the MQTT topic. EVerest API
modules can be loaded multiple times, e.g. if two DC power supplies are
connected.
If needed, adjust the heartbeat interval and communication check timeout
via the *cfg_heartbeat_interval_ms* and *cfg_communication_check_to_s*
configuration variables of the module.
Although communication check and heartbeat can be disabled with values
smaller or equal to zero, this is not recommended in a production
environment, since they are the only way to continuously check whether
the client and EVerest are online and responsive.
EVerest API clients are completely independent applications. They have
to be started independently of EVerest, possibly by their own *systemd*
service.
EVerest cannot start them, since it is agnostic of them. On EVerest
startup, the API modules raise an initial communication check error (if
communication check is enabled). This error is cleared with the first
communication check message sent from the client. It is raised again
when a timeout occurs or a request is not answered. Sending a
communication check message clears the error again.
It is the responsibility of the user to ensure that the client is and
remains available. This includes potentially a *watchdog* that restarts
the client in case of crash or deadlock. It is also the client's
responsibility to ensure proper initialization, shutdown and
surveillance of managed hardware, e.g. a DC power supply.

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -0,0 +1,14 @@
#############
Adapt EVerest
#############
EVerest is designed to be easily adaptable to different use cases and
environments. This section will guide you through the process of adapting
EVerest to your specific needs by explaining the various sub-systems and APIs
available.
.. toctree::
:maxdepth: 1
sub-systems
apis

View File

@@ -0,0 +1,416 @@
###########
Sub-systems
###########
The following sections are a functional description of the subsystems of
EVerest. A subsystem is a group of modules that together implement a
specific functionality. This documentation guides you through some of the
most important subsystems step by step, showing how to build them up from
individual modules. The following subsystems will be explained:
- Charging subsystem
- Authentication subsystem
- OCPP subsystem
- Energy management subsystem
Charging subsystem
==================
The charging subsystem contains all modules that are responsible for the
charging logic, car communication and hardware drivers related to a
single charging port. For multiple charging ports the whole subsystem
can simply be duplicated.
It depends on the Authentication subsystem to authorize charging
sessions and on the Energy management subsystem to allocate the energy
budget for the charging process.
The main module of this subsystem is the
:ref:`EvseManager <everest_modules_EvseManager>`.
It contains all the logic and state machines for one CCS AC or DC charging
port.
.. _ac-charging-port:
AC charging port
------------------
Let's build an AC charging port step by step. Start with the EvseManager
module. For a simple AC charger, the default configuration of the module
is sufficient.
First, we add a board support module. This is the most important
hardware driver.
In this example, we use the :ref:`YetiDriver <everest_modules_YetiDriver>`
module which is the BSP driver for the BelayBox. Check the configuration of
the module; it should be correct for the BelayBox. Click on the (i) button
to learn more about each configuration setting.
.. figure:: images/yeti-driver-config.png
:alt: Yeti Driver Configuration
:width: 320px
The first connection is for the mandatory
:doc:`evse_board_support interface </reference/interfaces/evse_board_support>`.
It implements the following functionality:
- Control Pilot Signal (CP): Set PWM duty cycle in X2, State X1/F,
report states A-F
- Set allow relays on/off flag
- Report PP resistor (if used)
- Report AC input current / phases capabilities of the system
- Stop charging button input
.. figure:: images/yeti-evse-connection.png
:alt: Yeti Driver connected to EVSE Manager
:width: 360px
As you can see, there are two more connections between the
YetiDriver and the EvseManager. Those are optional:
- :doc:`connector_lock </reference/interfaces/connector_lock>`
(if used in the hardware): EvseManager requests
locking/unlocking of the connector lock (for AC Type 2 Sockets)
- :doc:`ac_rcd </reference/interfaces/ac_rcd>` (optional):
Reports information about the RCD module in an
AC charger. This is only used for telemetry and error reporting,
the actual shutdown needs to be handled in hardware.
These two modules already allow basic AC charging functionality. Next
step is to add a power meter to allow for invoicing the charged amount
of energy. It may not be needed e.g. in a private installation.
You can use any hardware driver module that provides a
:doc:`powermeter interface </reference/interfaces/powermeter>`
implementation. For the BelayBox, we could have used the
*powermeter* implementation of the YetiDriver (which uses the Yeti
onboard metering). In most configurations, an external DIN-rail power
meter is used with an RS485/ModBus connection, so we use the
:ref:`GenericPowermeter <everest_modules_GenericPowermeter>`
module here:
.. figure:: images/powermeter-connection.png
:alt: Power meter connection
:width: 420px
:ref:`GenericPowermeter <everest_modules_GenericPowermeter>`
is a module that can easily be adapted to most ModBus-based
AC power meters by specifying the register mappings in the
configuration of that module. It requires a
:ref:`SerialCommHub <everest_modules_SerialCommHub>`
module to do the actual read/write to the serial RS485 port of the system.
This is a separate module as multiple device drivers may use the same
serial bus, so they can all be connected to the same
:ref:`SerialCommHub <everest_modules_SerialCommHub>` module.
Make sure to configure the correct serial device and baud rate.
Note that there are two optional requirements for power meters in the
EvseManager: *powermeter_grid_side* and *powermeter_car_side*. The first
one should be used if the power meter is measuring on the AC input side,
the second one should be used if it is measuring the output power to the
vehicle. It is ok to connect both if you have two power meters. For AC,
one is generally sufficient. For DC, two meters on AC and DC side may be
useful.
Next step is to add ISO communication, if support for AC ISO15118-2 is
desired:
.. figure:: images/iso-communication-connection.png
:alt: ISO Communication Connection
:width: 700px
Three modules have been added:
:ref:`EvseV2G <everest_modules_EvseV2G>`:
Implementation of ISO15118-2(AC/DC) and DIN SPEC70121(DC).
Connects to the *hlc/ISO15118_charger* requirement of EvseV2G. Make sure
to set the “device” config option to the ethernet device of the PLC
modem. Leave the other options on default for now.
:ref:`EvseSecurity <everest_modules_EvseSecurity>`:
Handles certificates and private keys for TLS/PnC. We
will connect it here even though PnC is not enabled yet.
:ref:`EvseSlac <everest_modules_EvseSlac>`:
Implementation of ISO15118-3 (SLAC) to pair the PLC modems
of the EV and the EVSE at the start of the session. Make sure to
configure the same “device” as used for the EvseV2G. The two must point
to the same PLC modem.
To actually enable ISO 15118, some settings in the EvseManager module
need to be adjusted:
----
.. figure:: images/enable-iso15118.png
:alt: Enable ISO 15118
:width: 350px
----
The recommended setting is to use *ac_hlc_enable*, which allows ISO
15118-2 sessions on AC. In this configuration, nominal PWM (>10% duty
cycle) will be used the same way as it is used for basic charging
(non-ISO) sessions. This is the most interoperable way that charges all
cars, even if they do not support ISO 15118-2 on AC. All other options
will only charge a subset of EVs out there.
Some cars however may not use ISO at all if they detect nominal PWM -
even if they support ISO 15118-2. You enable set *ac_hlc_use_5percent*
to start with 5% duty cycle PWM on CP similar to DC charging.
Several cars will now use ISO communication for AC. The downside is that
there are cars that cannot be charged at all, because they allow only DC
to be used when 5% signaling is enabled. (They violate the ISO 15118-3,
but many car implementations do it that way.)
With this setting, EVerest may switch back to nominal PWM from 5% in
accordance with the ISO 15118-3 regulations. Some EVs may cancel ISO
communication in this case, even though the standard clearly states
differently.
For those EVs, you can set the *ac_enforce_hlc* option to “true”. Then,
the 5% PWM will be used throughout the complete charging session as it
is done for DC. This is not allowed according to the standard though.
For completeness, well also add a
:ref:`PersistentStore <everest_modules_PersistentStore>`
module to the *kvs/persistent_store* requirement of the
:ref:`EvseManager <everest_modules_EvseManager>`.
This is needed to persistently store charging session information e.g-
for German Eichrecht requirements. It may not be needed in simple
configurations.
.. figure:: images/persistent-store-connection.png
:alt: Persistent Store Connection
:width: 700px
For AC, we are complete now.
.. _dc-charging-port:
DC charging port
------------------
Let's build a DC charging port using the phyVERSO board. Start with the
:ref:`EvseManager <everest_modules_EvseManager>` again and adjust
one setting to switch it to DC mode:
.. figure:: images/switch-to-dc-mode.png
:alt: Switch to DC Mode
:width: 320px
The basic configuration looks similar to the AC one. We just exchanged
the :ref:`YetiDriver <everest_modules_YetiDriver>` with the
:ref:`PhyVersoBSP <everest_modules_PhyVersoBSP>` driver and connected the
*connector_1* interface for *evse_board_support* to EvseManager. Note
that *connector_lock* and *ac_rcd* are no longer needed on DC.
The second port of the phyVERSO will not be used here but could be
connected to a second EvseManager. Check the configuration options and
especially verify serial port, baud rate, capabilities and reset GPIO.
They should be correct on the default phyVERSO Board.
.. figure:: images/dc-basic-connection.png
:alt: Basic DC Connection
:width: 700px
The power meter has been replaced by a
:ref:`LEM driver <everest_modules_LemDCBM400600>` , which is a power
meter often used for public DC charging. It is connected via ethernet,
so no SerialCommHub is needed. Verify the correct target IP is set.
Then, we will need to add the additional hardware drivers required for
DC charging:
.. figure:: images/dc-additional-hardware-drivers.png
:alt: Additional DC Hardware Drivers
:width: 750px
For the DC power supply, a
:ref:`Huawei driver <everest_modules_Huawei_R100040Gx>` was added here
to the *power_supply_DC* requirement of EvseManager. Set the correct CAN
device. As isolation monitor, the
:ref:`Bender isoCHA driver <everest_modules_Bender_isoCHA425HV>` was added.
As it is a modbus device, it requires a SerialCommHub again - the same way
as the GenericPowermeter in the AC configuration. Make sure the settings
for the serial port are correct.
Both of the AC and DC example configs will not yet charge a car. They
still need two things from the other subsystems:
- Energy from the energy management system
- Authorization from the Auth subsystem. If you don't need any
Authorization, you can also disable this requirement by setting
“disable_authentication: true” for EvseManager.
Authentication subsystem
========================
Let's add a simple authentication subsystem to the charging part we just
created. Start by adding the :ref:`Auth module <everest_modules_Auth>` .
It is the central logic core of this subsystem and manages all incoming
tokens, validations and reservations. Connect it to the *evse/evse_manager*
implementation on EvseManager. Through this interface, it will authorize
the charging sessions. If you have multiple charging ports (multiple EvseManagers)
you can connect all of them to the same Auth module. The Auth module
will then manage multiple ports.
.. figure:: images/multiple-connections.png
:alt: Authentication Subsystem
:width: 750px
To do anything useful, the Auth module requires two things:
1. :doc:`Auth token providers </reference/interfaces/auth_token_provider>`:
They are sources of auth tokens, e.g. RFID readers etc that output tokens
(but do not know whether they are valid or not).
2. :doc:`Auth token validators </reference/interfaces/auth_token_validator>`:
They can tell whether a token is valid or not.
Move the *token_provider* requirement to the right. The first auth token
provider that we will connect is the *auth_token_provider*
implementation of the EvseManager. This is needed for features such as
“Plug and Charge” and “Autocharge”, where the EV is used as a token to
authenticate the charging session.
----
.. figure:: images/add-pnc-autocharge.png
:alt: Add Plug-and-Charge and Autocharge Feature
:width: 350px
----
Next, we can connect an RFID reader as a second auth token provider:
----
.. figure:: images/add-rfid-reader.png
:alt: Add RFID Reader
:width: 380px
--------------
Now, let's add a very simple token validator:
--------------
.. figure:: images/add-token-validator.png
:alt: Add Token Validator
:width: 500px
--------------
The :ref:`LocalAllowlistTokenValidator <everest_modules_LocalAllowlistTokenValidator>`
module takes a simple ASCII file (see config) with one line per token.
All tokens listed in this file are considered valid, all others are invalid.
With this Auth system, we already have a very simple version that can be
used to authenticate RFID tokens, that have been previously added to the
local whitelist file.
OCPP sub-system
==================
Especially for public charging stations, authentication is done via a
cloud backend instead of a simple local whitelist. For this, we use OCPP
1.6 in this example. OCPP 2.0.1 can be used in a similar way by using
the OCPP201 module instead.
OCPP is both a token provider and a token validator.
It provides tokens when a *RemoteStart* /
*RequestStartTransactionRequest* command is issued, and it is used as a
token validator if e.g. an RFID or Plug&Charge contract should be
validated in the CSMS. So we will connect both of those connections and
remove the LocalWhiteListTokenValidator. OCPP has its own internal local whitelist
and authorization cache implementation, that is according to the standard:
.. figure:: images/remove-whitelist.png
:alt: Remove Whitelist
:width: 650px
OCPP requires several connections. Let's go through them step by step:
- :doc:`Auth token providers </reference/interfaces/auth_token_provider>`
and :doc:`Auth token validators </reference/interfaces/auth_token_validator>`
are the main ones for remote start and token validation functionality.
Connect them to the Auth module.
- :doc:`auth interface </reference/interfaces/auth>` needs to be connected to
the *Auth* module. This connection is mostly used to set the connection
timeout setting via the OCPP protocol.
- :doc:`reservation interface </reference/interfaces/reservation>` is used to
reserve/cancel reservations of connectors via OCPP from the CSMS.
- OCPP also requires a connection to the
:ref:`EvseSecurity <everest_modules_EvseSecurity>` module, which
is now shared between OCPP and EvseV2G. OCPP requires it to load the
certificate / keys for TLS to the CSMS. OCPP can also update/install
certificates for both OCPP and ISO 15118 from the CSMS.
- OCPP requires a helper module for system-specific implementations
(OTA update, logfile collection and upload). Here, we use
:ref:`Linux_Systemd_Rauc <everest_modules_Linux_Systemd_Rauc>`
from EVerest, which is the default implementation using systemd for
log collection and RAUC for OTA updates. This in turn requires a
:ref:`PersistentStore <everest_modules_PersistentStore>` module,
which is shared here with the EvseManager.
For more detailed information about the OCPP configuration, check out the
following resources:
- :ref:`OCPP1.6 module documentation <everest_modules_OCPP>`
- :ref:`OCPP2.0.1 module documentation <everest_modules_OCPP201>`
- :doc:`OCPP1.6 tutorial </tutorials/ocpp16>`
- :doc:`OCPP2.0.1 tutorial </tutorials/ocpp2>`
Now, we have a configuration that can be used in public environments. It
supports authentication via OCPP for RFID tags, and - since the LEM
power supply is used on the DC port - German Eichrecht compliant
metering with OCMF-signed meter values forwarding to the cloud.
Energy management subsystem
====================================
The last subsystem missing is the Energy management. In EVerest, the
energy management distributes energy between charging ports. It is also
needed if only one charging port exists.
Please refer to the
:doc:`Energy Management documentation </explanation/energymanagement/index>`
for a detailed explanation of how to set up the energy management
subsystem.
Multiple connectors
===================
In order to support multiple charging ports, the charging subsystem will
need to be loaded multiple times (also duplicating all direct
dependencies), but Auth and Energy management subsystems are used only
once.
Here is an example for two charging ports (leaving out the dependencies
of each functional block / most connections for clarity):
.. figure:: images/multiple-connectors.png
:alt: Multiple Connectors
:width: 750px
Most drivers implement only a single instance. So e.g. drivers for
isolation monitors, SLAC, ISO protocol etc all need to be duplicated
when EvseManager is duplicated.
A few driver modules also have two implementations of one interface. A
good example is the PhyVersoBSP, which is the driver for a dual port
controller in one module - so it supplies two separate CP pins etc to
two EvseManagers (again removing most other modules for clarity):
.. figure:: images/phyverso-bsp.png
:alt: phyVERSO BSP
:width: 600px
----
**Authors**: Cornelius Claussen, Piet Gömpel

View File

@@ -0,0 +1,478 @@
.. _exp_detail_module_concept:
#########################
EVerest Modules in Detail
#########################
This section gives you a bunch of theoretical input about the EVerest module
concept.
Other ways to approach the concepts of EVerest module development are:
1. For a very first glance and understanding of EVerest modules, try to check
the :ref:`Understanding EVerest Modules section <htg_getting_started_sw_understand_modules>` in the Quick
Start Guide.
2. A more hands-on intro to EVerest module development:
:doc:`Develop New EVerest Modules </tutorials/develop-new-module>`.
********
Overview
********
EVerest follows a microservice-like architecture.
A typical EVerest deployment consists of the following components:
* Several **module instances**, which are separate processes offering some
type of functionality;
* A **MQTT broker** (mosquitto), which provides the backbone of communication
between module instances;
* A **manager process**, which orchestrates the execution of module instances.
.. image:: images/everest-manager-modules-mqtt.png
:width: 360px
:align: center
.. note::
EVerest provides integration for modules written in C++, Javascript, Python
or Rust.
We will use notation for C++ below.
********
Concepts
********
Modules and module instances
============================
A **module** is a program providing a specific functionality within EVerest,
e.g. driving a particular type of hardware.
Each module has a unique **name**, assigned at development time.
Modules can accept **config** values, which can be set when executing the
module.
In an EVerest deployment, you launch **instances** of these modules, each
instance is a separate process.
There can be multiple instances of the same module, e.g. for driving multiple
devices of the same type.
Each module instance has a unique **instance ID**, independent of the module
name, which is assigned when configuring the deployment.
Different instances of the same module can also have different config values
set when configuring the deployment.
.. image:: images/everest-modules-and-instances.png
:width: 600px
:align: center
Interfaces: Communication between modules
=========================================
Think of interfaces as specific protocols (or languages) that modules can use
to communicate with each other.
An interface is a set of:
* **Commands**: Synchronous (remote) procedure calls with defined arguments
and return values - in short **CMDs**;
* **Variables**: Useful for asynchronous communication, a variable is a topic
(typically some particular value that changes over time) which users of the
interface can subscribe to, and which the module publishes updates on.
In short **VARs**.
For example, imagine a simple interface offered by a power supply module.
Turning power on or off could be implemented as CMDs, callable by other
modules.
The voltage and current values at the power supply could be implemented as a VAR -
the power supply module publishes this VAR regularly,
and other modules could subscribe to the VAR and observe its value over time.
Providing and requiring interfaces
==================================
Each module defines a set of interface implementations it **provides** to
other modules, and a set of interface implementations it **requires** from
other modules.
Providing interface implementations
-----------------------------------
A module is not simply declared as an implementor of an interface.
Instead, modules have a set of **interface implementations**,
each implementing one interface and having a unique **implementation ID**.
This is done because a module can implement the same interface multiple times.
This is reflected by providing multiple implementations with different IDs for
the same interface.
As an analogy, think of an internet router.
It does not just implement the IP protocol, it has multiple implementations
of it (several Ethernet ports, WLAN antennas, etc.), which can be connected to
different devices, and may even have different purposes, e.g. LAN and WAN
ports.
Requiring interface implementations
-----------------------------------
Just like a module can provide the same interface multiple times, it can also
require multiple implementations of the same interface.
For example, there could be one energy manager component, which communicates
with multiple power supply or EVSE manager modules.
Therefore, modules have a set of **interface requirements** with unique
IDs, each of which is for a particular interface and must be satisfied by
an interface implementation of another module.
Continuing with the router analogy from before, a PC can have
multiple network interfaces - e.g. one WLAN and one Ethernet -
which could easily be connected to different ports on different routers.
The following diagram shows how providing and requiring interface
implementation create relations between modules:
.. image:: images/everest-interfaces-provides-requires.png
:width: 420px
:align: center
Interface communication on the MQTT layer
=========================================
On the MQTT level, interfaces are implemented as follows:
* To listen for incoming CMDs or subscribe to a VAR, a module
subscribes to the corresponding MQTT topic;
* To send a CMD or update to a VAR, a module publishes a message on the
corresponding topic.
The MQTT topic for commands is:
``everest/{module instance ID}/{interface implementation ID}/cmd``
Similarly, the MQTT topic for variables is:
``everest/{module instance ID}/{interface implementation ID}/var``
Note that the path prefix ``everest`` may differ in some end-to-end tests.
Wiring it all together: The run configuration
=============================================
The **run configuration** is a YAML file which specifies the structure of your
deployment.
The run configuration defines the module instances to start:
* Their instance IDs;
* Which modules they are an instance of;
* What to set their configuration values to;
* For each interface requirement of the module instance:
* The instance ID of the module instance which provides the interface;
* The interface implementation ID within the providing module which will be used.
.. _exp-yaml-files:
*************************
Explaining the YAML files
*************************
Now, we will show how the concepts above map to the YAML files
defining modules and interfaces.
Consider the following example: We want two modules, a "ping server" and a
"ping client", to communicate over a "ping interface".
Let us define the ``interfaces/interface_ping.yaml`` first:
.. code-block:: yaml
description: Interface for a ping-pong interaction
cmds: # list of commands in the interface
command_ping: # name of the command
description: Send a ping with a payload to the ping server.
arguments: # list of arguments
payload:
description: An arbitrary string that the server will pong back.
type: string
result: # return value of the command
description: The same payload as the ping
type: string
vars: # list of variables in the interface
var_nping: # name of the variable
description: The number of pings the server has received so far
type: integer
Now, let us define a "ping server" module, which has an implementation of this
interface.
Here is the ``modules/PingServerModule/manifest.yaml`` file:
.. code-block:: yaml
description: Example ping-pong module
config: # list of config values
cfg_publish_number_of_pings: # name of the config value
description: Publish the number_of_pings variable every 5 seconds.
type: boolean
default: false
provides: # list of interface implementations
if_impl_id_ping: # implementation ID
interface: interface_ping # interface name
description: Responds to a ping with a pong
enable_external_mqtt: true # enable this if you want to use the MQTT layer directly in your code
metadata:
license: link-to-your-license.here
authors:
- Max Mustermann, Company Name Here
We can have a "ping client" module, which requires the ``ping_interface``.
Here is the ``modules/PingClientModule/manifest.yaml`` file:
.. code-block:: yaml
description: Example ping-pong client module
requires: # list of interface requirements
requirement_ping_server: # requirement ID
interface: interface_ping # interface name
enable_external_mqtt: true # enable this if you want to use the MQTT layer directly in your code
metadata:
license: link-to-your-license.here
authors:
- Max Mustermann, Company Name Here
And finally, we define a run configuration, where instances of the two modules
connect to each other:
.. code-block:: yaml
settings:
telemetry_enabled: true
active_modules: # list of module instances
instance_id_ping_server: # instance ID
config_module: # list of config parameters
cfg_publish_number_of_pings: true
module: PingServerModule # module which this is an instance of
instance_id_ping_client: # next instance ID
connections: # list of providers for interface requirements
requirement_ping_server: # requirement ID
- implementation_id: if_impl_id_ping # implementation ID
module_id: instance_id_ping_server # module instance ID of the provider
module: PingClientModule # module which this is an instance of
Graphically, this would look as follows:
.. image:: images/everest-runtime-config.png
:width: 480px
:align: center
For a tutorial where you implement and experiment a similar example,
refer to :doc:`Develop New EVerest Modules </tutorials/develop-new-module>`
********************************
Explaining the generated sources
********************************
When starting a project, you will typically use ``ev-cli`` to generate a
source code skeleton.
Here, we will explain the purpose and structure of the files
created by this code generation step.
Interface headers
=================
Using ``ev-cli generate-headers`` for the ``interface_ping`` from above,
three header files are generated::
.
└── build
└── generated
└── include
└── generated
└── interfaces
└── interface_ping
├── Implementation.hpp
├── Interface.hpp
└── Types.hpp
We will not list the contents of these files completely,
but we will explain the contents of the files generally.
``Interface.hpp`` contains a class called ``interface_pingIntf``
(in general, ``${INTERFACE_NAME}Intf``),
which is used when *requiring* the interface.
It contains the following functions:
* ``call_command_ping`` (in general ``call_${COMMAND_NAME}``), to call the
respective command;
* ``subscribe_var_nping`` (in general ``subscribe_${VAR_NAME}``) to register a
callback each time an update to the variable is published.
``${INTERFACE_NAME}Intf`` is essentially a proxy which routes command calls
and variable subscriptions to the EVerest framework.
``Implementation.hpp`` contains an abstract class called
``interface_pingImplBase``
(in general, ``${INTERFACE_NAME}ImplBase``), which is used
when *providing* the interface.
It contains the following functions:
* ``publish_var_nping`` (in general ``publish_${VAR_NAME}``), to publish an
update to the variable;
* ``handle_command_ping`` (in general ``handle_${COMMAND_NAME}``), which is
virtual - this function is called to handle the respective command.
Interface implementations extend ``${INTERFACE_NAME}ImplBase``, and must
implement all command handlers (``handle_${COMMAND_NAME}``).
The EVerest framework takes care of publishing variable updates,
listening for commands, calling the appropriate handler, and sending back its
return value to the caller.
The ``Types.hpp`` file contains custom type definitions.
Module files
============
Using ``ev-cli module create`` for the two modules from above generates
the following new files (we omit the ``manifest.yaml here``)::
.
└── modules
├── PingServerModule
│ ├── CMakeLists.txt
│ ├── PingServerModule.cpp
│ ├── PingServerModule.hpp
│ ├── doc.rst
│ ├── docs
│ │ └── index.rst
│ └── if_impl_id_ping
│ ├── interface_pingImpl.cpp
│ └── interface_pingImpl.hpp
└── PingClientModule
├── CMakeLists.txt
├── PingClientModule.cpp
├── PingClientModule.hpp
├── doc.rst
└── docs
└── index.rst
Focusing on the source and header files, generally, the tool generates:
* One source-header pair describing a class for the whole module:
``${MODULE_NAME}.{cpp, hpp}``;
* One source-header pair describing a class for each interface implementation
in the module: ``${IMPLEMENTATION_ID}/${INTERFACE_NAME}Impl.{cpp, hpp}``
The module class
----------------
The module class, which carries the same name as the module itself, is defined
in ``${MODULE_NAME}.hpp``.
Apart from a constructor (called by the EVerest framework on startup), it has
a few notable members:
* ``config`` of type ``Conf`` (defined in the same file): Config values for
the module;
* ``mqtt``: handle for MQTT communication, if ``enable_external_mqtt`` was
enabled in the manifest;
* ``init()``: Function called by the framework after initializing this module
- you may add code to it to add more initialization steps;
* ``ready()``: Function called by the framework when the deployment is ready
- you may initiate application logic in it;
* For each interface implementation:
``std::unique_ptr<${INTERFACE_ID}ImplBase> p_${IMPLEMENTATION_ID}``
- reference to the interface implementation;
* For each interface requirement:
``std::unique_ptr<${INTERFACE_ID}Intf> r_${REQUIREMENT_ID}`` - use this to
trigger commands or subscribe to variables on the provider.
The header file contains designated areas where further members or other
definitions can be added.
Code added to these areas will be preserved if the headers are
overwritten by the ``ev-cli module update`` command (e.g. if you updated
the module manifest).
``${MODULE_NAME}.cpp`` initially only contains stub implementations of the
``init()`` and ``ready()`` functions, which just call the ``init()`` and
``ready()`` functions in each interface implementation:
.. code-block:: c++
void PingServerModule::init() {
invoke_init(*p_if_impl_id_ping);
}
void PingServerModule::ready() {
invoke_ready(*p_if_impl_id_ping);
}
Further logic can be freely added to this file - ``${MODULE_NAME}.cpp`` is
not overwritten by ``ev-cli module update``, unless the ``--force`` option
is specified.
Definitions related to the module class are placed in the ``module`` namespace.
Interface implementations
-------------------------
For each interface implementation, a class is defined in
``${IMPLEMENTATION_ID}/${INTERFACE_NAME}Impl.hpp``.
This class extends ``${INTERFACE_NAME}ImplBase``, declaring overriding methods
for all command handlers, as well as a few additional notable members:
* ``config`` of type ``Conf`` (defined in the same file): Config values of
the implementation
* ``mod``: reference to the module instance (e.g. to call methods of the
module class);
* ``init()`` and ``ready()``, which have the same semantics as the module
class's ``init()`` and ``ready()``.
Like the module class, interface implementation classes are also
constructed by the framework at startup.
``${IMPLEMENTATION_ID}/${INTERFACE_NAME}Impl.cpp`` initially contains stub
implementations of the
``init()`` and ``ready()`` functions - recall from the previous subsection
that these are called by the module class's ``init()`` and ``ready()``
functions, therefore they are called at (roughly) the same point.
It also contains stubs for the command handlers, which return dummy values -
this way, the code generated by ``ev-cli`` can be built and ran
even if you have not yet written any code.
As was the case for the module class's files, you may freely extend the
``${IMPLEMENTATION_ID}/${INTERFACE_NAME}Impl.cpp`` file as it will not be
overwritten by subsequent ``ev-cli module update`` commands - however,
``${IMPLEMENTATION_ID}/${INTERFACE_NAME}Impl.hpp`` does get overwritten, so
you should only add your changes to the designated areas in that file.
Definitions related to the interface implementation are in the
``module.${IMPLEMENTATION_ID}`` namespace.
Note on concurrency
-------------------
Parts of the module logic may run in parallel. By itself, the EVerest
framework starts:
* One thread to execute the ``ready()`` function of the module class, which
you can freely use to start logic of your own (e.g. an endless loop, or
spawning worker threads);
* A thread pool to handle commands (these will call the command handlers in
interface implementations);
* A thread pool to watch for variable updates the module has subscribed to
(these will call the callbacks you provide to ``subscribe_${VAR_NAME}``)
In general, assume functions called by the framework may be running in
parallel. If data structures need to be shared between such functions
(especially for writing), you should use some form of locking.
------------------------------------------------
Authors: Valentin Dimov, Manuel Ziegler, Piet Gömpel

View File

@@ -0,0 +1,487 @@
.. _exp_dev_tools_edm:
###
edm
###
edm stands for EVerest dependency manager. It helps you orchestrating the
dependencies between the different EVerest repositories.
.. note::
The EDM tool was developed at a time when the EVerest source code was
organized in many different repositories. Since 2026, EVerest has essentially
changed to a (quasi-)mono-repository layout. Manual installation of EDM is
usually not necessary to work with recent versions of EVerest.
Dependency Manager for EVerest
##############################
Install and Quick Start
***********************
To install the **edm** dependency manager for EVerest you have to perform the
following steps.
Please make sure you are running a sufficiently recent version of **Python3 (>=3.6)** and that you are able to install Python packages from source.
See the *python3* command below for upgrading the required packages. Refer to
the
`Python Installing Packages <https://packaging.python.org/tutorials/installing-packages/#requirements-for-installing-packages>`_
documentation for indepth guidance if any problems arise. You may want to create and activate a virtual environment
using `venv <https://docs.python.org/3/library/venv.html>`_
.. code-block:: bash
python3 -m venv venv
source venv/bin/activate
before executing the commands below.
.. code-block:: bash
python3 -m pip install --upgrade pip setuptools wheel jstyleson jsonschema
Python packages needed to run edm
*********************************
The following Python3 packages are needed to run **edm**. If you install edm
using this guide they will be installed automatically.
+ Python >= 3.6
+ Jinja2 >= 3.0
+ PyYAML >= 5.4
Installing edm
**************
Now you can clone this repository and install **edm**:
.. code-block:: bash
git clone https://github.com/EVerest/EVerest.git
cd EVerest/applications/dependency_manager
python3 -m pip install . --break-system-packages
or in short
.. code-block:: bash
python3 -m pip install git+https://github.com/EVerest/EVerest.git@main#subdirectory=applications/dependency_manager --break-system-packages
.. note::
Alternatively, you can also install ``edm`` in a python virtual environment.
Make sure edm is available in your PATH after the installation. You can verify
this by running ``edm --version``.
Next you could run
.. code-block:: bash
edm init --workspace ~/checkout/everest-workspace
This creates a workspace in the ``~/checkout/everest-workspace``
directory from the most recent release of EVerest. If you want the most recent
main you can use:
.. code-block:: bash
edm init main --workspace ~/checkout/everest-workspace
The workspace will have the following structure containing all current
dependencies for EVerest:
.. code-block:: bash
everest-workspace/
├── everest-cmake
├── EVerest
├── everest-dev-environment
├── everest-framework
├── everest-sqlite
├── everest-utils
├── Josev
├── libcbv2g
├── libevse-security
├── libfsm
├── libiso15118
├── liblog
├── libnfc-nci
├── libocpp
├── libslac
├── libtimer
└── workspace-config.yaml
The ``workspace-config.yaml`` contains a copy of the config that was used to create
this workspace.
Enabling CPM_SOURCE_CACHE and setting PATH
******************************************
The EVerest dependency manager uses
`CPM <https://github.com/cpm-cmake/CPM.cmake>`_
for its CMake integration. This means you *can* and **should** set the
``CPM_SOURCE_CACHE`` environment variable. This makes sure that dependencies
that you do not manage in the workspace are not re-downloaded multiple times.
For detailed information and other useful environment variables please
refer to the `CPM Documentation <https://github.com/cpm-cmake/CPM.cmake/blob/master/README.md#CPM_SOURCE_CACHE>`_.
Also set the PATH variable:
.. code-block:: bash
export CPM_SOURCE_CACHE=$HOME/.cache/CPM
export PATH=$PATH:/home/$(whoami)/.local/bin
Building EVerest
****************
Make sure you have installed :doc:`ev-cli <ev-cli>` first.
You can now use the following commands to build the repository EVerest:
.. code-block:: bash
cd ~/checkout/everest-workspace/EVerest
mkdir build
cd build
cmake ..
make -j$(nproc) install
.. _cmake_integration_setup:
Setting up and updating a workspace
###################################
For letting **edm** do the work of setting up an initial EVerest workspace,
do this:
.. code-block:: bash
edm init --workspace ~/checkout/everest-workspace
If you are currently in the everest-workspace directory the following command
has the same effect:
.. code-block:: bash
edm init
For using a dedicated release version, you can do this:
.. code-block:: bash
edm init 2023.7.0
In this example, version 2023.7.0 is pulled from the server. This will only work
if your previous code is not in a "dirty" state.
Using the edm CMake module and dependencies.yaml
################################################
To use edm from CMake you have to add the following line to the top-level
CMakeLists.txt file in the respective source repository:
.. code-block:: bash
find_package(EDM REQUIRED)
To define dependencies you can now add a dependencies.yaml file to your source
repository. It should look like this:
.. code-block:: bash
---
sigslot:
git: https://github.com/palacaze/sigslot
git_tag: v1.2.3
cmake_condition: "EVEREST_DEPENDENCY_ENABLED_SIGSLOT"
options:
- "SIGSLOT_COMPILE_EXAMPLES OFF"
- "SIGSLOT_COMPILE_TESTS OFF"
pugixml:
git: https://github.com/zeux/pugixml
git_tag: v1.15
cmake_condition: "EVEREST_DEPENDENCY_ENABLED_PUGIXML"
If you want to conditionally include some dependencies, e.g. for testing, you can
do this in the following way:
.. code-block:: bash
catch2:
git: https://github.com/catchorg/Catch2.git
git_tag: v3.4.0
cmake_condition: "BUILD_TESTING"
Here *cmake_condition* can be any string that CMake can use in an if() block.
Please be aware that any variables you use here must be defined before a call to
*evc_setup_edm()* is made in your ``CMakeLists.txt``
Additionally you can set the ``EVEREST_MODIFY_DEPENDENCIES`` environment variable
to a file containing modifications to the projects ``dependencies.yaml`` files when
running cmake:
.. code-block:: bash
EVEREST_MODIFY_DEPENDENCIES=../dependencies_modified.yaml cmake -S . -B build
The ``dependencies_modified.yaml`` file can contain something along these lines:
.. code-block:: bash
nlohmann_json:
git: null # this makes edm look for nlohmann_json via find_package
libfmt:
rename: fmt # if find_package needs a different dependency name you can rename it
git: null
catch2:
git_tag: v1.2.3 # select a different git tag for a build
Selective library consumption
#############################
If your external project only needs specific everest-core libraries (e.g.
``liblog``, ``everest-util``, ``everest-io``, ``libocpp``, ``libiso15118``)
without building the full module framework, you can use the
``EVEREST_LIBS_ONLY`` and ``EVEREST_INCLUDE_LIBS`` CMake options.
**CMake options:**
.. list-table::
:header-rows: 1
* - Option
- Default
- Description
* - ``EVEREST_LIBS_ONLY``
- OFF
- Skip modules, applications, config, and code generation. Only build
libraries under ``lib/everest/``.
* - ``EVEREST_INCLUDE_LIBS``
- (empty)
- Semicolon-separated allowlist of libraries to build. Transitive
internal dependencies are resolved automatically. When empty, all
libraries are built.
* - ``EVEREST_EXCLUDE_LIBS``
- (empty)
- Semicolon-separated blocklist of libraries to skip.
**Example: building only liblog, everest-util, and everest-io**
.. code-block:: bash
cmake -S . -B build \
-DEVEREST_LIBS_ONLY=ON \
-DEVEREST_INCLUDE_LIBS="log;util;io"
cmake --build build
Transitive dependencies are resolved automatically. For example, requesting
``io`` will automatically include ``util`` (since ``everest-io`` depends on
``everest-util``).
**Example: building only libocpp**
.. code-block:: bash
cmake -S . -B build \
-DEVEREST_LIBS_ONLY=ON \
-DEVEREST_INCLUDE_LIBS="ocpp"
cmake --build build
This resolves the full dependency chain: ``ocpp`` -> ``log``, ``timer``,
``evse_security``, ``sqlite``, ``cbv2g``.
**Using from an external project's dependencies.yaml:**
.. code-block:: yaml
everest-core:
git: https://github.com/EVerest/everest-core.git
git_tag: 2026.02.0
options:
- "EVEREST_LIBS_ONLY ON"
- "EVEREST_INCLUDE_LIBS log;util;io"
The internal dependency map is defined in ``cmake/ev-lib-dependencies.cmake``.
.. note::
Libraries that depend on framework code generation (``tls``, ``helpers``,
``conversions``, ``slac``, ``external_energy_limits``, ``everest_api_types``)
are **not available** in ``EVEREST_LIBS_ONLY`` mode. Use
``EVEREST_EXCLUDE_MODULES`` instead if you need those libraries.
Framework thread pool scaling policy
####################################
The framework message handler uses a dynamically scaling thread pool for
operation messages such as variable updates, commands, errors, GetConfig and
ModuleReady messages. Its scaling policy can be selected at CMake configure
time.
**CMake options:**
.. list-table::
:header-rows: 1
* - Option
- Default
- Description
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY``
- ``latency``
- Selects the policy. Supported values are ``latency``, ``greedy``,
``conservative``, ``fixed_size`` and ``custom``.
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS``
- ``50``
- Maximum queued task wait time, in milliseconds, before the ``latency``
policy adds another worker.
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_TICK_MS``
- ``5``
- Supervisor tick, in milliseconds, for the ``latency`` policy.
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD``
- ``3``
- Queue size threshold at which the ``fixed_size`` policy adds another
worker.
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_HEADER``
- (empty)
- Header to include when the policy is ``custom``.
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_TYPE``
- (empty)
- Fully-qualified C++ policy type to use when the policy is ``custom``.
* - ``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_INCLUDE_DIR``
- (empty)
- Additional include directory for the custom policy header.
The built-in policies are:
.. list-table::
:header-rows: 1
* - Policy
- Behavior
* - ``latency``
- Default. Adds workers when queued work has waited longer than the
framework latency threshold.
* - ``greedy``
- Adds workers as soon as backlog is detected.
* - ``conservative``
- Adds workers only when the queue depth significantly exceeds the current
worker count.
* - ``fixed_size``
- Adds workers once the queue size reaches the configured
``EVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD``.
**Example: selecting a built-in policy for a full EVerest build**
.. code-block:: bash
cmake -S . -B build \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY=latency \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_THRESHOLD_MS=50 \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_LATENCY_TICK_MS=5
cmake --build build
**Example: selecting fixed-size scaling**
.. code-block:: bash
cmake -S . -B build \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY=fixed_size \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_FIXED_SIZE_THRESHOLD=3
cmake --build build
**Example: using a custom policy**
.. code-block:: bash
cmake -S . -B build \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY=custom \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_HEADER=my_policy.hpp \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_TYPE=my_project::MyPolicy \
-DEVEREST_FRAMEWORK_THREAD_POOL_SCALING_POLICY_CUSTOM_INCLUDE_DIR=/path/to/include
cmake --build build
A custom policy must provide the same interface as the built-in policies:
.. code-block:: cpp
struct MyPolicy {
static constexpr std::optional<std::chrono::milliseconds> supervisor_tick =
std::nullopt;
static bool should_grow(
std::size_t current_workers,
std::size_t queue_size,
std::optional<std::chrono::steady_clock::time_point> oldest_arrival);
};
Create a workspace config from an existing directory tree
#########################################################
Suppose you already have a directory tree that you want to save into a config
file. You can do this with the following command:
.. code-block:: bash
edm --create-config custom-config.yaml
This is a short form of:
.. code-block:: bash
edm --create-config custom-config.yaml --include-remotes https://github.com/EVerest/*
and only includes repositories from the EVerest namespace. You can add as many
remotes to this list as you want.
For example, if you only want to include certain repositories you can use the
following command.
.. code-block:: bash
edm --create-config custom-config.yaml --include-remotes https://github.com/EVerest/everest* https://github.com/EVerest/ext-switchev-iso15118.git
If you want to include all repositories, including external dependencies, in
the config you can use the following command:
.. code-block:: bash
edm --create-config custom-config.yaml --external-in-config
.. _git_information_at_a_glance:
Git information at a glance
###########################
You can get a list of all git repositories in the current directory and their
state using the following command:
.. code-block:: bash
edm --git-info --git-fetch
If you want to know the state of all repositories in a workspace you can use
the following command:
.. code-block:: bash
edm --workspace ~/checkout/everest-workspace --git-info --git-fetch
This creates output that is similar to the following example:
.. code-block:: bash
[edm]: Git info for "~/checkout/everest-workspace":
[edm]: Using git-fetch to update remote information. This might take a few seconds.
[edm]: "everest-dev-environment" @ branch: main [remote: origin/main] [behind 6] [clean]
[edm]: "everest-framework" @ branch: main [remote: origin/main] [dirty]
[edm]: "everest-deploy-devkit" @ branch: main [remote: origin/main] [clean]
[edm]: "libtimer" @ branch: main [remote: origin/main] [dirty]
[edm]: 2/4 repositories are dirty.
Further information can be seen as shell output by calling edm with parameter
**-h** or **--help**.
----
**Authors**: Kai-Uwe Hermann, Stefan Wahren, Andreas Heinrich, Manuel Ziegler

View File

@@ -0,0 +1,229 @@
.. _exp_dev_tools_evcli:
######
ev-cli
######
``ev_cli`` has mainly two purposes:
- Generate C++ header files for defined interfaces
- Create/update auto generated files for modules (C++ only).
Generating the header files is done in the build process of ``EVerest``. For this
you don't need to install ``ev-dev-tools`` by yourself, it happens automatically during the build process.
For creating and updating auto generated files for modules you need to install ``ev-dev-tools`` to use it during development.
.. _evcli_install:
*******
Install
*******
There are two possibilites to use/install ``ev-dev-tools``.
You can use the automatically installed version from python venv in build directory or
you can install the python package manually.
Use automatically installed `ev-dev-tools` from python venv
===========================================================
Build ``EVerest`` as explained in the :ref:`Quick Start Guide <htg_getting_started_sw>`.
This will create a python venv in your build directory.
You can activate it with:
.. code-block:: bash
cd build/
source ./venv/bin/activate
Install `ev-dev-tools` manually
===============================
To install ``ev_cli`` manually from github repository:
.. code-block:: bash
python3 -m pip install git+https://github.com/everest/EVerest.git@main#subdirectory=applications/utils/ev-dev-tools
*****************************
ev-cli command line interface
*****************************
The ``ev_cli`` package comes with a command line tool, named ``ev-cli``.
It has the following subcommands
- ``module``:
auto generation and update of EVerest modules from its interface and
manifest definitions
- ``interface``:
auto generation of C++ header files for defined interfaces
- ``helpers``:
utility commands
- ``types``:
auto generation of C++ header files for types
To see a list of all subcommands and options, simply call:
.. code-block:: bash
ev-cli --help
The `module`, `interface` and `types` commands have the following options in
common:
- ``--work-dir``:
work directory which also contains the manifest definitions (default: ``.``)
- ``--everest-dir``:
root directory of EVerest core or any directory containing interface
and module definitions (default: ``.``)
- ``--schemas-dir``:
schemas directory of the EVerest framework, containing the schema
definitions (default: ``../everest-framework/schemas``)
- ``--clang-format-file``:
if C++ output should be formatted, set this to the path of the
``.clang-format`` file
Generating C++ header files for defined interfaces
==================================================
Assuming that the interface definitions in yaml format are located at
``./interfaces/*.yaml``, simply::
ev-cli interface generate-headers
This will generate the c++ header files for all interfaces and output them
to ``./generated/include/generated``. To generate only a single interface, call::
ev-cli interface generate-headers InterfaceName
For each interface an ``Implementation.hpp`` and ``Interface.hpp``
header file will be generated. The former represents the `implementers`
view, and the latter the `users` view of the interface, when used in a
module.
Creating and updating auto generated files for modules (C++ only)
=================================================================
Assuming the modules are located at ``./modules`` and the initial
skeleton for a module named `Example` with its manifest in
``./modules/Example/manifest.yaml`` should be created, call::
ev-cli module create Example
This will create the following files inside the ``./modules/Example``
subdirectory
- ``CMakeLists.txt``:
build instruction file for CMake
- ``ld-ev.hpp``/``ld-ev.cpp``:
glue code files for this module to get hooked up by the EVerest
framework
- ``Example.hpp``/``Example.cpp``:
header and source file for the module
Furthermore, for each interface provided by the module a subdirectory
with the name of the `interface id` will be created. If, for example,
the manifest looks like:
.. code-block:: yaml
description: Example module
provides:
main:
description: SampleInterface implementation
interface: SampleInterface
# ...
# ...
a subdirectory named ``main`` will be created, including two files
``SampleInterfaceImpl.hpp`` and ``SampleInterfaceImpl.cpp``. The header
file declares the implementation of `SampleInterface`, which derives
from the auto generated interface header files from the previous
subsection.
Now it is up to the user to implement logic in the module and interface
implementation `cpp` source files.
If the modules' ``manifest.yaml`` or interface definitions, used by the
module, change, you can update the generated files by using::
ev-cli module update Example
**Note**:
1.
``cpp`` source files will never be changed or overwritten by the
`update` subcommand. The `create` subcommand only resets / overrides
the files when using the ``--force`` option
2.
``hpp`` header files and the ``CMakeLists.txt`` file will get
updated, if its interface dependencies definitions change and the
`update` subcommand is used. You can force an update by using the
``--force`` option. During an update, the sections marked like::
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
.....
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
will be kept. If you want to completely reset / override these
files, you need to recreate the using `create` subcommand with the
``--force`` option.
3.
Generated files will never be deleted. So make sure, you do this if
you, for example, change the interface ids or remove interfaces from
the module
These additional options might be useful for the `create` and `update`
subcommands:
1. ``--force``:
force creation or update
2. ``--diff``:
don't touch anything, only show a `diff` of what would be changed
3. ``--only``:
this option takes a comma separated list of files, that should be
touched only. This is especially helpful, if you want to recreate
only a single interface implementation ``cpp`` file, because you
changed the corresponding interface a lot. To get a list of possible files, you can simply call::
ev-cli module create Example --only which
this would output for the above mentioned example::
Available files for category "core"
cmakelists
ld-ev.hpp
ld-ev.cpp
module.hpp
module.cpp
Available files for category "interfaces"
main.hpp
main.cpp
So calling::
ev-cli module create Example --only main.cpp,cmakelists --force
would recreate the ``CMakeLists.txt`` and the
``main/SampleInterfaceImpl.cpp`` files, whereas::
ev-cli module update Example --only module.hpp
would update only the module header file ``Example.hpp``.
----
**Authors**: Kai-Uwe Hermann, Andreas Heinrich, Manuel Ziegler, Christoph Burandt

View File

@@ -0,0 +1,118 @@
##############################################
Internals of the EVerest Development Container
##############################################
This document explains the internal working of the EVerest
development container (devcontainer) for different setup variants
and how things are connected.
For a more hands-on explanation, consider reading:
- :doc:`Tutorial: Setup the EVerest Development Container </tutorials/setup-devcontainer/index>`
- :doc:`How-to Guide: How to use a development container for EVerest development and sil testing </how-to-guides/devcontainer-usage/index>`
*******************************
The Docker Compose Project Name
*******************************
The Docker Compose project name determines how containers are
named and grouped. By default, it uses the
**current folder name with _devcontainer suffix**
(consistent with VSC behavior), but can be customized.
Set the environment variable ``DOCKER_COMPOSE_PROJECT_NAME``
to your desired project name before building/starting the devcontainer.
For example:
.. code-block:: bash
DOCKER_COMPOSE_PROJECT_NAME=my-project ./applications/devrd/devrd Start
This will name the containers with the pattern:
``{project_name}-{service}-1``, where
- ``{project_name}`` is the value of ``DOCKER_COMPOSE_PROJECT_NAME``
- ``{service}`` is the service name defined in the docker-compose files
- ``1`` is the instance number (default is 1)
*********************
Environment Variables
*********************
You can generate an ``.env`` file with auto-detected values using this command:
.. code-block:: bash
./applications/devrd/devrd env
This will create a ``.devcontainer/.env`` file with the following content:
.. code-block:: bash
# Auto-generated by setup script
ORGANIZATION_ARG=EVerest
REPOSITORY_HOST=github.com
REPOSITORY_USER=git
COMMIT_HASH=<..>
EVEREST_TOOL_BRANCH=main
UID=<..>
GID=<..>
HOST_WORKSPACE_FOLDER=<..>
These variables are automatically mapped in the container to the following environment variables:
- ``ORGANIZATION_ARG``: Maps to ``EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION``
- ``REPOSITORY_HOST``: Maps to ``EVEREST_DEV_TOOL_DEFAULT_GIT_HOST``
- ``REPOSITORY_USER``: Maps to ``EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER``
- ``EVEREST_TOOL_BRANCH``: Maps to ``EVEREST_TOOL_BRANCH``
- ``HOST_WORKSPACE_FOLDER``: The directory mapped to ``/workspace`` inside the container
Use this mechanism if you have a different organization or git host or user.
This is useful if you have forked and you are hosting your development outside github.
************************
Workspace Folder Mapping
************************
The ``/workspace`` directory inside the container can be mapped
to any folder on your host system. The workspace folder is
determined in the following priority order:
1. **Command line option**: ``-w`` or ``--workspace`` flag
2. **Environment variable**: ``HOST_WORKSPACE_FOLDER`` environment variable
3. **`.env` file**: ``HOST_WORKSPACE_FOLDER`` value in ``.devcontainer/.env``
4. **Current directory**: Falls back to the current working directory
The ``.devcontainer`` directory (containing ``.env`` and Docker Compose files)
is always located relative to where the ``devrd`` script is installed.
This allows you to:
- Run ``devrd`` from any directory in your workspace
- Use a single ``devrd`` installation to manage multiple workspaces by changing ``HOST_WORKSPACE_FOLDER`` in the ``.env`` file
One can set workspace mapping via command line:
.. code-block:: bash
./applications/devrd/devrd env -w /path/to/workspace
Or edit ``.devcontainer/.env`` directly and set ``HOST_WORKSPACE_FOLDER``
Then run from anywhere:
.. code-block:: bash
cd /path/to/workspace/subfolder
../applications/devrd/devrd start # Works correctly, uses workspace from .env file
***************
Troubleshooting
***************
See the :doc:`separate troubleshooting section </tutorials/setup-devcontainer/troubleshooting>` for help
on devcontainer-specific issues.
----
**Authors:** Florian Mihut, Andreas Heinrich

View File

@@ -0,0 +1,122 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="361px" height="351px" viewBox="-0.5 -0.5 361 351" content="&lt;mxfile&gt;&lt;diagram id=&quot;LkL3k23o4wMPku4sNzY7&quot; name=&quot;Page-1&quot;&gt;5ZfBcpswEIafhjsgm9jH2CXpITl5pj2rsEVqBcvIwth9+opoZaySNJnpxM64F4/07Qppf/3egYit6/295q14xBJUlMblPmKfojRdJjP7O4CDAxmbO1BpWTqUjGAjfwHBmGgnS9gGiQZRGdmGsMCmgcIEjGuNfZj2HVW4a8srmIBNwdWUfpWlEY4u5vHIP4OshN85iSlSc59MYCt4if0JYnnE1hrRuFG9X4MatPO6uHV3L0SPB9PQmLcsSN2CHVcd1UbnMgdfLJS2dpqiNgIrbLjKR7rS2DUlDE+M7WzMeUBsLUws/AHGHOgieWfQImFqRVFoytvhWuy0wQYcuZNK0SOnVVGhW+x0QeckTxmuK6CszKGhgpNlpMQ9YA1GH2yCBsWN3IW3y8kk1TFv1NEOSMrnZWVXLeviUrLOnpE1U/ZUq292UA2DtdRFJ43NWmngP0EfM7RP8cTudlyWsdsotfvHrBWTqwovohfSwKblTwL1tr+For8o6g60gf1fBaMom1Oh1B5n1Cz6sdckvoGIkz6Txf8u8fx6nJtNnbu8lHOzd3MuSz+Qc1MWOjdZntG6N9dj3cXUuv7d5/zeXfwf3p3dXNC7y9c1zr9s8uRtuibZB9L1z57A2Bl19f+a14RNr8Cw7ymsnY4fJU+xky87lv8G&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<path d="M 180 60 L 180 105 L 60 105 L 60 150" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 180 60 L 180 105 L 300 105 L 300 150" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="120" y="0" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 121px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
63A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="180" y="34" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<path d="M 60 210 L 60 290" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="0" y="150" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 180px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="60" y="184" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<path d="M 300 210 L 300 290" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="240" y="150" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 180px; margin-left: 241px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="300" y="184" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<rect x="0" y="290" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 320px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EVSE1
<br/>
</b>
16A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="60" y="324" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EVSE1...
</text>
</switch>
</g>
<rect x="240" y="290" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 320px; margin-left: 241px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EVSE2
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="300" y="324" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EVSE2...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -0,0 +1,256 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="580px" height="471px" viewBox="-0.5 -0.5 580 471" content="&lt;mxfile&gt;&lt;diagram id=&quot;LkL3k23o4wMPku4sNzY7&quot; name=&quot;Page-1&quot;&gt;3VnLctsgFP0abz1Cr1jL1HHSzjStZ7Jou/JQiUi0WGgQfqhfXxTAeqA0jiNLbrzwiANc4JzDRdgTZ77e3zGYJfc0QmRiW9F+4txMbDsArvgugUICvuNJIGY4khCogAf8BynQUugGRyhvNOSUEo6zJhjSNEUhb2CQMbprNnukpDlqBmNkAA8hJCb6DUc8kejMsyr8I8JxokcGlqpZQ91YAXkCI7qrQc5i4swZpVw+rfdzREruNC+y3+0ztYeJMZTyYzrYssMWko1am5oXL/RiUSTWroqU8YTGNIVkUaEfGN2kESojWqJUtflMaSZAIMBfiPNCCQk3nAoo4WuialEaXZeyiGJKUySRW0yICmmuSi00pxsWqnkqT3HIYqRa+RIqV1Drppi4Q3SNOCtEA4YI5HjbVBcqk8SHdhWP4kFR2U2r865pnY1Fq9tBq0/ErD78FA9x+bBIEYuLLyLjHKqYrtOIGObQvkwwK5UnME1XGcWCjLZYTSl2CeboIYNPFO1EhmvS/iytW8Q42v+TMlXreGqpKkG6Kl3sqmwDdApJapnGt95OMgheZ15lrb5955u+A2As4/n9Gw9meJXj9PcKGOwO7TbbaboNBAPaTccd2W6zDrvZY9ltdka7mewObTf3akS7dSW3NrXbHN3DVLzEseO4RaLDai17XOB2dr0hT4+u7dwrwZdn4EEJfu0r+5nypT6L6wkzGCtf6sn0mTBpmGWXekA79pCG6yJ3BMPZHSe0NZrjunZhb467vBQ3qOOAeX2+Xn4yTVfZKiQwz3HYtp0Y97ui7KnwoyxMPV282dcrb4qX7HjKTws1vrwOujR2tEHVCEt5TdVyAX1BLPQh0ZJB7qTl4XJ7e2wgqxVIcmAEepL0sOzjVDZv81/ny+X4Ml+Kpk5TCsc7UVNxekyt2gc0w7rB1JudTWTz5nySyJculh+4Bqu+f5pe3hGxnlGoiqUb0sfHHL1ZxateEvL/JqK4mU59L6g+V/0I+lLcgcU1f214l1vUa73S+P2I235TeiHsWbUVxeqvI9m8+v/NWfwF&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<path d="M 291 60 L 291 105 L 171 105 L 171 150" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 291 60 L 291 105 L 411 105 L 411 150" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="231" y="0" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 232px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EnergyNode
<br/>
</b>
grid_connection_point
</div>
</div>
</div>
</foreignObject>
<text x="291" y="34" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EnergyNode...
</text>
</switch>
</g>
<path d="M 171 210 L 171 280" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="111" y="150" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 180px; margin-left: 112px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EnergyNode
<br/>
</b>
api_sink_1
</div>
</div>
</div>
</foreignObject>
<text x="171" y="184" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EnergyNode...
</text>
</switch>
</g>
<path d="M 411 210 L 411 280" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="351" y="150" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 180px; margin-left: 352px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EnergyNode
<br/>
</b>
api_sink_2
</div>
</div>
</div>
</foreignObject>
<text x="411" y="184" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EnergyNode...
</text>
</switch>
</g>
<rect x="111" y="410" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 440px; margin-left: 112px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EvseManager
<br/>
</b>
evse_manager_1
</div>
</div>
</div>
</foreignObject>
<text x="171" y="444" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EvseManager...
</text>
</switch>
</g>
<rect x="351" y="410" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 440px; margin-left: 352px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EvseManager
<br/>
</b>
evse_manager_2
</div>
</div>
</div>
</foreignObject>
<text x="411" y="444" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EvseManager...
</text>
</switch>
</g>
<path d="M 171 340 L 171 410" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="111" y="280" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 310px; margin-left: 112px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EnergyNode
<br/>
</b>
ocpp_sink_1
</div>
</div>
</div>
</foreignObject>
<text x="171" y="314" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EnergyNode...
</text>
</switch>
</g>
<path d="M 411 340 L 411 410" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="351" y="280" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 310px; margin-left: 352px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EnergyNode
<br/>
</b>
ocpp_sink_2
</div>
</div>
</div>
</foreignObject>
<text x="411" y="314" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EnergyNode...
</text>
</switch>
</g>
<path d="M 17 180 L 104.63 180" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 109.88 180 L 102.88 183.5 L 104.63 180 L 102.88 176.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 180px; margin-left: 64px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
API
</div>
</div>
</div>
</foreignObject>
<text x="64" y="183" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
API
</text>
</switch>
</g>
<path d="M 7 310 L 104.63 309.61" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 109.88 309.58 L 102.9 313.11 L 104.63 309.61 L 102.87 306.11 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 310px; margin-left: 59px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
OCPP
</div>
</div>
</div>
</foreignObject>
<text x="59" y="313" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
OCPP
</text>
</switch>
</g>
<path d="M 571 309.66 L 477.37 309.66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 472.12 309.66 L 479.12 306.16 L 477.37 309.66 L 479.12 313.16 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 310px; margin-left: 521px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
OCPP
</div>
</div>
</div>
</foreignObject>
<text x="521" y="313" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
OCPP
</text>
</switch>
</g>
<path d="M 571 179.66 L 477.37 179.66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 472.12 179.66 L 479.12 176.16 L 477.37 179.66 L 479.12 183.16 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 180px; margin-left: 521px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
API
</div>
</div>
</div>
</foreignObject>
<text x="521" y="183" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
API
</text>
</switch>
</g>
<path d="M 451 29.66 L 357.37 29.66" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 352.12 29.66 L 359.12 26.16 L 357.37 29.66 L 359.12 33.16 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 30px; margin-left: 401px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
OCPP
</div>
</div>
</div>
</foreignObject>
<text x="401" y="33" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle">
OCPP
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,161 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="361px" height="481px" viewBox="-0.5 -0.5 361 481" content="&lt;mxfile&gt;&lt;diagram id=&quot;LkL3k23o4wMPku4sNzY7&quot; name=&quot;Page-1&quot;&gt;7VpNc5swEP01vmYkBAKOiWOnh2YmM5lpm6NiFKDByCPLsd1fXwECxEcC8Qd23OaQkR4r0O6+1T5sj9B4vrnjZBHcM49GIwN4mxG6HRmGC035PwG2GYCRlQE+D70MgiXwGP6hCgQKXYUeXVYMBWORCBdVcMbimM5EBSOcs3XV7IVF1acuiE8bwOOMRE30Z+iJIEMdC5T4Nxr6Qf5kCNSVOcmNFbAMiMfWGoQmIzTmjIlsNN+MaZTELo9Ltm76ztViY5zGos8CI1vwRqKV8k3tS2xzZ6knfVdTxkXAfBaTaFKiN5ytYo8mdwRyVtp8Z2whQSjB31SIrUokWQkmoUDMI3WVxt51khY5jVlMM2QaRpG6ZdMr5eiSrfhM7VNxShDuU2WFMyjxQFumInFH2ZwKvpUGnEZEhG/V7BJFEr+wK+MoByqU7WFFFx1W51Rh7QijclKPwCYUvxJvrwxLTZ+U88n4dqNPtvkkltvSVyXzpyKmclKuS2f5wqXg7LU4EVCKEC5qOUgxLQtalmYRWS7DWSVRyUNf5HDMIsZTnxEGxIV28UDtinFrY7BPavNzafjcmi0lgyO5rZtnOfCTwTjks1UopNUNp+SV8sKC5yY5Ip9WLMPoemTI5wO0CBr8qRbZOggFfVyQNEJr2buqdHo3qm+UC7r5MGDqKsKq1anWV/S0ddlIYJ6FQGsiGOwfY+tyjiXc5K57KupCtPOxZA9wLH3+gAHAIQC0HTAAWDZoP+t2TZt56LSlS6XHZKsZLFgYi6V254cE0OoSVesSoZpwqtu7H9rLQbaDkkCFK/04hQdudRBVSXUFZJ/vIpacPVAeSr8of7cJdtV7pUmWDNX7ZAtHiQFA6mmDo3gKQP8meLaMNOwaw0AHIx34kf3+jHQOw0jYl5HVUw50kHEo8XVE3p2sgeGjaS9knJH2MsxaReEBtZd9OdrLaVIXnuy9If8sabBGaX9Z8dWSt/NodRb8nPgyrSOLL3tnTu0m6F2Aa+JL0vog4qunuOo6F47UBM+XkZ8UX6ZtHld8uYdhZG/xZV+0+DqrDub8G+rLdE6oviBs+H/cE/3rqgS3WRsn+x7F7S6NyY/HCexXDhCfUTnUX0YsMGQ5WAOL5q/7iWXeF87iC7B8M10FYVxAfxi0IIwD9Yf/+ioLZ0vRHFzY985tn6KJKfe39yQmfimu9FLZrzxqYYbPBFIjwVksNPwl/Ws/xfDkenqYQqt/Bekcr87ktPwtS/baU/4gCE3+Ag==&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<path d="M 180 190 L 180 235 L 60 235 L 60 280" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 180 190 L 180 235 L 300 235 L 300 280" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 150 130 L 150 70.1" fill="none" stroke="#2d7600" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 150 63.35 L 154.5 72.35 L 150 70.1 L 145.5 72.35 Z" fill="#2d7600" stroke="#2d7600" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<rect x="120" y="130" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 160px; margin-left: 121px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
63A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="180" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<path d="M 60 340 L 60 420" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 90 280 L 90 260 Q 90 250 100 250 L 140 250 Q 150 250 150 240 L 150 200.1" fill="none" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 150 193.35 L 154.5 202.35 L 150 200.1 L 145.5 202.35 Z" fill="#005700" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 30 269.9 L 30 230 Q 30 220 40 220 L 126 220 Q 136 220 136.07 210 L 136.2 191.68" fill="none" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 30 276.65 L 25.5 267.65 L 30 269.9 L 34.5 267.65 Z" fill="#6f0000" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 30 340 L 30 409.9" fill="none" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 30 416.65 L 25.5 407.65 L 30 409.9 L 34.5 407.65 Z" fill="#6f0000" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<rect x="0" y="280" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 310px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="60" y="314" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<path d="M 300 340 L 300 420" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 270 280 L 270 260 Q 270 250 260 250 L 220 250 Q 210 250 210 240 L 210 200.1" fill="none" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 210 193.35 L 214.5 202.35 L 210 200.1 L 205.5 202.35 Z" fill="#005700" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 330 269.9 L 330 230 Q 330 220 320 220 L 239 220 Q 229 220 228.9 210 L 228.72 190.84" fill="none" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 330 276.65 L 325.5 267.65 L 330 269.9 L 334.5 267.65 Z" fill="#6f0000" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 330 340 L 330 409.9" fill="none" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 330 416.65 L 325.5 407.65 L 330 409.9 L 334.5 407.65 Z" fill="#6f0000" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<rect x="240" y="280" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 310px; margin-left: 241px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="300" y="314" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<path d="M 90 420 L 90 350.1" fill="none" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 90 343.35 L 94.5 352.35 L 90 350.1 L 85.5 352.35 Z" fill="#005700" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<rect x="0" y="420" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 450px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EVSE1
<br/>
</b>
16A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="60" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EVSE1...
</text>
</switch>
</g>
<path d="M 270 420 L 270 350.1" fill="none" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 270 343.35 L 274.5 352.35 L 270 350.1 L 265.5 352.35 Z" fill="#005700" stroke="#005700" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<rect x="240" y="420" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 450px; margin-left: 241px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EVSE2
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="300" y="454" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EVSE2...
</text>
</switch>
</g>
<path d="M 210 60 L 210 119.9" fill="none" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 210 126.65 L 205.5 117.65 L 210 119.9 L 214.5 117.65 Z" fill="#6f0000" stroke="#6f0000" stroke-width="3" stroke-miterlimit="10" pointer-events="all"/>
<rect x="120" y="0" width="120" height="60" fill="#1ba1e2" stroke="#006eaf" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 121px;">
<div data-drawio-colors="color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EnergyManager
</b>
</div>
</div>
</div>
</foreignObject>
<text x="180" y="34" fill="#ffffff" font-family="Helvetica" font-size="12px" text-anchor="middle">
EnergyManager
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,36 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="121px" height="61px" viewBox="-0.5 -0.5 121 61" content="&lt;mxfile&gt;&lt;diagram id=&quot;LkL3k23o4wMPku4sNzY7&quot; name=&quot;Page-1&quot;&gt;jZNNb4QgEIZ/DXeFXbM9ttbdXnoyac9UpkKCYlh2dfvri2VQ7KZJL2Z45gveGQkru+lk+SBfjQBNaCYmwp4JpQ/5zn9ncAugYPsAWqtEQPkKavUFCDOkFyXgvAl0xminhi1sTN9D4zaMW2vGbdin0duuA2/hDtQN1/f0XQknAz3ss5W/gGpl7Jxn6Ol4DEZwllyYMUGsIqy0xrhgdVMJetYu6hLyjn94l4tZ6N1/EmhIuHJ9wbcRWmif+vThjXY2qre6itAXWvgSaH8Toa4RMfpIaOk7sEEmRZII1MHdorjWXHoB8/0y7x6lclAPvJm9o98mz6TrtD/lS3b6XpTgCtbBlCB8/wlMB87efAh62R63EZfxsMPZjOto8zgvmYy1QMZxm9ql9Cq4N1DzeFxn++NLfhBWfQM=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EVSE
</b>
<br/>
<div>
32A, 3ph
</div>
</div>
</div>
</div>
</foreignObject>
<text x="60" y="34" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EVSE...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,56 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="121px" height="191px" viewBox="-0.5 -0.5 121 191" content="&lt;mxfile&gt;&lt;diagram id=&quot;LkL3k23o4wMPku4sNzY7&quot; name=&quot;Page-1&quot;&gt;zZRNb6MwEIZ/DXfABXWPTUqzh+4pUnt28RR71zBoMoRkf/2aMoSg9EuqKu0F2c+8tmfesYnUuj5sSLf2FxrwURqbQ6RuozT9kVyF7wCOI8hVNoKKnBlRMoOt+wsCY6GdM7BbCBnRs2uXsMSmgZIXTBNhv5Q9o1+e2uoKLsC21P6SPjrDdqTXWTzzn+AqO52cxBKp9SQWsLPaYH+GVBGpNSHyOKoPa/CDd5Mv47q7N6KnxAga/syCVNLg41QbmFCqTJHYYoWN9sVMV4RdY2DYIA6zWXOP2AaYBPgbmI/SN90xBmS59hKFxtwMXQjTBhsYyZ3zXra8LELq2mFHpeSppO+aKhCV3KqhgrNlUvgGsAamYxAQeM1uv2ymljtRnXSzbWEgzr3uouSy176TTaM09yGr1VMYVMNg7ajsHAfVikD/ATopaJJMJJx2WpbkN1Eazo9Vay9atWxEbx3DttUvBvXh3S1Nf9PUPRDD4V3DJKoycViebTbd4n5+BMnE7NkDyOOve3z1scfFw7b4nK0q/Y9tzbPvszVM55/LS+zsD62Kfw==&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<path d="M 60 60 L 60 130" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<rect x="0" y="0" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 30px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Circuit Breaker
<br/>
</b>
16A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="60" y="34" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Circuit Breaker...
</text>
</switch>
</g>
<rect x="0" y="130" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 160px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
EVSE
<br/>
</b>
32A, 3ph
</div>
</div>
</div>
</foreignObject>
<text x="60" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
EVSE...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,135 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="492px" height="192px" viewBox="-0.5 -0.5 492 192" content="&lt;mxfile&gt;&lt;diagram id=&quot;Z5-atGQC2fyeHDTmyhzX&quot; name=&quot;Page-1&quot;&gt;5Zddb9sgFIZ/jW8nf8e5bNO0m7RN1SJt18ic2GjYRJg0yX79jmuwjXHaqkv3oeUiMi9wgOc94ThetKqOd5Lsyk+CAvdCnx696MYLw0Ua4ncrnDohCZedUEhGOykYhA37AVr0tbpnFBproBKCK7azxVzUNeTK0oiU4mAP2wpur7ojBTjCJifcVb8xqspOzRJ/0N8DK0qzcuDrnoqYwVpoSkLFYSRFay9aSSFU91QdV8BbdoZLN+/2TG+/MQm1etEEPeOB8L0+nN6YOpnTSrGvKbQTfC+6PpRMwWZH8rb3gPaiVqqKYyvARx0OpILj2T0F/UkxQ0BUoOQJh5gJC50NOjsWkYZ1GFjHS62VY85GJNrfoo89IMAHTWGeSOgAWdcgi9NnTOJfQ7MVtdK5HKQ9KofLDL2zqKI0tlBlsYsqCGdQpRcglTik7tqf75QRHkXZIBolxXdYCS4kKrWooaXDOJ9IhLOixmaOgAD16xYMw5/hle6oGKXtMrPkbW/eAH6fbgZ+kjjw0xn20QXYp26Wfv1/yKf+nyO/cMkfd0Iqhz7U9KqtNQPVJ+6CRhGpzPCck6ZhuZFvGTfTMKZu+U+RBGrVL5fjiFMyw8loEjhR7MGuenPw9Ar3guFOepv6OMYmf8K/EXuZg541rlKTQI7f00CIqQDlBHr0sj/2i+zNHHs/VM/ZO/j1mtv+bzErXr5bjj92DZ4WjNda5wS6nHVLx7qPQLaobNilSzclTfk4N9CNe6LwomxvRkwgP7vMNRdnz1f3OH2b4m5etUc0v7Rvpf8uzSjIfhdNbA6v8F0mD/+DovVP&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="490" height="190" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<rect x="185" y="110" width="120" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 140px; margin-left: 186px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
EnergyNode
</div>
</div>
</div>
</foreignObject>
<text x="245" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
EnergyNode
</text>
</switch>
</g>
<rect x="11" y="125" width="60" height="30" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 140px; margin-left: 12px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Grid
</div>
</div>
</div>
</foreignObject>
<text x="41" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
Grid
</text>
</switch>
</g>
<rect x="421" y="125" width="60" height="30" fill="none" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 140px; margin-left: 422px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
EV
</div>
</div>
</div>
</foreignObject>
<text x="451" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
EV
</text>
</switch>
</g>
<path d="M 77.37 70 L 421 70" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 72.12 70 L 79.12 66.5 L 77.37 70 L 79.12 73.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 70px; margin-left: 246px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Export
</div>
</div>
</div>
</foreignObject>
<text x="246" y="75" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
Export
</text>
</switch>
</g>
<path d="M 71 30 L 414.63 30" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 419.88 30 L 412.88 33.5 L 414.63 30 L 412.88 26.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 30px; margin-left: 246px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Import
</div>
</div>
</div>
</foreignObject>
<text x="246" y="35" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
Import
</text>
</switch>
</g>
<rect x="305" y="110" width="46" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-dasharray="8 8" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 44px; height: 1px; padding-top: 140px; margin-left: 306px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Leaf Side
</div>
</div>
</div>
</foreignObject>
<text x="328" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
Leaf S...
</text>
</switch>
</g>
<rect x="139" y="110" width="46" height="60" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-dasharray="8 8" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 44px; height: 1px; padding-top: 140px; margin-left: 140px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Root Side
</div>
</div>
</div>
</foreignObject>
<text x="162" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="16px" text-anchor="middle">
Root S...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,743 @@
.. _exp-energymanagement:
############################
Energy Management in EVerest
############################
This section provides an in-depth explanation of the energy management concept
implemented in EVerest.
It covers the representation of energy systems as energy trees, the handling of
energy requests and distribution, and the configuration of energy trees within
EVerest. The central modules involved in this concept are the
:ref:`EnergyManager <everest_modules_EnergyManager>` and
:ref:`EnergyNode <everest_modules_EnergyNode>` modules.
One of its central ideas of EVerest's energymanagement to represent the energy
system for which power is distributed as an energy tree containing energy nodes.
This enables the representation of arbitrarily complex configurations of
physical and logical components within the targeted energy system.
The following sections present this concept in more detail.
Energy nodes
------------
An energy node can be either a logical or physical component within the energy
system.
Energy nodes can typically be classified into the following categories:
* **Physical Components**: Circuit breakers, electrical fuses
* **Logical Components**: Limits from OCPP, EEBus, or other external sources
* **Charging Stations**: Unidirectional or bidirectional charging stations
(or in general any sink or source of power)
An EVerest module becomes an energy node by implementing the
:doc:`energy </reference/interfaces/energy>` interface.
.. note::
At the time of writing, two EVerest modules are considered energy nodes as
per the above definition: **EnergyNode** and **EvseManager**.
More may be added in the future.
The **EnergyNode** module fulfills central aspects of the energy management
concept.
When the term **EnergyNode** is used, it refers to the actual module,
whereas **energy node** refers to the general definition above.
The **EnergyNode** module both requires and provides the
:doc:`energy </reference/interfaces/energy>` interface.
This design enables the representation of arbitrary energy tree configurations
within the EVerest configuration file as explained in detail in a later
section.
Energy trees
------------
Energy trees are used to model various energy system configurations.
Below are examples demonstrating how energy systems can be represented in
EVerest.
The simplest energy tree consists of a single leaf node representing an EVSE
with a physical hardware capability of 32 A on 3 phases.
.. image:: images/single_node.drawio.svg
:name: single-node-label
:align: center
Typically, the electrical connection of charging stations is protected by a
circuit breaker.
Adding this to the representation results in:
.. image:: images/single_node_with_circuit_breaker.drawio.svg
:name: single-node-with-circuit-breaker-label
:align: center
In this example, the circuit breaker limits the current to 16 A, even though
the EVSE supports 32 A.
The module managing power distribution must enforce this limitation.
For a more complex setup, consider the following example:
.. image:: images/energy_tree.drawio.svg
:name: energy-tree-label
:align: center
Here, a top-level circuit breaker limits the line to 63 A.
Two additional circuit breakers protect the lines to two EVSEs, each fused at
32 A.
EVSE1 can consume 16 A on three phases, while EVSE2 can consume 32 A on three
phases.
This module accounts for all existing limitations when distributing power to
energy nodes.
All the scenarios above can be represented within EVerest.
The power distribution to the EVSEs is managed by this module, considering the
limitations of each individual node.
How these setups above can be represented in EVerest is presented in section
:ref:`'Configuration of energy trees in EVerest' <configuration_of_energy_trees_in_everest>`.
Energy requests and distribution
--------------------------------
The EnergyManager module requires exactly one module implementing the
:doc:`energy </reference/interfaces/energy>` interface.
This interface defines:
* A single variable **energy_flow_request** of type **EnergyFlowRequest**
* A single command **enforce_limits**
The concept of the usage of this interface is further described in the
following sections.
Energy flow request variable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The **EnergyFlowRequest** type is recursive, containing a list of child
**EnergyFlowRequest**.
It defines the power and current requested by an energy node, along with its
limitations (e.g., hardware or software constraints).
In essence, a module specifies its requirements and limitations through this
type, which are then communicated to its parent node.
The parent node creates an aggregated **EnergyFlowRequest**, incorporating its
own limitations and the requests from its children.
Energy flow requests are constructed from the leaves to the root of the energy
tree, resulting in a single **EnergyFlowRequest** that contains all child
requests.
This final request serves as input for this module, which calculates the limits
to enforce down the tree.
The following diagram illustrates how energy nodes communicate requests, with
green arrows representing energy flow requests:
.. image:: images/energy_tree_request_and_distribution.drawio.svg
:name: energy-tree-request-and-distribution-label
:align: center
Enforcing limits
^^^^^^^^^^^^^^^^
The **enforce_limits** command propagates limits down the tree.
Each energy node calls this function on its child nodes to enforce calculated
limits.
Note that the EnergyManager itself does not represent an energy node.
It communicates the resulting **EnergyFlowRequest** to a single connected
energy node, which then propagates the limits further down the tree.
Details of the EnergyFlowRequest type
-------------------------------------
Energy nodes may have varying types of limits.
To understand this better, consider a zoomed-in view of an energy node:
.. image:: images/zoom_in_energy_node.drawio.svg
:name: zoom-in-energy-node-label
:align: center
In reality, an energy node may have different limits for charging (import) and
discharging (export).
The **EnergyFlowRequest** type accounts for this distinction:
* **Import**: Energy flow direction from the grid to the consumer/EV (charging)
* **Export**: Energy flow direction from the EV to the grid (discharging)
Additionally, each direction may have separate limits for the root and leaf
sides of the energy node.
For example, a DC power supply may have AC limits on the root side (facing the
grid) and DC limits on the leaf side (facing the EV).
Limits may also change over time, which is why the *schedule_import* and
*schedule_export* properties are lists containing multiple limit
specifications.
Besides the limiting schedules for import and export, it also contains
a *setpoint_schedule*. This is a list (time series) just like the limiting schedules
and it may contain setpoints for each timeslot, see below for a more detailed description.
Each value that is given for a limit or schedule has a source property (string).
It is used to track which value is the actual limiting factor for the result in the
optimization algorithm.
This is useful to understand how the result that is sent to the EvseManager is composed.
Below is an example JSON representation of an **EnergyFlowRequest** for a leaf node:
.. code-block:: json
{
"children": [],
"evse_state": "Charging",
"node_type": "Evse",
"priority_request": false,
"schedule_export": [
{
"limits_to_leaves": {
"ac_max_current_A": {
"value": 10.0,
"source": "EVSE1_leave"
}
},
"limits_to_root": {
"ac_max_current_A": {
"value": 16.0,
"source": "EVSE1_root"
},
"ac_max_phase_count": {
"value": 3,
"source": "EVSE1_phase"
},
"ac_min_current_A": {
"value": 0.0,
"source": "EVSE1_mincurrent"
},
"ac_min_phase_count": {
"value": 1,
"source": "EVSE1_minphase"
},
"ac_number_of_active_phases": 3,
"ac_supports_changing_phases_during_charging": true
},
"timestamp": "2024-12-17T13:08:36.479Z"
}
],
"schedule_import": [
{
"limits_to_leaves": {
"ac_max_current_A": {
"value": 32.0,
"source": "EVSE1_leave"
}
},
"limits_to_root": {
"ac_max_current_A": {
"value": 32.0,
"source": "EVSE1_root"
},
"ac_max_phase_count": {
"value": 3,
"source": "EVSE1_phase"
},
"ac_min_current_A": {
"value": 6.0,
"source": "EVSE1_mincurrent"
},
"ac_min_phase_count": {
"value": 1,
"source": "EVSE1_minphase"
},
"ac_number_of_active_phases": 3,
"ac_supports_changing_phases_during_charging": true
},
"timestamp": "2024-12-17T13:08:36.479Z"
}
],
"schedule_setpoints": [],
"uuid": "evse1"
}
Setpoints
---------
Setpoints can optionally be specified for each time slot. Note that the schedule_setpoints list
may have different timestamp entries then the limiting schedules.
A setpoint entry may have an ampere or watt limit or specify a Watt-Grid Frequency table as setpoint.
Only one of those three options may be set in each timeslot, but they may be different for each timeslot.
The priority property is intended to be used if multiple conflicting setpoints exist in the energy tree.
This is not implemented for now, the priority property will be ignored for now.
In most cases a setpoint is not neccessary as the same functionality can also be implemented by setting the
limits at the node appropriately.
It is especially useful for the bidirectional use case, as it selects whether charging or discharging should be performed:
E.g. with watt limits of -10kW to +10kW and a setpoint of -2kW, it will discharge at 2kW.
A setpoint of +3kW will switch to charging without changing the limits.
The Frequency based watt limit table can be set through e.g. OCPP 2.1 smart charging. It is intended to
specifiy a grid stabilizing mode, in which the charger charges when the grid frequency is too high and discharges
to support the grid if the frequency is too low.
External limits
---------------
External limits can be added to the energy system using EVerest modules
implementing the
:doc:`external_energy_limits </reference/interfaces/external_energy_limits>`
interface.
At the time of writing, the **EnergyNode** module is the sole module that
provides this functionality.
The `external_energy_limits` interface defines the **set_external_limits**
command, which modules like OCPP or API can use to specify external energy
limits.
These limits are then considered by the **EnergyNode** module when creating
its energy flow request.
To apply external limits, a module must require the `external_energy_limits`
interface and invoke the **set_external_limits** command.
The next section details how to configure these limits in EVerest.
.. _configuration_of_energy_trees_in_everest:
Configuration of energy trees in EVerest
----------------------------------------
The following section describes how to configure the EVerest configuration file
in order to represent the targeted energy tree.
In order to do that we are using a complex energy tree example and implement
this in the configuration step by step.
This is the energy tree that we are going to represent in the EVerest
configuration:
.. _energy_tree_complex_label:
.. image:: images/energy_tree_complex.drawio.svg
:alt: Example of a complex energy tree
:align: center
This energy tree represents a setup with two EVSEs.
There are two external sources that are able to provide external energy limits:
OCPP and the API module.
OCPP is able to set external limits for each EVSE as well as for the whole
charging station.
This is indicated by the three arrows labeled with OCPP.
The API module is only able to set the limits for the two EVSEs, but not for
the whole charging station.
.. note::
To improve readability, unrelated module configurations and connections are
omitted in the examples below.
First, we add two EvseManager modules to the config file representing our
energy leaf nodes.
.. code-block:: yaml
active_modules:
evse_manager_1:
module: EvseManager
evse_manager_2:
module: EvseManager
The two EVSEs can receive limits from OCPP.
Therefore, we add two **EnergyNode** modules that represent the sinks for the
external limits.
The **EnergyNode** module requires a connection to a module implementing the
:doc:`energy </reference/interfaces/energy>` interface.
This is implemented by connecting the previously added EvseManager modules to it.
Any external limit applied to the added EnergyNode modules will be applied to
its energy child nodes (the EvseManager modules) now.
.. code-block:: yaml
active_modules:
evse_manager_1:
module: EvseManager
evse_manager_2:
module: EvseManager
ocpp_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_1
implementation_id: energy_grid
ocpp_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_2
implementation_id: energy_grid
We continue with adding **EnergyNode** modules that represent the sinks for the
limits received by the API module.
Note that the **EnergyNode** module provides and requires the
:doc:`energy </reference/interfaces/energy>` interface at the same time.
This allows us to connect **EnergyNode** modules and therefore fullfill the
requirement of others.
Note that the modules **ocpp_sink_1** and **ocpp_sink_2** are connected to the
**api_sink_1** and **api_sink_2**.
This means that both limits can be considered by this module without
overriding each other.
.. code-block:: yaml
active_modules:
evse_manager_1:
module: EvseManager
evse_manager_2:
module: EvseManager
ocpp_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_1
implementation_id: energy_grid
ocpp_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_2
implementation_id: energy_grid
api_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: ocpp_sink_1
implementation_id: energy_grid
api_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: ocpp_sink_2
implementation_id: energy_grid
We are now only missing a represention for the complete charging station.
Therefore, we add another **EnergyNode** module with a fuse limit of 63 A and
we name it **grid_connection_point**.
We connect **api_sink_1** and **api_sink_2** to it.
.. code-block:: yaml
active_modules:
evse_manager_1:
module: EvseManager
evse_manager_2:
module: EvseManager
ocpp_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_1
implementation_id: energy_grid
ocpp_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_2
implementation_id: energy_grid
api_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: ocpp_sink_1
implementation_id: energy_grid
api_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: ocpp_sink_2
implementation_id: energy_grid
grid_connection_point:
module: EnergyNode
config_module:
fuse_limit_A: 63
phase_count: 3
connections:
energy_consumer:
- module_id: api_sink_1
implementation_id: energy_grid
- module_id: api_sink_2
implementation_id: energy_grid
Now we have the complete energy tree represented, but we're still missing to
include the modules that set the external energy limits, so the OCPP and API
module.
Since these modules require (optionally multiple) connections to modules
implementing the
:doc:`external_energy_limits </reference/interfaces/external_energy_limits>`
interface, we need to also add the connections to the **EnergyNode** modules we
have added previously.
Finally, we also add the **EnergyManager** module and connect the
**grid_connection_point** to it.
.. code-block:: yaml
active_modules:
evse_manager_1:
module: EvseManager
evse_manager_2:
module: EvseManager
ocpp_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_1
implementation_id: energy_grid
ocpp_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: evse_manager_2
implementation_id: energy_grid
api_sink_1:
module: EnergyNode
connections:
energy_consumer:
- module_id: ocpp_sink_1
implementation_id: energy_grid
api_sink_2:
module: EnergyNode
connections:
energy_consumer:
- module_id: ocpp_sink_2
implementation_id: energy_grid
grid_connection_point:
module: EnergyNode
config_module:
fuse_limit_A: 63
phase_count: 3
connections:
energy_consumer:
- module_id: api_sink_1
implementation_id: energy_grid
- module_id: api_sink_2
implementation_id: energy_grid
ocpp:
module: OCPP
connections:
evse_energy_sink:
- module_id: grid_connection_point
implementation_id: external_limits
- module_id: ocpp_sink_1
implementation_id: external_limits
- module_id: ocpp_sink_2
implementation_id: external_limits
api:
module: API
connections:
evse_energy_sink:
- module_id: api_sink_1
implementation_id: external_limits
- module_id: api_sink_2
implementation_id: external_limits
energy_manager:
module: EnergyManager
connections:
energy_trunk:
- module_id: grid_connection_point
implementation_id: energy_grid
We have now added all the required modules and connections to represent the
energy tree example :ref:`shown above <energy_tree_complex_label>`.
One important detail is still missing, which is the module mapping.
For detailed information about the module mapping please see
:doc:`3-tier module mappings </explanation/tier-module-mappings>`.
Since the connections of a module in the EVerest config does not automatically
map to a specific EVSE (or the whole charging station, represented by EVSE#0),
the **EnergyNode** modules must have a module mapping.
This allows the modules that make use of the **set_external_limits** command to
call it for the correct node.
Modules like OCPP and API can only know at which requirement the command
**set_external_limit** shall be called in case the energy node that is
connected to it has a specified module mapping in the EVerest config.
This is a full example including the module mappings:
.. code-block:: yaml
active_modules:
evse_manager_1:
module: EvseManager
mapping:
module:
evse: 1
evse_manager_2:
module: EvseManager
mapping:
module:
evse: 2
ocpp_sink_1:
module: EnergyNode
mapping:
module:
evse: 1
connections:
energy_consumer:
- module_id: evse_manager_1
implementation_id: energy_grid
ocpp_sink_2:
module: EnergyNode
mapping:
module:
evse: 2
connections:
energy_consumer:
- module_id: evse_manager_2
implementation_id: energy_grid
api_sink_1:
module: EnergyNode
mapping:
module:
evse: 1
connections:
energy_consumer:
- module_id: ocpp_sink_1
implementation_id: energy_grid
api_sink_2:
module: EnergyNode
mapping:
module:
evse: 2
connections:
energy_consumer:
- module_id: ocpp_sink_2
implementation_id: energy_grid
grid_connection_point:
module: EnergyNode
mapping:
module:
evse: 0
config_module:
fuse_limit_A: 63
phase_count: 3
connections:
energy_consumer:
- module_id: api_sink_1
implementation_id: energy_grid
- module_id: api_sink_2
implementation_id: energy_grid
ocpp:
module: OCPP
connections:
evse_energy_sink:
- module_id: grid_connection_point
implementation_id: external_limits
- module_id: ocpp_sink_1
implementation_id: external_limits
- module_id: ocpp_sink_2
implementation_id: external_limits
api:
module: API
connections:
evse_energy_sink:
- module_id: api_sink_1
implementation_id: external_limits
- module_id: api_sink_2
implementation_id: external_limits
energy_manager:
module: EnergyManager
connections:
energy_trunk:
- module_id: grid_connection_point
implementation_id: energy_grid
Energy distribution
-------------------
The EnergyManager module implements an algorithm to distribute available power
to energy leaf nodes:
* It calculates and enforces limits for each energy leaf node in the tree.
* It ensures that no node exceeds its specified limits for current, power, or
phase count.
* It distributes power equally among child nodes, if their collective request
exceeds the parent node's limits.
* The algorithm prefers charging over discharging if the specified limits allow
for both.
* It supports phase switching between single-phase and three-phase modes,
optimizing power usage for low-demand scenarios if
**switch_3ph1ph_while_charging_mode** is enabled.
Phase switching
---------------
This module supports switching between single-phase (1ph) and three-phase (3ph)
configurations during AC charging.
.. warning::
Some vehicles (such as the first generation of Renault Zoe) may be
permanently damaged when switching from 1ph to 3ph during charging.
Use at your own risk!
To use this feature, several configurations must be enabled across different
EVerest modules:
- **EvseManager**: Adjust the following configuration options to your needs:
- ``switch_3ph1ph_delay_s``
- ``switch_3ph1ph_cp_state``
- **Module implementing the `evse_board_support </reference/interfaces/evse_board_support>` interface:**
- Set ``supports_changing_phases_during_charging`` to ``true`` in the reported capabilities.
- Define the minimum number of phases as 1 and the maximum as 3.
- Ensure the ``ac_switch_three_phases_while_charging`` command is implemented.
- **EnergyManager**: Adjust the following config options to your needs:
- switch_3ph1ph_while_charging_mode
- switch_3ph1ph_max_nr_of_switches_per_session
- switch_3ph1ph_switch_limit_stickyness
- switch_3ph1ph_power_hysteresis_W
- switch_3ph1ph_time_hysteresis_s
Refer to the manifest.yaml for documentation of these configuration options.
If all of these are properly configured, the EnergyManager will handle the
1ph/3ph switching.
To enable this, an external limit must be set.
There are two ways to configure the limit:
1. **Watt-based limit (preferred option):** The limit is set in Watts (not
Amperes), even though this involves AC charging.
This provides the EnergyManager with the flexibility to decide when to
switch.
The limit can be defined by an OCPP schedule or through an additional
EnergyNode.
2. **Ampere-based limit:** The limit is defined in Amperes, along with a
restriction on the number of phases (e.g., ``min_phase=1`` and
``max_phase=1``).
This enforces switching and allows external control over the switching time,
but the EnergyManager loses its ability to choose when to switch.
Best practices
^^^^^^^^^^^^^^
In general, this feature works best in a configuration with 32 A per phase and
a Watt-based limit.
In this setup, there is an overlap between the single phase and three phase
domain:
- Single-phase charging: 1.3 kW to 7.4 kW
- Three-phase charging: 4.2 kW to 22 kW (or 11 kW)
This avoids switching too often in the most elegant way.
Other methods to reduce the number of switch cycles can be configured in the
EnergyManager, see config options above.
Current limitations
-------------------
* The algorithm does not account for real-time power meter readings from
individual nodes.
* It does not redistribute unused power when the actual consumption is below
the assigned target value.

View File

@@ -0,0 +1,179 @@
.. error_framework:
###############
Error Framework
###############
This explains the general idea and the components provided by the error framework.
For practical hints on the usage of the error framework, consult the
:ref:`error framework how-to guide <htg_error_framework>`.
*******
Purpose
*******
The error framework is used to handle errors in the EVerest framework.
As not every module can "decide" by itself how to react to an error and how
to handle it, the error framework provides functionality that allows modules
to react to errors that are raised in required other modules.
The other main purpose of the error framework is to provide a way to monitor
and report errors in the system. This can be used for example for displaying
or reporting to an OCPP backend.
*****
Usage
*****
General
=======
The Error class
---------------
The Error class is a simple struct that holds all required information
required to handle the error. Data members include:
- type
- sub-type
- arbitrary message + description
- the raising module's ID
- vendor_id
- severity
- timestamp
- uuid
- state (active, cleared, ...)
Raise an error
--------------
Each implementation of an interface can raise errors that are defined in the
interface. There is one function `raise_error` that takes an error object as
argument. The error object is an instance of the class `Error`. To create
the initial error object, the `ErrorFactory` is used.
Clear an error
--------------
An error can be cleared by the same implementation that raised the error. There
are multiple functions to clear an error.
The function `clear_error` provides two different overloads. The first
overload takes the `ErrorType` as argument and clears errors with matching
`ErrorType`.
The second overload takes `ErrorType` and `ErrorSubType` as arguments.
In this case, only the error with matching `ErrorType` and `ErrorSubType`
is cleared.
The function `clear_all_errors_of_impl` clears all errors of the current
implementation.
Subscribe to an error
---------------------
A module can subscribe to errors of its requirements. This way the module can
react to those errors. There are two functions to subscribe to errors.
The function `subscribe_error` takes the `ErrorType` and two callback functions
as arguments.
The `ErrorType` is the type of the error that the module wants to subscribe to.
The first callback is called when the error is raised.
The second callback is called when the error is cleared.
The function `subscribe_all_errors` takes again two callback functions as
arguments. The first callback is called when an error of any type is raised by
the requirement. The second callback is called when an error is cleared.
Subscribe globally to all errors
--------------------------------
This feature is currently only available for C++ modules. It allows a module
to subscribe globally to all errors of all modules. This can be used for
example for logging purposes or error reporting.
To enable this functionality, the flag `enable_global_errors` in the module's
manifest file must be set to `true`.
With this, the function `subscribe_global_all_errors` is added to the
auto-generated code. This way the function can be used as the other subscribe
functions, with two callback functions as arguments.
The ErrorFactory
----------------
Since a module does not have direct access to some information that is required
to create an error object, as for example the `module_id`, the class
`ErrorFactory` is used, which is provided for each implementation of an
interface, with correct default values.
The `ErrorFactory` is used to create the initial error object.
This error object can be raised as it is, or can be modified before raising.
The ErrorFactory provides five signatures for the create_error function:
- *Default*: Takes no arguments and initializes with default values.
- *Standard*: Requires `ErrorType`, `ErrorSubType`, and `message`.
- *Contextual*: Extends the *standard* signature by adding either `severity`, `state`, or *both*.
The ErrorStateMonitor
---------------------
The `ErrorStateMonitor` is a class that is used to monitor the error state of
implementations and requirements.
It is provided for each implementation of an interface and for each requirement
of an module.
To check if an error is currently active, the function `is_error_active` is
used. This function takes the `ErrorType` and `ErrorSubType` as arguments and
returns a boolean value. If the error is active, the function returns `True`,
otherwise `False`.
To check if a specific set of errors is in a specific state, the struct
`StateCondition` is defined.
This struct has the members `ErrorType`, `ErrorSubType` and `active: boolean`.
The function `is_condition_satisfied` can either take a single `StateCondition`
or a list of `StateCondition` as argument.
If a single `StateCondition` is passed, the function returns `True` if the
error is in the state as defined in the `StateCondition`.
If a list of `StateCondition` is passed, the function returns `True` if all
conditions are satisfied.
***********
Usage Guide
***********
Creating Error objects
======================
Error objects may always be created using the `ErrorFactory` of the
implementation.
Error objects can be edited after creation, before raising them.
The following attributes may not be changed after creation:
- `timestamp`
- `origin.module_id`
- `origin.implementation_id`
- `uuid`
The global subscription
=======================
If a module is subscribed to global all errors, it may do only "reporting"
actions, but no "handling" actions. This means that the module does not change
its behavior based on the error, but only reports the error for example to a
log file.
Side effects of raising errors
==============================
The error framework allow module implementations to get notified about an error
from one of their requirements by subscribing to the error. This can be used for
reporting purposes (e.g. via OCPP) or it can be used to adjust the control flow
of the module based on the raised error.
It is important to note that raising errors can therefore lead to side effects
within other modules. The side effects shall be documented as part of the module
documentation (see e.g. EvseManager or OCPP).

View File

@@ -0,0 +1,320 @@
.. _exp-hardware-architecture:
#####################
Hardware Architecture
#####################
This page gives some ideas and guidance on the general architecture of AC or DC
charging stations and helps to choose the best components.
***************
DC architecture
***************
The following block diagram shows a typical architecture for a DC charger:
.. image:: images/dc-architecture.png
:align: left
On the top, the Linux high-level controller runs EVerest plus all customer
specific software.
EVerest connects to the hardware components through the EVerest-integrated
hardware drivers or external custom drivers that use the EVerest APIs to
communicate with EVerest.
The hardware components are typically connected to the Linux controller by
CAN, RS458, Ethernet or similar. They may be different in your design.
On the bottom, a typical low-level controller design is shown.
Handling the electrical safety in the low-level design is crucial, as the
high-level Linux controller cannot guarantee timings or even that it is running
at all.
The safety MCU shall handle at least the following functionality:
* Control pilot signal I/O:
It outputs the PWM according to the duty cycle controlled by EVerest and reads
states A-F back.
* Contactor close signal:
It receives an “on/off” flag from EVerest and also internally creates a second
“on/off” flag.
As an example, the internal flag is only “on” if e.g. CP is in state C and no
overtemperature is detected with the PT1000 on the connector pins.
It outputs a contactor close signal only if both flags are “on”.
It is responsible for opening the output contactors in case of CP state not C,
over-temperature errors, loss of PE connection and all other critical
faults - independent from the Linux high-level control.
* The isolation monitor and the over-voltage protection circuitry shall also be
able to directly open the output contactors, independent of other components.
This fault signal may also be routed through the safety MCU.
EVerest will read values from the isolation monitor and the OVP module as well
and will issue a shutdown, but this will come (1) too late and (2) the safety
shutdowns shall be working even if Linux is down.
The safety MCU may require certification for e.g. UL as it contains safety in
software functionality.
The output contactors should be the last component before the plug to the EV.
Then they fully disconnect the user from all internal circuitry, so as long as
the contactors are open, no internal fault causes a safety hazard on the output
plug pins.
The block diagram above shows only two output contactors to fully switch the
output on and off.
Some power supplies may require a third contactor that switches a precharge
resistor in the output path.
This is required if the DC power supply does not have an accurate and fast
current limit functionality at very low limits (e.g. 1A).
If the DC power supply cannot ramp down the voltage quickly, an additional
contactor may be required that switches a load resistor on the output for
active discharge.
Both are not shown here as they are typically not required with most EV
charging power supplies.
***************
AC architecture
***************
The typical architecture for an AC charger is similar to that of a DC
charger, but has fewer components on the power path. The requirements for the
safety MCU apply here as well.
.. image:: images/ac-architecture.png
:align: left
************************
Choosing components (AC)
************************
Output contactors
=================
The output contactors shall have a mirror feedback contact.
With many DIN rail components, the mirror contact (or auxiliary contact) can be
mounted as a snap on device.
Ensure that the minimum current requirements are met.
Some contactors require between 10 mA and 50 mA of current flowing through the
mirror contact to ensure the contacts remain clean.
Especially for PCB mount contactors, check the contact air gap.
It should be at least 3 mm (check IEC 61851-1:2017 8.1 for alternatives).
The 3 mm found in the IEC 61851-1 originates from IEC 60664-1 Table F.2 for
4 kV rated impulse voltage, overvoltage category 3, inhomogeneous field.
For a homogeneous field, 1.2 mm would be enough.
In general, a 4-pole contactor should be used.
Some applications (such as solar-based charging) may want to use two 2-pole
contactors to allow for 1 ph/3 ph switching.
See the chapter on RCD below on limitations when using this configuration.
A significant part of the generated heat comes from the coil current.
It is recommended to lower the coil voltage as per specifications from the
manufacturer after the switching.
Examples for PCB mount 4-pole relays:
Panasonic AHER4191, Omron G9KC.
RCD
===
Integration of an RCD is optional; but if it is not in the charging station it
has to be installed in the upstream installation outside of the charging
station.
RCDs shall comply with one of the following standards:
IEC 61008-1, IEC 61009-1, IEC 60947-2 and IEC 62423.
For AC charging, a type B RCD is generally required to protect against both
AC and DC fault currents.
The RCD Type B may also be integrated into the charging station, simplifying
the installation requirements.
As Type B RCDs are quite expensive, a common solution is to integrate a
Type A RCD for AC faults and a DC fault current detector as a separate module.
In this case, the 6 mA DC fault detection module should follow IEC 62955
(check IEC 61851-1:2017 8.5; the standard says the IEC 62955 is an example to
be compliant).
Such modules are available as PCB mount or as individual modules, e.g. from
Bender/Vacuumschmelze (Benvac), Würth Elektronik, Western automation and
several others.
.. note::
The fault output of these modules shall directly open the output contactor
without waiting for Linux and EVerest.
EVerest should be informed after the switching off so that the error can be
reported.
If the general output contactor is used for RCD switch off, IEC 62955 requires
a 4-pole relay.
E.g., two 2-pole relays are no longer allowed by this standard.
In this configuration a combination of two 2-pole contactors for 1 ph/3 ph
switching followed by a 4-pole relay for RCD switch off may be required.
Power meter
===========
For AC applications, a lot of different DIN rail components are available from
many different manufacturers.
Typically, they have a ModBus RS-485 interface to the host.
Most of them can be easily added to EVerest by simple register mapping in the
GenericPowerMeter module if not supported already.
Make sure they are MID-compliant for CE.
If German Eichrecht is required, it is a bit harder to find power meters.
They are available from Bauer or EMH, for example.
************************
Choosing components (DC)
************************
Isolation monitor
=================
Most isolation monitoring devices come as DIN rail devices.
Check the following specifications:
- Certified to IEC 61557-8 or equivalent (see IEC 61851-23: 2023 CC 4.1.5)
- Measures the isolation resistance (total to PE or individually for
negative to PE and positive to PE).
Measurement range should include 100 kOhm with some margin,
e.g. 50 kOhm - 500 kOhm.
- Voltage range >= maximum voltage of DC power supply
- Communication interface with host system (e.g. ModBus RS485, CAN, ...)
- Self-test functionality via communication interface (trigger start of
self test, read result).
Relying on automatic periodic self-testing is no longer allowed in the 2023
edition of IEC 61851-23.
- Self-test should be quick (e.g. < 10 s), long self-tests may lead to
timeout issues with certain vehicles
- Time needed to detect a fault should be short (e.g. <5 s)
- Measures the voltage between DC positive and negative wire and report
via communication interface
- Separate fault output that can be used to trigger an emergency shutdown
independently from the charge controller (and EVerest!)
- Ideally: Over-voltage detection and shutdown according to IEC 61851-23:
2023 6.3.1.106 (we are not aware of a product that has this at the time of
writing)
.. note::
We are not aware of a product that fulfills all of these specifications, so
some trade-offs may need to be made and additional hardware may be required.
Example devices are Bender isoCHA425, Dold RN 5897/021 or Acrel AIM-D100.
Over voltage monitor
====================
IEC61851-23:2023 has stricter requirements than the earlier version.
One of the new safety requirements is a fast over-voltage protection, that
triggers if the DC voltage is above the limits specified in the standard for
9 ms.
The actual detection and shutdown needs to be handled outside of EVerest (e.g.
in dedicated hardware).
EVerest can only provide the value of the over-voltage limit (as it depends on
the maximum voltage reported by the EV) and start/stop the monitoring.
Refer to IEC61851-23:2023 6.3.1.106.2 for requirements.
.. note::
We are not aware of an off-the-shelf product that fits this requirement.
AC/DC converter / DC power supply
=================================
For new products we highly recommend to have a voltage range of 150 V to 1000 V
for best compatibility for CCS.
Charin suggests 920 V as the high limit, which may be on the edge already with
e.g. Lucid Air.
The lower limit is a bit flexible, but we recommend not to have more than
200 V.
Some vehicles refuse to charge if the lower limit is too high, even if they do
not require such a low voltage.
Another important topic is the current capability.
Some power supplies have quite low current limits, e.g. 30 A for a 30 kW power
supply.
This means it can only reach 30 kW on a 1000 V vehicle, while it will be
limited to 9 kW on a 300 V vehicle.
Many power supplies actually have two 500 V converters internally, and they
can be arranged in a serial or parallel configuration.
In this case, it is often possible to get a higher current output for the low
voltage vehicles in parallel mode.
The driver code should use this functionality and switch automatically between
the two modes.
High quality power supplies often have a constant power output, e.g. they can
deliver the full 30 kW over the full voltage range.
Those will give the best user experience.
Other important features are full protection (fully protected against
shorts / load dump under full load), noise, efficiency and reliability.
Example devices are UUGreenPower, Huawei, SCU, Tonhe or Infypower.
Output contactors
=================
Output contactors shall have the capability to open the contact at the maximum
current possible in the system.
Most contactors survive this only a limited number of times, e.g. three times
before they need to be replaced.
We recommend that the low-level safety architecture ensures that the DC power
supplies ramp down shortly before the contactors open in an emergency shutdown
to protect the contactors.
If this is not possible, the recommendation is to use contactors that are
robust enough to withstand this quite often.
Under normal conditions the contactors always switch at zero current, but due
to the poor quality of EV side implementations emergency shutdowns under full
load will happen.
The contactors shall also have mirror feedback contacts so that EVerest knows
when they are fully open/closed and stuck contactors can be detected.
Ensure that the minimum current requirements are met.
Most contactors require between 10 mA and 50 mA of current flowing through the
mirror contact to ensure the contacts remain clean.
Verify that the contact gap is in accordance with IEC 60664-1 for the maximum
voltage.
Power meter
===========
For DC applications, different DIN rail components are available from many
different manufacturers.
Typically, they have a ModBus RS-485 interface to the host.
Make sure they are MID-compliant for CE.
If German Eichrecht is required, it is a bit harder to find power meters.
They are available from LEM, DZG, AST, Isabellenhütte or Carlo Gavazzi, for
example.
----
**Authors**: Cornelius Claussen

View File

@@ -0,0 +1,395 @@
================
Hardware Drivers
================
This chapter describes Hardware Driver modules that are supported
natively by EVerest. The presented components have been prequalified
by Pionix to work with EVerest to ensure a quick path to production.
----------------------------
Isolation Monitoring Devices
----------------------------
Bender ISOMETER isoCHA425
-------------------------
*Hardware Driver Module* :ref:`Bender_isoCHA425HV <everest_modules_Bender_isoCHA425HV>`
You can find more information about the device here:
`<https://www.bender.de/produkte/isolationsueberwachung/isometerr-isocha425hv-mit-agh420-1>`_
Here are the most important specifications:
- RS485/ModBus connection
- Up to 1000 V DC with AGH420-1/AGH421-1
- \< 10s response time
- Firmware \< 5.00: CableCheck time: Self test ~22s + 10s response time,
can be used with IEC 61851-23:2014, but cannot be used with IEC
61851-23:2023 certification
- Firmware 5.00 improves CableCheck time to allow usage with IEC
61851-23:2023 (available now from Bender as of Q1/2025). Usage of
older 4.x firmware is not recommended.
- AGH421-1 allows full disconnection from the DC wires to allow for
multiple IMDs on the same wires, one active at a time
- Measures DC output voltage
- Measures voltage between DC+/DC- and PE as well
- Two separate relays to trigger in case of errors
| EVerest supports this device with the "Bender_isoCHA425HV" module.
The module requires a "SerialCommHub" module to be loaded as well for
the ModBus communication.
| All settings of the device can be adjusted in the module
configuration.
The error output relays should be wired directly to the DC output relay
of the charger to enable a low-level emergency shutdown functionality
which works independently of EVerest.
EVerest will read the isolation resistance values and switch off if
they fall below 100 kOhm as well, but safety certification should not
rely on the Linux system.
Dold RN5893 IMD
-------------------------
*Hardware Driver Module* :ref:`Dold RN5893 <everest_modules_DoldRN5893>`
You can find more information about the device here:
`<https://www.dold.com/en/products/relay-modules/monitoring-devices/insulation-monitors/rn-5893>`_
Here are the most important specifications:
- Width: 52,5 mm
- Classification: For DC charging stations
- Response value: 1 - 500 kΩ
- IMD type: AC, DC, AC/DC
- Nominal voltage IT system: AC 0 - 230, AC 0 - 690 (Coupling device), DC 0 - 230, DC 0 - 1000 (Coupling device) V
- Auxiliary voltage: AC/DC
- Earth fault indicator: Yes
- Approvals: UL-Recognized
- Response value type: Adjustable
- Bus interface: Modbus RTU
- Enclosure design: Distribution board
- Type: RN 5893
| EVerest supports this device with the "DoldRN5893" module.
The module requires a "SerialCommHub" module to be loaded as well for
the ModBus communication.
| All settings of the device can be adjusted in the module
configuration.
---------------
NF/RFID Readers
---------------
Many NXP chips are be supported.
All modules implement the *auth_token_provider* interface and publish a
token to be consumed by matching modules as soon as the NFC chip detects
a compatible RFID card.
NxpNfcFrontendTokenProvider
---------------------------
*Hardware Driver Module* :ref:`NxpNfcFrontendTokenProvider <everest_modules_NxpNfcFrontendTokenProvider>`
The variety of hardware supported by the underlying NxpNfcFrontendWrapper
is limited by the time of writing (only CR663), but can be extended.
This module relies on NXP's proprietary NxpNfcRdLib which users need
to obtain from NXP, due to license reasons (Download is free, but requires
accepting the license terms).
PN532TokenProvider
------------------
*Hardware Driver Module* :ref:`PN532TokenProvider <everest_modules_PN532TokenProvider>`
Supports the PN532 integrated tranceiver.
PN7160TokenProvider
-------------------
*Hardware Driver Module* :ref:`PN7160TokenProvider <everest_modules_PN7160TokenProvider>`
Supports the PNC7160 NFC Controller via the NCI interface.
No Linux kernel module required.
--------------------
AC/DC power supplies
--------------------
Huawei R100040Gx
----------------
*Hardware Driver Module* :ref:`Huawei_R100040Gx <everest_modules_Huawei_R100040Gx>`
The device is supported by EVerest with the "Huawei_R100040Gx" module.
Most important specs:
- 40 kW ACDC with 150 V - 1000 V output
- low noise fan design
- ultra compact
- automatic switching between series and parallel mode
- stackable
- CAN bus interface
In the driver configuration, set the addresses of the modules that are
used by this driver. If empty (default), it will use all modules that it
can find on the CAN bus.
If using multiple modules in a stacked configuration, connect the
outputs in parallel and connect all modules to the same CAN bus. Then
specify all module addresses in the module configuration.
Huawei V100R023C10
------------------
*Hardware Driver Module* :ref:`Huawei_V100R023C10 <everest_modules_Huawei_V100R023C10>`
This device is supported in EVerest with the Huawei production firmware.
The setup of this device is complex.
The development kit with unencrypted firmware is not supported by this driver.
Most important specs:
- Central power unit architecture with satellites, up to 740 kW
- 150 V - 1000 V output, up to 12 satellites
- Ethernet communication with EVerest
- Support for multiple connectors on one satellite
- Full support for production firmware with TLS encryption and GOOSE
security
Infypower BEG1K075G
-------------------
*Hardware Driver Module* :ref:`InfyPower_BEG1K075G <everest_modules_InfyPower_BEG1K075G>`
Supported by EVerest with the "InfyPower_BEG1K075G" module. Stacking of
multiple modules is not yet supported by the driver.
Most important specs:
- 22 kW bidirectional AC/DC
- up to 1000 V output voltage
Make sure to update the module to the latest firmware version - older
firmware versions on this converter are known to have bugs that could
result in hardware damages. New firmware is available from InfyPower.
UUGreenPower UR1000X0
---------------------
*Hardware Driver Module* :ref:`UUGreenPower_UR1000X0 <everest_modules_UUGreenPower_UR1000X0>`
Both the 30 kW and 40 kW uni-directional modules from UUGreenPower are
fully supported by EVerest with the "UUGreenPower_UR1000X0" module.
The bidirectional versions are not yet supported, but support is planned
for an upcoming release of EVerest.
Most important specs:
- 30 / 40 kW AC/DC
- up to 1000 V output voltage
- automatic series / parallel switching implemented in driver. Can be
fixed to series or parallel mode in configuration.
If multiple modules are used in a stacked configuration, you must set
the "module\\addresses" configuration parameter to the
addresses of all modules in the stack.
By default, it uses the broadcast address. With multiple modules, this
will result in each module delivering the full current to the EV instead
of sharing the current.
Other AC/DC power supplies
--------------------------
- :ref:`DPM1000 <everest_modules_DPM1000>`
- :ref:`InfyPower <everest_modules_InfyPower>`
- :ref:`Winline <everest_modules_Winline>`
------------
Power meters
------------
DC: LEM DCBM400/600
-------------------
*Hardware Driver Module* :ref:`LemDCBM400600 <everest_modules_LemDCBM400600>`
This power meter is fully supported by EVerest a (LemDCBM400600)
driver.
It supports German Eichrecht regulations and Eichrecht-compliant fault
recovery:
- After power failure of the complete unit, the transaction is closed
with the correct signed meter value from the moment the power loss
happened. It is then also closed in the CSMS if OCPP is used.
- If a communication loss happens during charging, charging is stopped.
If the communication is re-established before the EV unplugs, the
signed meter value is used to close the transaction in the CSMS. If
it does not re-establish before the EV unplugs, the transaction
cannot be billed (and no signed meter value will be used to close the
CSMS transaction).
Version V2 is required to really be Eichrecht-compliant. The driver
auto-detects V1 and V2 hardware versions and supports both.
DC: Acrel DJSF1352
------------------
*Hardware Driver Module* :ref:`Acrel_DJSF1352_RN <everest_modules_Acrel_DJSF1352_RN>`
This is a simple DC power meter with no Eichrecht support. It uses
ModBus/RS485 and requires a SerialCommHub module to work.
DC: AST DC650
-------------
*Hardware Driver Module* :ref:`AST_DC650 <everest_modules_AST_DC650>`
The driver is implemented in the "AST_DC650" module using the SLIP
protocol. Eichrecht support is not complete in the driver in the current
release for all fault cases, but this will come in an upcoming release.
There is a possibility to use it with a REST-based API similar to the
LEM.
DC: DZG GSH01
-------------
*Hardware Driver Module* :ref:`DZG_GSH01 <everest_modules_DZG_GSH01>`
The driver is implemented in the "DZG GSH01" module using the SLIP
protocol.
Note that you need an extra serial port - it cannot be shared with
e.g. other ModBus-based devices.
Eichrecht and OCMF handling are fully implemented.
DC: Isabellenhütte IEM-DCC
--------------------------
*Hardware Driver Module* :ref:`IsabellenhuetteIemDcr <everest_modules_IsabellenhuetteIemDcr>`
There is a driver for the Isabellenhütte IEM-DCC meter in EVerest.
AC: GenericPowermeter
---------------------
*Hardware Driver Module* :ref:`GenericPowermeter <everest_modules_GenericPowermeter>`
The GenericPowermeter driver has support for most ModBus-based AC power
meters. It supports a yaml configuration file for register mapping to
adapt to new power meters.
Example register mappings are included for the following power meters:
- Eastron SDM72DM, SDM72DM-V2, SDM230, SDM630-V2
- Klefr 693x - 694x
For all non-Eichrecht power metering, this should be easy to adapt.
AC: Iskra WM3M4 & WM3M4C
------------------------
*Hardware Driver Module* :ref:`RsIskraMeter <everest_modules_RsIskraMeter>`
There is a community driver in the "RsIskraMeter" module. Eichrecht
support is implemented, some fault cases may require additional
handling.
-----------------
Power line modems
-----------------
The following power line modems are supported in the
:ref:`EvseSlac module <everest_modules_EvseSlac>`.
The chip is detected automatically, but each chip may require some
configuration for a real product.
Qualcomm QCA7000/7005/7006
--------------------------
Fully supported including proprietary extensions for link detection and
chip reset.
It is recommended to enable "do_chip_reset" for all Qualcomm chips. This
will reset them after each charging session for improved stability.
If it is booting from the internal flash and correctly configured for
EVSE, it only requires the SPI Linux kernel driver to be loaded (which
is included in the Linux main line).
If it is configured for host boot, additional software outside of
EVerest is required to do firmware loading and configuration for EVSE.
If connected via the Ethernet port on QC7006, no special driver is
needed in Linux (except for the Ethernet interface driver).
Lumissil CG5317
---------------
Fully supported including proprietary extensions for link detection.
Soft reset extensions are not yet in the release, but should not be
necessary for this chip if the latest firmware is being used. Contact
Lumissil to ensure you have the latest firmware.
If it is booting from an externally attached SPI Flash (attached to the
CG5317) and correctly configured for EVSE, it only requires the SPI
Linux kernel driver to be loaded. The kernel driver is open source
licensed but not included in the main line.
If it is configured for host boot, additional software outside of
EVerest is required to do firmware loading and configuration for EVSE.
You can get these tools under NDA from Lumissil.
If connected via the Ethernet port, no special driver is needed in Linux
(except for the Ethernet interface driver).
Vertexcom MSE-102x
------------------
It is auto-detected and supported by the EvseSlac module. Proprietary
extensions for link detection and reset are not yet implemented.
It may be used without those extensions.
If connected via the Ethernet port, no special driver is needed in Linux
(except for the Ethernet interface driver).
----------------------------
BSP for complete controllers
----------------------------
Some controllers are supported natively by EVerest and require no
additional work for Control Pilot, PLC, relay switching etc.
PHYTEC phyVERSO controller
--------------------------
*Hardware Driver Module* :ref:`PhyVersoBSP <everest_modules_PhyVersoBSP>`
phyVERSO is fully supported with all features of the hardware for both
charging ports (AC and DC).
Pionix test hardware
--------------------
All testing hardware from Pionix is fully supported:
- BelayBox
- uMWC
Other BSP modules
-----------------
- :ref:`AdAcEvse22KwzKitBsp <everest_modules_AdAcEvse22KwzKitBsp>`
- :ref:`TIDA010939 <everest_modules_TIDA010939>`
- :ref:`YetiDriverBsp <everest_modules_YetiDriver>`

View File

@@ -0,0 +1,77 @@
.. exp_high_level_overview:
##############################
High-Level Overview of EVerest
##############################
EVerest is the open source firmware stack for EV charging stations. By digitally abstracting
the complexity of multiple standards and use cases, EVerest runs on any device, from unmanaged
AC home chargers to complex multi-EVSE satellite public DC charging stations with battery and
solar support. EVerest supports all the standards and protocols needed for standards-compliant,
interoperable and secure charging. In other words, EVerest does the hard work of translating
standards into working code to ensure that every car works with every charger with every
charging app and network.
Key features of EVerest
========================
All in all, you can expect the following:
* Modular and extensible architecture
* Support for AC and DC charging
* Support for EV charging protocols
* OCPP 1.6, OCPP 2.0.1 and OCPP 2.1
* ISO 15118-2, -3 and -20
* IEC 61851
* DIN SPEC 70121
* Ready-to-use hardware drivers for many compatible hardware components
* BSPs for charge controllers
* Powermeters
* Isolation monitors
* DC Power supplies
* RFID/NFC readers
* Payment terminals
* Energy management implementations and API
* Standardized and stable APIs to allow easy integrations
* Bring-up modules for custom hardware testing and integration
* Ensured standards compliance
* OTA service to keep EV chargers up-to-date
* Security best practices following OpenSSF
* ISO / IEC 5230 open source license compliance
* Secure communication channels through TPM
* Yocto support for custom embedded Linux images
EVerest Architecture
=====================
EVerest contains a rich set of modules that can be combined to build a full EV charging station software stack.
The architecture is modular and based on loosely coupled components that communicate via MQTT.
.. image:: images/0-2-everest-top-level-diagram-module-communication.png
Each module runs as an independent process and communicates with other modules via well-defined interfaces.
This allows module to subscribe to variables published by other modules and to call commands provided by other modules.
A more detailed explanation of the EVerest architecture and module concept can be found in the
explaination about :doc:`EVerest modules in detail </explanation/detail-module-concept>`.
Hardware Requirements
=============================
EVerest can run on any Linux-based operating system that comes with the required dependencies.
Our (strong) recommendation is Yocto.
Find more information on how to set up your Yocto-based environment in the respective
:doc:`EVerest Linux and Yocto guides </explanation/linux-yocto/index>`.
The hardware requirements to run EVerest very much depend on the use case and the modules
that are used in the specific scenario. As a general guideline, the following minimum
requirements should be met:
* CPU: minimum imx6ULL or comparable, recommended is imx93 or AM62x
* RAM: minimum of 512 MB, recommended is 1 GB or more
* Flash: minimum of 1 GB, recommended is 4 GB or more
Getting Started
=====================
Please refer to the :doc:`Quick Start Guides </how-to-guides/getting-started/get-started-sw>` to get started with EVerest.

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

@@ -0,0 +1,31 @@
sequenceDiagram
autonumber
participant Powermeter
participant EvseManager
title Start of Powermeter or recovery after communication loss
Note over Powermeter: Device communication (re)established
Powermeter->>Powermeter: Request status from device
Powermeter->>Powermeter: Detects a running transaction
Powermeter->>Powermeter: Mark need_to_stop_transaction to true
alt Next command is startTransaction
EvseManager->>Powermeter: startTransaction
Powermeter-->>Powermeter: stopTransaction
Note over Powermeter: internal triggered stopTransaction will not send <br>a response to EvseManager since no stopTransaction was issued
Powermeter->>Powermeter: Mark need_to_stop_transaction to false
Powermeter-->>EvseManager: startTransaction Response (OK/ID)
Powermeter->>Powermeter: Mark need_to_stop_transaction to true
Note over EvseManager: Transaction started successfully
else Next command is stopTransaction
EvseManager->>Powermeter: stopTransaction
Powermeter-->>EvseManager: stopTransaction Response (OK/OCMF)
Powermeter->>Powermeter: Mark need_to_stop_transaction to false
end
Note over Powermeter: In case of CommunicationError during start/stop<br> transaction please check the start/stop transaction diagrams

View File

@@ -0,0 +1,50 @@
sequenceDiagram
autonumber
participant Powermeter
participant EvseManager
participant OCPP
participant CSMS
title Start of a Transaction
Note over EvseManager: User plugs in EV and authorizes
EvseManager->>OCPP: Event(SessionStarted)
OCPP->>CSMS: StatusNotification.req(Preparing)
CSMS-->>OCPP: StatusNotification.conf
alt successful case
EvseManager->>Powermeter: startTransaction
Powermeter-->>EvseManager: startTransaction Response (OK/ID)
EvseManager->>OCPP: Event(TransactionStarted)
OCPP->>CSMS: StartTransaction.req
CSMS-->>OCPP: StartTransaction.conf
Note over EvseManager: Transaction started successfully
else startTransaction failing due to power loss
EvseManager->>Powermeter: startTransaction
Powermeter-->>EvseManager: startTransaction Response (FAIL)
EvseManager->>OCPP: Event(Deauthorized)
OCPP->>CSMS: StatusNotification.req(Finishing)
CSMS-->>OCPP: StatusNotification.conf
EvseManager->>OCPP: raiseError (PowermeterTransactionStartFailed)
OCPP->>CSMS: StatusNotification.req(Finishing, PowermeterTransactionStartFailed)
CSMS-->>OCPP: StatusNotification.conf
Note over EvseManager: Transaction did not start
end
alt EvseManager configured to become inoperative in case of Powermeter CommunicationError
Powermeter->>EvseManager: raise_error(CommunicationError)
Note over Powermeter,EvseManager: Powermeter raises a CommunicationError <br/>and EvseManager is registered for notification
EvseManager->>OCPP: raise_error (Inoperative)
OCPP->>CSMS: StatusNotification.req(Faulted)
CSMS-->>OCPP: StatusNotification.conf
end

View File

@@ -0,0 +1,45 @@
sequenceDiagram
autonumber
participant Powermeter
participant EvseManager
participant OCPP
participant CSMS
title Stopping Transaction in Error
Note over Powermeter, CSMS: Transaction is running
Powermeter->>Powermeter: detects a <br/> CommunicationError
Note over Powermeter,EvseManager: Powermeter raises a CommunicationError <br/>and EvseManager is registered for notification
Powermeter->>EvseManager: raise_error (CommunicationFault)
Powermeter->>OCPP: raise_error (CommunicationFault)
OCPP->>CSMS: StatusNotification.req(Charging, CommunicationFault)
CSMS-->>OCPP: StatusNotification.conf
alt EvseManager configured to become inoperative in case of PowermeterCommError
EvseManager->>EvseManager: Pause charging
EvseManager->>OCPP: raiseError (Inoperative)
OCPP->>CSMS: StatusNotification.req(Faulted)
Note over EvseManager: Note that we would just continue charging otherwise
end
Note over Powermeter, CSMS: User stops the transaction
alt successful case (Powermeter has no CommunicationError)
EvseManager->>Powermeter: stopTransaction (ID)
Powermeter-->>EvseManager: stopTransaction Response (OK/OCMF)
EvseManager->>OCPP: Event(TransactionFinished(OCMF))
OCPP->>CSMS: StopTransaction.req(OCMF)
CSMS-->>OCPP: StopTransaction.conf
else stopTransaction failing due to subsequent power loss (this applies as well when Powermeter still in CommunicationError)
EvseManager->>Powermeter: stopTransaction (ID)
Powermeter->>EvseManager: stopTransaction Response (FAIL)
EvseManager->>OCPP: Event(TransactionFinished)
Note right of OCPP: In this case we can't stop the transaction including the OCMF
OCPP->>CSMS: StopTransaction.req()
CSMS-->>OCPP: StopTransaction.conf
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -0,0 +1,117 @@
###########
Explanation
###########
The explanation pages will give you detailed information about the features of
EVerest.
Let us have a look at the most important topics first.
Below that, you will be presented with a categorized list of all articles.
.. grid:: 1 2 2 3
:gutter: 2
.. grid-item-card:: Framework Overview
:link: high-level-overview
:link-type: doc
Get a high-level overview of the EVerest framework.
.. grid-item-card:: Error Framework
:link: error-framework
:link-type: doc
How to communicate error states between modules.
.. grid-item-card:: EVerest Modules in Detail
:link: detail-module-concept
:link-type: doc
Learn about the module concept of EVerest.
.. grid-item-card:: Tier Module Mapping
:link: tier-module-mappings
:link-type: doc
EVerest's 3-tier module mapping explained.
.. grid-item-card:: Adapt EVerest
:link: adapt-everest/index
:link-type: doc
Learn how EVerest can be adapted to your use-case.
.. grid-item-card:: The EVerest Dependency Manager
:link: dev-tools/edm
:link-type: doc
Tool helping to orchestrate dependencies between the different EVerest repositories.
.. grid-item-card:: The ev-cli Development Tool
:link: dev-tools/ev-cli
:link-type: doc
Command line tool to generate C++ code from interface and manifest definitions.
.. grid-item-card:: The Plug&Charge Process in EVerest
:link: pnc-process
:link-type: doc
Learn how Plug&Charge is implemented in EVerest.
.. grid-item-card:: Linux / Yocto and EVerest
:link: linux-yocto/index
:link-type: doc
Learn how to integrate EVerest in your embedded application via Yocto and allow for secure OTA updates.
.. grid-item-card:: Hardware Architecture
:link: hardware-architecture
:link-type: doc
Some ideas and guidance on the general architecture of AC or DC chargers.
.. grid-item-card:: Powermeter OCMF Handling
:link: powermeter-ocmf
:link-type: doc
How OCMF records are expected to be handled by modules implementing powermeters.
.. grid-item-card:: A Selection of included Hardware Drivers
:link: hardware-drivers
:link-type: doc
Description of natively supported hardware driver modules included in EVerest.
.. grid-item-card:: Structure of the EVerest Documentation
:link: the-everest-documentation
:link-type: doc
How this documentation is structured.
.. grid-item-card:: Use the EVerest Development Container
:link: devcontainer-internal/index
:link-type: doc
Internal working of the EVerest development container
for different setup variants and how things are connected.
.. toctree::
:hidden:
:maxdepth: 1
high-level-overview
error-framework
detail-module-concept
tier-module-mappings
adapt-everest/index
energymanagement/index
dev-tools/edm
dev-tools/ev-cli
pnc-process
linux-yocto/index
hardware-architecture
hardware-drivers
the-everest-documentation
powermeter-ocmf
devcontainer-internal/index

View File

@@ -0,0 +1,19 @@
.. _exp_linux_yocto:
#########################
Linux / Yocto
#########################
You will find different explanations about using EVerest with
Linux and Yocto in the following sections:
.. toctree::
:maxdepth: 1
setting-up-linux-os
building-yocto
ota-updates
partitioning-schemes-for-rauc-ota
Make sure to check out the :doc:`How-to-guide on cross-compilation </how-to-guides/yocto-cross-compilation>`
for a step-by-step guide on how to cross-compile EVerest for a Yocto-based Linux system.

View File

@@ -0,0 +1,248 @@
.. _exp_linux_yocto_ota_updates:
##########################
Over-the-air-updates (OTA)
##########################
One of the most important (and often underestimated) features of a
charging station is the ability to remotely update the software when the
charger is installed. Updates can provide:
- General bug fixes
- Fixing compatibility issues with new EVs (or old EVs with new
firmware versions)
- Fixing compatibility issues with OCPP backends (or new versions
deployed on the backend side)
- Security issues
- New features
Updates may be delivered remotely over a network, called Over-the-Air (OTA),
or may be provided locally where supported by the charging station.
EVerest supports *RAUC* as an update tool, which has the following advantages:
- Open source project with a large community:
https://rauc.io
- Secure by design: The update files are cryptographically signed (and
optionally encrypted). Signature is checked during installation, so
the source of the update file can be trusted. This simplifies the
update delivery process a lot compared to other tools that only rely
on transport mechanism security. Updates can be downloaded from a
simple unencrypted HTTP server or even a local USB flash drive
without compromising security.
- Robust: Uses A/B partitioning and does full image updating
- Atomic switching between A/B slots can be implemented
- Support partial downloads by HTTP streaming: Block based partial
downloads reduce the bandwidth needed
There are some considerations to make when choosing an update system:
+-----------------------------------+-----------------------------------+
| Full image updates | Partial component / individual |
| | file updating |
+===================================+===================================+
| Very robust. The complete image | Risk of producing an installed |
| always has the correct | combination where one component |
| dependencies built in. | is too old to work with the other |
| | recently updated component. |
| | Requires careful tracking of |
| | compatibility between components. |
+-----------------------------------+-----------------------------------+
| Writing full images to A/B slots | Often quite complex |
| is straightforward. Combined with | implementations. That can |
| an atomic switch between the | introduce a lot of room for bugs |
| boot slots, there is no critical | which brick devices during failed |
| time where e.g. a power loss | updates, power losses during |
| could brick a device. | updates or upgrading to |
| | incompatible updater software |
| | versions. |
+-----------------------------------+-----------------------------------+
| Simple versioning: a single | Complex versioning: Always a |
| version number is enough to | combination of the different |
| specify which software image | components / files. |
| version is installed. | |
+-----------------------------------+-----------------------------------+
| Recovers from file system errors | Relies entirely on the filesystem |
| in the root partition: It writes | implementation to repair itself |
| a new clean FS on every update | and may brick if that fails. |
+-----------------------------------+-----------------------------------+
| Updates everything: rootfs, | Often limited to e.g. application |
| kernel, bootloader, … | update. It may e.g. not update |
| | kernel or base system. |
+-----------------------------------+-----------------------------------+
| Downside: Full image updates | Advantage: only download changed |
| require more download | files and thus have the smallest |
| bandwidth/data. Can be mitigated | possible download. |
| to some extent by block based | |
| partial download. | |
+-----------------------------------+-----------------------------------+
An update process should consider the bootloader, loading the Linux kernel, and
the root file system. A root file system can be a standard Linux partition (ext4).
Other solutions are available including: squashfs, file system snapshots, and
bundle based solutions (NixOS, Snap). The root file system is usually read-only
and an overlay file system is used to support charger specific updates.
An OTA solution needs to consider how configuration information is maintained
across root file system updates.
EVerest has chosen RAUC as the most suitable update system, mainly due to its
robust, brick-free mechanisms and its inherent security features.
RAUC can support adaptive updates that use HTTP streaming to only download
blocks that have changed between releases. This can reduce the overheads of using
full images.
Security is provided on a block-based level, so there is no need to
first download the complete image and validate signature etc. It is done
on the fly.
This also means that no extra disk space is needed to store the update
image: It will be directly streamed from the source into the inactive
slot partition.
RAUC implementation in EVerest
------------------------------
EVerest interacts with RAUC via its D-Bus interface. This is provided by the
`Linux Systemd Rauc module </reference/modules/Linux_Systemd_Rauc>`_.
In EVerest the update process is fully integrated with OCPP.
In the OCPP use case, the CPO will need to provide storage for the
update file that is accessible via HTTP with range requests. The CSMS
then sends this URL in the update request to EVerest, and EVerest will
trigger RAUC on the D-Bus to actually perform the update.
You will need to implement the following in your Yocto system as this is
very system dependent:
- A partitioning setup that provides A/B slots for rootfs, A/B boot and
data partitions (more details about this is covered in the appendix!)
- RAUC configuration file for your setup (system.conf)
- RAUC backend that performs switching slots/marking good/bad. RAUC
already comes with backend support for many bootloaders such as
U-Boot and Barebox etc.
- PKI to be used for signing / optionally encrypting the update files
- A recipe that builds RAUC bundles (update files) directly in Yocto
If you use PHYTEC SoMs: Their *ampliPHY* distribution already has working
examples for all of the above in *ampliphy-rauc* or *ampliphy-secure*
distributions.
Refer to RAUC's integration documentation for more information:
https://rauc.readthedocs.io/en/latest/integration.html
RAUC has support for atomic switching between slots and uses features from
the bootloader. It is important to understand this interaction since the
bootloader may be able to automatically rollback if an update is not successful.
Some processors also support secure and encrypted boot options which can ensure
that only valid images are loaded. They may also provide mechanisms to support
dual boot loaders.
.. tip::
Look at the documentation for your processor and chosen bootloader to
understand what options are provided for slot switching and automatic boot
failure recovery.
Test your integration locally first using RAUC on the command line:
.. tip::
rauc install http://myudateserver.com/version1.raucb
RAUC should perform a successful installation on the currently unused
slot. Once that is done, issue a reboot and verify it cleanly boots into
the new slot.
Once booted successfully into the new slot, you need to mark the slot as
“good”, otherwise it may fall back to the previous one on the next
boot.
Some implementations do this in a *systemd* service that runs at the end
of the boot process. This is not recommended in production. EVerest
will take care of marking the slot as "good" when EVerest starts up
successfully. It will then also report the status to the OCPP backend
automatically etc.
To mark it "good", manually use:
.. code-block:: bash
rauc status mark-good
You also may want to check RAUC's status before and after the update to
verify it is configured correctly. It shows an output like this:
.. code-block:: bash
root@mysystem:~# rauc status
=== System Info ===
Compatible: mysystem-v1
Variant:
Booted from: rootfs.0 (system0)
=== Bootloader ===
Activated: rootfs.0 (system0)
=== Slot States ===
[bootloader.0] (/dev/mmcblk1, boot-emmc, inactive)
o [rootfs.1] (/dev/mmcblk1p6, ext4, inactive)
bootname: system1
boot status: good
[boot.1] (/dev/mmcblk1p2, vfat, inactive)
x [rootfs.0] (/dev/mmcblk1p5, ext4, booted)
bootname: system0
mounted: /
boot status: good
[boot.0] (/dev/mmcblk1p1, vfat, active)
Also try to use *mark-bad* and test if it falls back to the previous one
on the next boot.
EVerest interacts with RAUC via D-Bus, so make sure it is running as a
D-Bus service. The D-Bus interface is also the boundary between
EVerest and the underlying Linux system here.
Once you verified that RAUC performs updating and fall-backs in manually
controlled command line mode, you should be all set up for EVerest
updates.
Custom Update Mechanism
------------------------
In case you do not want to use RAUC and/or integrate your custom update
mechanism into EVerest, you can also implement the
`EVerest System API <../../reference/api/system_API/index.html>`_.
This would still allow you to update EVerest via OCPP, but you would need to handle
the actual update process yourself and provide status updates to EVerest via the
System API.
Optimize the base system
------------------------
If you have a lot of processes running in the Linux system and a very
high CPU load (which easily happens on small embedded systems), take
some time to select the correct nice levels for all services running on
the system. You can set the nice level in the systemd unit files.
.. tip::
Being "nicer" means getting CPU less often if lots of processes are scheduled.
Especially for high-level communication (aka ISO 15118), run EVerest at
e.g. a nice level of -20 to ensure it is getting enough CPU slices
during the charging process. If you have other tasks outside of
EVerest, make sure they have a higher nice level.
Using a preemptive kernel is also a good idea to ensure low latencies in
user space. Check *CONF_PREEMT* documentation in the Linux kernel.
--------------------------------
**Authors**: Cornelius Claussen, Manuel Ziegler, Piet Gömpel

View File

@@ -0,0 +1,61 @@
.. _partitioning-schemes-for-rauc-ota:
#################################
Partitioning schemes for RAUC OTA
#################################
As there are many ways to set up partitions on the storage device for
RAUC-based updates, this chapter will only provide a few ideas. Your
actual implementation may be different in the end.
As a target, we would like to have:
- two full-size A/B rootfs partitions
- two full-size A/B boot partitions for bootloader and FIT image
(containing kernel/initrd/device tree for secure boot)
- one/two user data partition(s)
- one small factory data partition that contains (read only) files that
are programmed once during production and will never change
(e.g. serial numbers, certification region config etc)
The user data partition can be mounted as overlayfs on specific folders
to store run time-generated data (e.g. log files, user configuration
files, certificates, ...).
A factory reset should be implemented that clears the overlayfs.
The most simple version of this is to use a single user data partition
and mount it as overlay (e.g. on */var*) both for slot A and B. Then all
changes in the overlay will survive an update of the underlying rootfs.
For an example on how to do this, refer to the BelayBox Yocto sources.
The Raspberry Pi uses three boot partitions, for most other boards only
two are needed.
.. code-block:: shell
part --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4096 --fixed-size 512
part --source bootimg-partition --ondisk mmcblk0 --fstype=ext4 --label boot_a --align 4096 --fixed-size 512
part --source bootimg-partition --ondisk mmcblk0 --fstype=ext4 --label boot_b --align 4096 --fixed-size 512
part --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root_A --align 4096 --fixed-size 3000
part --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root_B --align 4096 --fixed-size 3000
part --ondisk mmcblk0 --fstype=ext4 --label factory_data --align 4096 --fixed-size 128
part --ondisk mmcblk0 --fstype=ext4 --label overlay --align 4096 --fixed-size 7000
The disadvantage of this is the following: If the configuration file
format changes due to an update of the underlying rootfs, a separate job
may need to be run on the first boot into the new slot to transfer the
configuration files to the new format. If the boot into the new slot
fails, it will fall back to the old slot. The older version then is
maybe not compatible with the new config file format, so a full fallback
is not possible in this case.
To allow for better config file migration with fallback, consider to use
two user data partitions and a separate migration task (e.g. in initrd)
that transfers the files from the old user data partition to the new
one. It may adapt the file format in the process. In this case, falling
back to the old rootfs will work as it will also use the older overlay
user partition.
If you have an eMMC device, consider using the hardware boot partition
feature that eMMC offers for the bootloader. This will enable atomic
switching between the active boot slots.

View File

@@ -0,0 +1,105 @@
.. _exp_linux_yocto_setup_linux_os:
#####################################
Setting up the Linux operating system
#####################################
In principle, you can use any Linux-based operating system as long as it
comes with the required dependencies to run EVerest.
We strongly recommend using Yocto as it has some advantages over other
distributions:
- It can be set up to do reproducible builds with versioning.
- Most CPUs and SoMs already come with Yocto board support packages
(BSP).
- EVerest comes with full support for selected Yocto long term support
releases (LTS) (scarthgap as of the time of writing).
- It can be nicely integrated with your CI/CD to build complete
production images and update packages.
- It provides a software bill of materials of all packages in the Linux
system for managing licenses.
- Broad community
- Automatic generation of cross-compile toolchains, that can be used
during the development phase.
You can find more information about the Yocto project here:
https://www.yoctoproject.org
.. warning::
Setting up the Linux base system for your product is a quite complex task
that should be performed by domain experts. In case you do not have experts
in your team, consider getting help from a company specialized on this.
The end product's reliability, security and user experience strongly depends
on a sound architecture, implementation and maintenance strategy of the base
Linux system. This should not be underestimated.
Covering all aspects of setting up a Linux base system is out of the
scope of this documentation, but we would like to give some examples and ideas
and point out some typical solutions to questions you will have on your
journey. Do not consider this complete!
Setup a Yocto build environment
-------------------------------
Yocto has comprehensive caching capabilities that mean build times are substantially
reduced for successive builds. However an initial build will take hours since initial
versions need to be fetched and built so that caches are populated. There is support
for sharing downloads and caches that can reduce build times and are worth
considering where you have a co-located team.
A good build machine will have lots of RAM, SSD storage and multi-core processor as
well as a fast Internet connection.
It is possible to use a high-performance laptop especially for incremental builds
once the initial build is complete.
.. warning::
Running this inside of a virtual machine is not recommended.
A full Yocto build easily requires 50-100 GB of disk space, and it will
use multiple cores. So, make sure you have enough RAM per core (e.g. 2-3
GB per (hyperthread) core).
Install a Linux distribution supported by Yocto and install all
necessary dependencies. See here for more information about that:
https://docs.yoctoproject.org/ref-manual/system-requirements.html
Alternatively, consider building in a container. Once you move to
production, a build container will probably be needed anyway to build
images in your CI/CD.
It is also recommended to archive the containers to be able to do fully
reproducible builds of older versions in the future.
Yocto itself can produce builds that are completely tagged (i.e. each
source package is tagged with a fixed version or Git hash), so they are
in principle reproducible.
There are - however - a few build dependencies to the host system that
may prevent you from building your released 1.0 version in ten years
from now. As an example, the Python version in ten years from now may
not run the old bitbake correctly anymore. Also, the Yocto recipes
contain only download URLs and version tags, but not the source packages
itself.
Let's start with an example and set up the Yocto build environment that
we use for EVerest on the BelayBox hardware.
Building the BelayBox Yocto image
---------------------------------
An example can be found here for the BelayBox:
https://github.com/PionixPublic/dev-hardware-yocto
Check out the *README* in this repository on how to build and install
this Yocto on the BelayBox.
--------------------------------
**Authors**: Cornelius Claussen, Manuel Ziegler

View File

@@ -0,0 +1,149 @@
.. _exp-pnc-process:
##################################
The Plug&Charge Process in EVerest
##################################
This is an explaination how Plug&Charge is technically implemented in EVerest.
For a tutorial on how to do Plug&Charge in the EVerest software-in-the-loop, please refer to
the :doc:`Plug&Charge tutorial </tutorials/plug-and-charge>`.
For a goal oriented how-to-guide, pleaser refer to :doc:`/how-to-guides/configure-pnc`.
*************************
Plug&Charge Authorization
*************************
There are a lot of resources available on Plug&Charge and ISO15118 PKI involved in this process,
so this guide is not going to repeat how Plug&Charge actually works.
It rather explains what EVerest provides with respect to Plug&Charge and how EVerest needs to
be configured in order to suit your Plug&Charge use case.
************************************
The Authorization process in EVerest
************************************
In essence, the Plug&Charge Authorization runs like any other authorization in EVerest,
like local RFID authorization or remote authorization. Have a look at how the authorization
process in EVerest in designed within the :ref:`Documentation of the Auth module <everest_modules_handwritten_Auth>`.
************************
Involved EVerest modules
************************
The E2E Plug&Charge process involves communication from the EV to systems in the cloud. The
main protocols involved are ISO15118 and OCPP. In EVerest, several modules and interfaces
are involved in the Plug&Charge process. Here is an overview of how everything comes together
in EVerest:
.. image:: images/plug_and_charge_modules.png
:align: center
.. note::
This visualization only presents the interfaces and connections between them that are
relevant for Plug&Charge.
Let's have a look step by step:
Step 0
======
Before a Plug&Charge session can start, the following certificates and keys should be installed on
the charger:
* V2G Root certificate
* SECC Leaf certificate
* SECC Leaf private key
* MO Root certificate (optional)
These certificates and keys can be installed during provisioning of the charger, or they can be
installed using OCPP1.6 or OCPP2.x. The paths to store these files can be configured in the
EvseSecurity module. Please see the :ref:`Documentation of the EvseSecurity <everest_modules_EvseSecurity>`
for further information on how to do the configuration for this module.
In the visualization, step (0) shows the process that represents the previously described process of
provisioning the charger with the correct certificates, before there is a physical
connection to the EV. The OCPP/OCPP201 and EvseV2G module require a module that implements
the :doc:`evse_security interface </reference/interfaces/evse_security>`,
in order to execute the following commands:
* install_ca_certificate (Used by OCPP to install root certificates. This process is initiated by the OCPP CSMS)
* update_leaf_certificate (Used to install or update SECC leaf certificates)
* generate_certificate_signing_request (Used to generate a CSR that is used in the SignCertificate.req of OCPP)
* verify_certificate (Used by EvseV2G to verify the contract certificate and by OCPP to verify new leaf certificates)
* get_mo_ocsp_request_data (Used by EvseV2G and OCPP to get the OCSP request data of the contract certificate (chain))
There are more commands provided by the :doc:`evse_security interface </reference/interfaces/evse_security>`,
which are not included in the Plug&Charge process.
For a successful Plug&Charge authorization process, the following certificates need to be installed on the charger:
* SECC leaf certificate (including sub cas)
* V2G Root Certificate(s)
* MO Root Certificates(s) (only if the EV contract shall be verified locally).
This can be controlled by the OCPP configuration keys described in the section
:ref:`how-to-configure-pnc-ocpp-configuration` for more information.
As mentioned above, these certificates can be installed manually or by the CSMS. In case Plug&Charge is enabled
and no (valid) SECC leaf certificate is installed or it expires within the next 30 days, the charging station
will attempt to retrieve a SECC leaf certificate from the CSMS automatically. This process can also be triggered
manually by the CSMS by using a *TriggerMessage(SignCertificate).req* message.
Step 1
======
This step is triggered by a physical connection between the EV and the charger. A TLS connection is required
between the EV and the charger to allow Plug&Charge, so the EvseV2G module retrieves the SECC leaf certificate
chain and private key from via the evse_security.yaml interface and sets up a TLS server, to which the EV
can connect as a TLS client.
Step 2
======
When charger and EV have agreed on Contract being the selected payment option, we have something going on
that we can call a Plug&Charge process. The EV sends its contract certificate chain and requests authorization
from the charger. The EvseV2G module generates a
:ref:`ProvidedIdToken <authorization-ProvidedIdToken>`,
which is the EVerest type that contains data about the authorization request, including the contract
certificate and OCSP request data.
The *ProvidedIdToken* is transmitted via the *evse_manager* interface to the EvseManager module.
Step 3
======
The EvseManager module implements the *token_provider* interface and can therefore publish the
:ref:`ProvidedIdToken <authorization-ProvidedIdToken>`
containing the contract certificate and OCSP data within EVerest to the central authorization module
in EVerest: Auth.
Step 4
======
The Auth module sends commands containing the *ProvidedIdToken* to its registered
:doc:`token_validator(s) </reference/interfaces/auth_token_validator>`,
which are OCPP/OCPP201 in the case of Plug&Charge. The OCPP module(s) validate the token based on the requirements
specified in the OCPP protocol (either validating locally or by the CSMS).
Step 5
======
In case the validation was successful, the Auth module notifies the EvseManager using the authorize command,
that authorization is present and the charging session can be started.
Step 6
======
The EvseManager forwards the authorization response to the EvseV2G module, which can then send the
awaited ISO15118 response to the EV.
.. note::
We have taken some shortcuts and ignored some further communication going on during the full process,
but these steps cover what's important for Plug&Charge in EVerest.
----
**Authors**: Piet Gömpel

View File

@@ -0,0 +1,41 @@
.. _exp-powermeter-ocmf:
########################
Powermeter OCMF Handling
########################
This document explains how EVerest modules implementing the :doc:`powermeter interface </reference/interfaces/powermeter>`
shall handle OCMF report generation and transmission when used in conjunction with the
:ref:`EvseManager module <everest_modules_EvseManager>`.
The following sequence diagrams illustrate the interactions between the involved modules
during the start and stop of a transaction, including error handling scenarios:
- :ref:`Start of a transaction <exp-powermeter-ocmf-start-transaction>`
- :ref:`Stopping transaction in error <exp-powermeter-ocmf-stopping-transaction-error>`
- :ref:`Start of Powermeter or recovery after communication loss <exp-powermeter-ocmf-start-recovery>`
.. _exp-powermeter-ocmf-start-transaction:
Start of a transaction
======================
.. mermaid:: images/ocmf_start_of_transaction.mmd
.. _exp-powermeter-ocmf-stopping-transaction-error:
Stopping Transaction in Error
=============================
.. mermaid:: images/ocmf_stopping_transaction_in_error.mmd
.. _exp-powermeter-ocmf-start-recovery:
Start of Powermeter or recovery after communication loss
========================================================
.. mermaid:: images/ocmf_start_of_pmeter_or_transaction_after_powerloss.mmd
----
**Authors**: Florin Mihut, Piet Gömpel

View File

@@ -0,0 +1,161 @@
.. _exp_the_everest_documentation:
#########################
The EVerest Documentation
#########################
This section explains how different files in different places are compiled
into to html document you are reading. The general structure that is being
aimed for is also explained. If you only read one subsection on this page
it should be
:ref:`Structure of the Documentation <exp_the_everest_documentation_structure_of_doc>`.
If you only want to modify existing documents, this may well be sufficient.
Practical instructions on working on the documentation are located in the
:ref:`How-to section <documenting_everest>`.
.. _exp_the_everest_documentation_structure_of_doc:
******************************
Structure of the Documentation
******************************
Our documentation is structured according to the `Diátaxis <https://diataxis.fr/>`_ framework:
* *tutorials*: Learn by doing through guided practice.
* *how-to guides*: Practical steps to achieve specific tasks.
* *reference*: Technical facts, APIs, and configuration details.
* *explanations*: Conceptual deep-dives and background theory.
**Tutorials** shall allow a new user to successfully do *something*. No concrete
real-world problem needs to be solved at this point (as the Diátaxis authors put
it: a driving lesson is just not about getting from A to B but about the driving
itself). Deep explanations are to be avoided. It's important to provide a
safe route to some success and allow the reader to gain confidence in his developing
practical skills.
**How-to guides** show how real-world problems are solved by giving practical directions.
The target audience are users which already gained some knowledge through other means.
The instructions are practical and serve to achieve specific goals that many users need.
**Reference** material may be worthless to the novice because it requires an
understanding of the EVerest framework and does not give any practical advice.
For users who are familiar with the basics, the reference is a goal-agnostic, precise and
effective source of facts for all the decisions to be made in everydays work.
**Explanations** provide the necessary context and background to understand.
Things learned in *tutorials* and *how-to guides* will often require further
knowledge to be put in a bigger picture and reveal the *why* of many technical
decisions encoded in the EVerest frameworks architecture.
Please keep this framework in mind when contributing new content. We encourage you
to split your contributions into multiple documents that align with the Diátaxis philosophy.
Linking between these documents ensures users have quick access to related material
without cluttering a single page.
By keeping individual documents focused and concise, they become much more readable.
Tutorials, in particular, should remain brief and link to the Explanations section for
deeper background information.
Since this structure was not chosen from the outset, it is quite possible that
some sections of the EVerest documentation do not conform to this structure in
an exemplary manner. These should therefore not be regarded as good examples that
should be followed without further consideration.
************
Source Files
************
EVerest documentation uses Sphinx as documentation generator. As input format,
reStructuredText is used. See here for more information about Sphinx:
https://www.sphinx-doc.org/en/master/
The :ref:`Sphinx Style Guide <everest_doc_sphinx_style_code>` included in this
documentation serves as a reference for the syntax.
.. note::
It is not required to get a deep understanding of Sphinx to create
documentation for EVerest. You can check existing pages and you will
see how easy it is to start documenting. In the end always make sure
the end result (html) looks as intended!
The locations of the source files that make up the documentation you are reading,
are within the `EVerest/EVerest repository <https://github.com/EVerest/EVerest>`_.
.. note::
You will find a number of documentation files that are not part of the documentation you are reading
but still reside inside the `EVerest/EVerest repository <https://github.com/EVerest/EVerest>`_.
See :ref:`below <documenting_everest_doc_near_source_code>`.
Main EVerest Documentation
==========================
This is a coherent documentation that helps you with getting a fast overview
of the EVerest framework, the EVerest tools and also contains some tutorials.
Reference Documentation
=======================
EVerest interfaces, modules, types and the EVerest API contain documentation
as part of their definitions, right inside the corresponding yaml files.
Those files may also contain configuration settings along with short explanations.
In the `EVerest/EVerest repository <https://github.com/EVerest/EVerest>`_:
* ``types/*.yaml``: Definitions of the internal EVerest types for inter-module communication.
This adds to the *reference* section.
* ``interfaces/*.yaml``: Definitions of the internal interfaces for inter-module communication.
This adds to the *reference* section.
* ``modules/.../manifest.yaml``: Definition of the individual modules. This adds to the *reference*
section.
* ``docs/source/reference/EVerest_API``: This specific subfolder contains the definitions of the
*EVerestAPI*. They are transformed to html to become part of the *reference* section.
The generated pages can be found in
:ref:`the reference section of the main documentation <everest_reference>`.
Optionally, EVerest modules can contain additional handwritten documentation.
See next subsection for more information on this.
Handwritten Documentation
=========================
Each module directory can contain additional handwritten documentation.
- ``modules/.../docs/index.rst``: Handwritten explanations for individual modules.
The contents will automatically be hyperlinked from the page containing the
automatically generated reference docs (explained in the subsection before).
It's considered good practice to also link back from the handwritten
text to the auto-generated reference page of the respective module.
As an example, see the auto-generated
:ref:`reference page of the EvseManager <everest_modules_EvseManager>`.
In the second paragraph, you see a link to the detailed handwritten
documentation.
General documentation that is not associated with a specific module:
- ``docs/source``: Find *tutorials*, *how-to-guides* and *explanations* source files here.
.. _documenting_everest_doc_near_source_code:
Documentation Near Corresponding Source Code
============================================
The documentation parts explained up to now are all part of the main EVerest
documentation you are reading right now. Some documentation snippets can also
be found directly in different GitHub repositories of the EVerest organisation.
These are often README.md files stored near the corresponding source code.
Those docs snippest are not being pushed to the EVerest main documentation.
Examples:
- md files in certain places the EVerest repository
- ``docs/README.md``: How to build the documentation you are reading
- ``applications/utils/everest-testing/README.md``: How to use pytest with EVerest
- md/general doc files in other repos (`everest-admin-panel <https://github.com/EVerest/everest-admin-panel>`_,
`ext-switchev-iso15118 <https://github.com/EVerest/ext-switchev-iso15118>`_, ...)

View File

@@ -0,0 +1,95 @@
.. _tier_module_mapping:
**********************
3-tier Module Mappings
**********************
EVerest modules and even individual interface implementations can have mappings
assigned to them. These mappings are inspired by the OCPP 3-tier model and are
available for error handling since `everest-framework v0.16.0 <https://github.com/EVerest/everest-framework/releases/tag/v0.16.0>`_,
which is included in EVerest since `release 2024.7.0. <https://github.com/EVerest/EVerest/releases/tag/2024.7.0>`_.
These mappings are exposed for usage in module code since `everest-framework v0.18.0 <https://github.com/EVerest/everest-framework/releases/tag/v0.18.0>`_,
which is included in EVerest since `release 2024.10.0. <https://github.com/EVerest/EVerest/releases/tag/2024.10.0>`_.
Following an example how a mappping for the EvseManager could look like:
.. code-block:: yaml
connector_1:
module: EvseManager
mapping:
module:
evse: 1
connector: 1
This would result in a mapping of the whole module,
including its implementations for e.g. evse and token_provider to "evse = 1"
and "connector = 1".
By default, a module is mapped to the whole charging station.
So to ensure that only the parts of the module that should belong
to a specific evse/connector are actually mapped to it,
you could replace this simple mapping with a more detailed one
as shown in the following example:
.. code-block:: yaml
connector_1:
module: EvseManager
mapping:
implementations:
evse:
evse: 1
connector: 1
Here, the module stays mapped to the whole charging station
and therefore an implementation as well. For the "evse" implementation,
this mapping is now overwritten to indicate that it belongs to
a specific "evse = 1" and "connector = 1".
Modules can access the mapping information in the following ways depending
on which specific information is required.
If the mapping of a requirement is of interest it can be accessed via a
get_mapping() function:
.. code-block:: cpp
r_name_of_the_requirement->get_mapping()
This returns an optional Mapping struct.
If the mapping of an interface implementation is of interest it can
also be accessed via a get_mapping() function:
.. code-block:: cpp
p_name_of_an_implementation->get_mapping()
This returns an optional Mapping struct.
If the mapping of the current module is of interest it can be accessed via the
module info:
.. code-block:: cpp
this->info.mapping
This returns an optional Mapping struct.
Mapping information is also available in error reporting via
"error.origin.mapping":
.. code-block:: cpp
const auto error_handler = [this](const Everest::error::Error& error) {
const auto evse_id = error.origin.mapping.has_value() ? error.origin.mapping.value().evse : 0;
};
const auto error_cleared_handler = [this](const Everest::error::Error& error) {
const auto evse_id = error.origin.mapping.has_value() ? error.origin.mapping.value().evse : 0;
};
subscribe_global_all_errors(error_handler, error_cleared_handler);

View File

@@ -0,0 +1,181 @@
.. _htg_bring_up_ac:
###########
AC BringUp
###########
Make sure to have completed the bring up of the CP signaling as
described in :ref:`htg_basic_bringup`.
It may be a good idea to verify the powermeter functionality now as
well - even though it is not strictly necessary for charging.
Please refer to :ref:`htg_bring_up_powermeter` for the bring-up of
the powermeter.
Once the CP signaling has been verified, most of the work for AC is
done. Let's verify the AC-specific components:
- RCD
- Connector Lock
You can continue to use your bringup config you used for the bring up
of CP, PP and relays, which includes the
:ref:`BSP BringUp module <everest_modules_BUEvseBoardSupport>`.
RCD
===
Residual current monitoring is mandatory in most countries, but the
exact regulations differ in different regions. Typically, both AC and DC
residual current faults need to be detected. If the RCD is part of your
charger setup (it may also be in the installation instead) it is a good
idea to test it now.
Connect an RCD test device, and verify it triggers both on AC and DC
faults within the correct timings and levels according to your region.
Connector Lock
==============
A connector lock is required for AC charging in case your charging station
has a socket outlet. Verify that the lock is working correctly by
commanding it to lock and unlock via your BSP driver.
Milestone: First Charging of a Real Car
=======================================
Now that all individual components have been verified, it is time to
assemble all pieces and charge a real car.
Start by creating a simple AC basic charging configuration file for
EVerest with a minimum amount of components - e.g. with one ModBus
power meter and your board support driver for CP/PP/Relays. You can set
“disable_authentication: true” in the configuration of EvseManager, then
no Auth manager is needed.
.. image:: images/basic-ac-charging-config.png
:alt: AC Basic Charging Configuration
:width: 550px
Copy the config file to your charger prototype. Make sure EVerest is
not running as a *systemd* service as we will start it manually in the
beginning. So in case of doubt, try to shutdown the *everest* system
service:
.. code-block:: bash
systemctl stop everest
Then start EVerest with the following command:
.. code-block:: bash
manager /path/to/simple_ac_config.yaml
It should look similar to the following:
.. image:: images/everest-manager-start.png
:alt: EVerest Manager start
Watch out for the log line “Ready to start charging”. Once that appears,
you can plug in the EV simulator.
Simulate a simple charging session by going to state B. Wait for PWM to
appear, then switch to state C. The relay should click and charging
should work.
Now, switch back to state B to pause charging. The relay should open and
EVerest will wait in state “ChargingPausedByEV”. Go back to state C to
close relay again.
Now, stop charging by first switching to state B followed by state A
(unplug). PWM should stop and the relay should open.
**Congratulations, you now simulated a full AC charging session on your
prototype!**
Two more things that should be tested before trying a real car:
Stop EVerest with ``Ctrl+C``. Switch the EV simulator to state B and
start EVerest again. Ideally, the plug-in should be detected right
after “Ready for charging” and PWM should be enabled. Switch to state C
after PWM is on and the relay should close.
.. tip::
If this does not work, your BSP driver needs to publish the current state
when the “enable” command is called.
This test is important as it simulates the behavior of a power
loss while a charging session is running. Especially in a home
environment, it is expected that the charging continues once the grid is
back.
Now you are ready to connect your first real EV. Here are a few things
you should also try with real EVs:
- Try pause/resume from EVSE side if you have some sort of human
interface (you will need to call the pause/resume charging commands
on EvseManager). If the EV supports it, also triggered by EV side.
- Unplug power input to the charger while a charging session is active
and replug it to verify it starts charging again (simulate grid black
out).
- Try different amperage limits.
Here is a (non-complete) list of things you should test as well in the
full setup:
- Try over-current shutdown (draw more amperage than the PWM allows,
e.g. set PWM to 6 A / 10% but connect a heater to the output of the
EV tester that draws much more than 6A).
- Test under-/over-voltage behavior (different countries have different
requirements).
- Test over-/under-temperature scenarios.
EVerest BringUp for AC ISO 15118-2
==================================
If basic AC charging is fully working, it is time for the ISO 15118-2
charging bring-up. You can use a simulator for this, but unlike for
basic charging, ISO 15118 simulators are quite complex and expensive.
You could use a real car for this.
.. note::
At the time of writing, most cars do not support ISO 15118 for AC - they do
so only on DC. Refer to https://github.com/EVerest/logfiles
to get a better idea which car brands / models can be used for testing.
As with basic charging, we first create a new configuration file by
extending the one we just used for basic charging.
We add an :ref:`ISO 15118 stack <everest_modules_EvseV2G>` as well as a
:ref:`SLAC module <everest_modules_EvseSlac>`:
.. image:: images/iso-15118-stack.png
:alt: ISO 15118 Stack
Start EVerest the same as we did for the basic charging test.
Now connect a real car and watch the output. Explaining the actual ISO
15118 protocol is beyond the scope of this document.
For more information on how to debug ISO communication, refer to
:doc:`Debugging ISO15118 </how-to-guides/debug-iso15118>`.
Now we have the charging functionality up and running for the most
important paths.
.. tip::
A lot of error cases should be tested now as well as we mostly covered the
happy paths - but this goes beyond the scope of this how-to-guide.
----
**Authors**: Cornelius Claussen

View File

@@ -0,0 +1,396 @@
.. _htg_basic_bringup:
################
Basic BringUp
################
This chapter guides you through the basic bring-up of the control pilot (CP),
proximity pilot (PP), and relays for both AC and DC chargers. It is a
prerequisite to the more specific bring-up guides for AC and DC charging.
Control Pilot
=============
The first step - both for AC and DC - is to verify the control pilot
(CP) signal functionality and stability. A lot of problems we have seen
in the field are related to unstable CP signals. So, this step is key
for a stable product.
The normative requirements are described in IEC 61815-1. This section
will guide you through the most important requirements specified in the
norm to be tested at bring-up. It is not complete, IEC gives more
requirements that should be followed at design time.
.. warning::
For all parameters: Ensure that the limits are guaranteed over the complete
temperature and input voltage ranges as well as all other environmental
factors over the complete lifetime of the product.
Being on the edge of the range during bring-up already, will most likely
result in out-of-spec performance in production due to part tolerance,
component aging etc.
First, verify PWM output with no car / nothing connected to it.
You should have an EVSE board support driver for your hardware already.
In this how-to-guide, we will use the BringUp & Qualification tool from
EVerest to select the different CP states manually. This ensures that
(a) the CP signal is correct and (b) the wiring up to the EVerest HW
driver is also correct.
You can find an example configuration file in EVerest for the BelayBox
that you can modify to use the correct BSP driver for your hardware:
.. code-block:: bash
config/bringup/config-bringup-yetidriver.yaml
Before you start the bring-up, make sure there is no other EVerest
instance running (e.g. do a ``systemctl stop everest``).
On the target, start it the following way:
.. code-block:: bash
/etc/everest/run_tmux_helper.sh /etc/everest/bringup/config-bringup-yetidriver.yaml /usr
Now, connect an EV simulator where you can set the states A/B/C/E and
simulate a diode failure / loss of PE connection between EV and EVSE
failure.
State D is not really used anymore nowadays; it dates back to the times
when lead-acid batteries were used that leak hydrogen gas during
charging.
Connect your EVSE, the simulator and a scope similar to this:
.. image:: images/cp-scope.png
:alt: CP Scope
:width: 600px
Now go through the following checklist step by step:
.. tip::
Select PWM off / X1, switch EV simulator to state A.
This should output constant +12 V on the CP line.
- Select AC coupling on the scope and verify the ripple noise. There
is no hard limit, but we recommend keeping it below 100 mV VPP.
.. image:: images/constant-cp-line.png
:alt: Constant CP Line
:width: 560px
- Switch to DC coupling, measure DC voltage range. It must be +12 V
+/- 5% (11.4 V to 12.6 V). Use a multimeter if your scope does not
provide sufficient DC accuracy.
Now select PWM State F. This outputs constant -12 V on the CP line.
- Select AC coupling on the scope and verify the ripple noise. There
is no hard limit, but we recommend keeping it below 100 mV.
- Switch back to DC coupling, measure DC voltage range. It must be
-12 V +/- 5% (-12.6 V to -11.4 V). Use a multimeter if your scope
does not provide sufficient DC accuracy.
Select *PWM on* with a duty cycle of 5%, and connect the EV on the
simulator (State B). Note that the images below were done in state A,
but you should use state B.
- PWM frequency: Must be in the range 980 Hz - 1020 Hz. It should be
1000 Hz.
.. image:: images/pwm-freq-1.png
:alt: PWM Frequency
:width: 560px
- Measure the (high) pulse length for the 5% duty cycle. Measure the
time between the zero crossings. It should be 50 µs.
.. image:: images/pwm-freq-2.png
:alt: PWM Frequency
:width: 560px
- Equivalent source resistance: 970 - 1030 Ohm. Using a 1% 1-kOhm
resistor should be sufficient to fulfill this requirement if the
output of the PWM generator is low impedance.
- Set state B and verify that the PWM duty cycle can be set to any
value between 5% (HLC) and the maximum current value your EVSE
supports (e.g. 53.3% for 32 A for AC). For AC, the maximum PWM for
your application can be calculated with:
.. math::
dutycyclePercent = maxAmpere / (0.6 * 100)
For DC, it is always 5%. The range from 5% to
10% is not used and it is ok to not support PWM in that range.
- Set state B, which will have a PWM voltage range from +9 V to -12
V. Verify rise time is below 10 µs from 10% (-9.9 V) to 90% (6.9 V)
of the signal. The example has a rise time of 4.3 µs.
.. image:: images/pwm-freq-3.png
:alt: PWM Frequency
:width: 560px
- State B: Verify fall time is less than 13 µs. As you can see in the
screenshot, the fall time is 10.68 µs, which is longer than the open
circuit time due to the diode in the EV simulator.
.. image:: images/pwm-freq-4.png
:alt: PWM Frequency
:width: 560px
- Set state C. Verify rise time is below 7 µs from 10% (-10.2 V) to
90% (4.2 V) of the signal. The example has a rise time of 3.08 µs.
.. image:: images/pwm-freq-5.png
:alt: PWM Frequency
:width: 560px
- State C: Fall time must be less than 13 µs from 90% (4.2 V) to 10%
(-10.2 V) of the signal. The example has a fall time of 12 µs which
is in range.
.. image:: images/pwm-freq-6.png
:alt: PWM Frequency
:width: 560px
- Set State E (sometimes called *CP Error* or so). The CP signal
should be at 0 V constantly according to the norm. Some EV simulators
only short after the diode as seen in the screenshot. Make sure that
your CP detection circuitry also treats that as state E.
.. image:: images/pwm-freq-7.png
:alt: PWM Frequency
:width: 560px
- Test diode failure detection: Short the diode on the EV side. Many
off-the-shelf EV simulators can be easily modified with an extra push
button if it is not included already. Verify that the BSP throws an
error (DiodeFault)
- Test short state changes: Toggle between states B, C, B quickly to
produce a short time in state C (about 200 ms or less). The short
state C should be reliably reported to EVerest. It is a common issue
that the safety MCU filters out state durations that are too short.
This will cause issues with the BCB toggle wakeup sequence detection
of ISO 15118-3.
- Disconnect PE between EV and EVSE. This should be detected as an
error or state A.
Now that the basic functionality is working, test stability of the state
detection. Most cheap EV simulators only use the nominal resistor values
to test the states, but ideally you have a simulator that can use the
minimum and maximum values for each state.
Ensure that the following states are detected correctly over the
complete range:
+-----------------+-----------------+-----------------+-----------------+
| State | Minimum R | Nominal R | Maximum R |
| | applied by EV | | applied by EV |
+=================+=================+=================+=================+
| B | 1870 Ω | 2740 Ω | 4610 Ω |
+-----------------+-----------------+-----------------+-----------------+
| C | 909 Ω | 1300 Ω | 1723 Ω |
+-----------------+-----------------+-----------------+-----------------+
If possible, use a debug GPIO, that shows the exact timing of the ADC
sampling and connect it to the second channel of your scope like this:
.. image:: images/pwm-freq-8.png
:alt: PWM Frequency
:width: 560px
In the screenshot, two PWM signals are shown. The cyan one is 5% duty
cycle and the yellow one is 95% duty cycle. The magenta signal shows the
time the ADC reads the signal. As you can see, the first magenta pulse
is nicely aligned with the end of the low part of the PWM, so it will
read the -12 V reliably even at 95% duty cycle.
The second magenta pulse is nicely aligned with the high part of the PWM
providing stable readings even at just 5% PWM. The 5% case is the more
critical one, as very high duty cycles are not typically used.
The most common problem for unstable CP detection (which results in
charging sessions breaking and potentially leads to relays opening under
full load) is that the ADC sampling time starts too early or ends too
late. In both cases it will capture part of the edge of the high part of
the signal, resulting in a lower measured value. This can lead to e.g. a
state C detection while it is state B in reality.
Make sure to observe the correct timing under real conditions with a car
attached. With an open circuit and the 2 µs rise time, timing may be
perfect, but with a car attached and a 10 µs rise time it may not work
correctly.
This is especially critical at the 5% duty cycle as the high part is
only 50 µs long. Never sample in the first 10 µs. When using the MCU to
synchronize ADC and PWM, make sure that interrupt priorities are set
correctly so that additional (interrupt) load on the MCU does not lead
to delayed ADC triggers *sometimes*.
This needs to be 100% stable over long periods of time (hours/days !).
Some MCU (especially those made for motor control) can trigger the ADC
synchronously with the PWM generation without involving an interrupt
routine.
Apply some filtering and averaging in software (e.g. average over 10
pulses, filter out the highest and the lowest value or similar). It
should not detect a wrong state, not even a single time.
Do not filter too much though. This is another common flaw that should
be tested at this stage: IEC 61851-1 requires switching off power when
the EV transitions from C2 to B2 within 100 ms. As the relays also take
some time to open, do not delay the detection of the state change by
more than 50 ms due to filtering. Report all state changes to EVerest,
also those that are short-lived. Otherwise, some features will not work
correctly, e.g. ISO 15118 resume after pause.
Now test with several different real cars as well, as the implementation
on the EV side differs a bit across vendors and models.
Relays
======
The AC output contactor bring-up is the same as for the DC output relay.
Verify the correct function of the relay. Command the MCU to close the
relay (use the BringUp tools *Allow power on* button) and verify it
closes the relay in a timely manner (given that there is no RCD error
etc).
EVerest will issue the *allow_power_on/force_power_off* commands to the
BSP driver just like you can do it in the BringUp module. It is
important to understand the logic:
If the safety MCU receives an *“Allow power on”*, it may switch on the
relay if all other requirements are met (e.g. CP in state C, RCD current
ok, temperature in range etc). The MCU may decide to not switch on the
relay if some other local requirements are not met. It is just important
to always report the actual relay state with the PowerOn/PowerOff
events.
The following checklist tests if the safety MCU behaves correctly in
regard to the CP state C2. It does not test for other requirements such
as over-temperature etc.
If the safety MCU receives a force power off from EVerest, it shall
open the relay immediately.
Complete the following checklist:
- State A, PWM off, force power off. Set State B then enable PWM 5%
and then C.
Then allow power on. Relays should switch on within a short period
of time after sending allow power on. You should see a “Power On”
event (the time between “Allow power on” and the “Power On”
feedback event should be short, e.g. less than 300 ms or so, no
hard limit here). Click on “Force power off”. You should see a
“Power Off” (same timings as above). These timings should be short
- see below. You can click “Power On/Off” a couple of times and
verify the timing of the feedback.
- Toggle the relay on and off a couple of times and observe the
PowerOn/PowerOff events. There should be exactly one event for each
on or off switching of the relay. Make sure that there are not
multiple PowerOn/PowerOff/PowerOn events being generated because the
relay is bouncing.
- State A, PWM off, state State B. Then enable PWM 5%, allow *power on*.
Relay should not close. Wait a few seconds, then set state C.
Relay should close immediately after entering state C. Ignore the
time shown on the “Power On” event. It measures the time from the
last “Allow power on” command to feedback.
- State C, PWM 5%, Relay closed. Then set state B. Relay should open
immediately (max 100 ms). Go back to state C. Relay should close
again. (IEC 61851-1:2017 Table A.6: Sequence 8.1)
- State C, PWM 5%, Relay closed. Then stop PWM but stay in state C.
Relay should open after a minimum of 6 seconds. (IEC 61851-1:2017
Table A.6: Sequence 10.2)
Timing on closing relays is quite relaxed, but ISO 15118 has a limit of
one second from the ISO command to switch on to the ISO feedback that it
was done. As most of the time is spent in the ISO communication stacks,
the MCU should ensure that the time from commanding the relays to close
to receiving the feedback that it was closed correctly should be in the
order of 100 ms in the MCU.
Timing on opening the relays is more critical and a good value is
opening within 50 ms after the command arrives at the latest. IEC
61851-1 requires the relay to be open after a maximum of 100 ms after
state C->B (or A etc) transition.
Verify that the feedback is reported correctly. A typical error during
bring-up with EVerest is missing relay feedback. Make sure they are
always reported to represent the relay state correctly. Send the events
on any change, regardless of the cause of switch on or off in the MCU.
Proximity Pilot
===============
This section is for Type 2 CCS Chargers. PP works a little different for
Type 1 Chargers.
For AC chargers with a type 2 socket, PP needs to be verified as well.
AC Chargers with a permanently attached cable do not use the PP signal.
DC chargers normally do not use PP as they have an attached cable,
however it may be used in some configurations to e.g. detect a cut
cable.
Most of the EV simulators can only switch the nominal resistor values
for 13 A/20 A/32 A. To test the PP functionality, you could e.g. use
small wired resistors and connect them manually between PP and PE or
build yourself a small tester that can do the minimum/nominal and
maximum resistor values.
Verify that the BSP reports the correct cable ampacity for all values in
this table:
================= ========= ========= =========
Cable ampacity Minimal R Nominal R Maximal R
================= ========= ========= =========
13 A 1100 Ω 1500 Ω 2460 Ω
20 A 400 Ω 680 Ω 936 Ω
32 A 164 Ω 220 Ω 308 Ω
63 A 3ph/70 A 1ph 80 Ω 100 Ω 140 Ω
================= ========= ========= =========
Values below 60 Ω above 4500 Ω should be treated as errors and the BSP
should report “None” as PP ampacity. Resistor values in between the
defined ranges in the table should be treated as the lower ampacity
value.
Ensure that PP is measured quickly after plug-in of the vehicle. The MCU
should also monitor PP throughout the complete charging session and
report an error if PP connection breaks.
With CP/PP and relays, the minimal setup for AC charging has already
been verified.
.. tip::
In addition, you may want to verify a few more components before charging a
real car.
But you can also do this later on.
Once the CP signaling has been verified you can continue with the bring up of
the powermeter and the bringup for AC or DC, depending on your use case.
- :doc:`AC BringUp </how-to-guides/bringup/ac>`
- :doc:`DC BringUp </how-to-guides/bringup/dc>`
- :doc:`BringUp Powermeter </how-to-guides/bringup/powermeter>`
----
**Authors**: Cornelius Claussen

View File

@@ -0,0 +1,386 @@
.. _htg_bring_up_dc:
##########
DC BringUp
##########
Make sure to have completed the bring up of the CP signaling as
described in :ref:`htg_basic_bringup`.
For a DC charger, CP is essential. Several other pieces of hardware need
to be brought to life as well, though. This gives you a minimal setup
before testing the complete setup:
- The DC power supply that delivers the charging current to the car
- Isolation monitoring device
- Optional: Proximity pilot
Let's start with the DC power supply as this is the most important one.
DC Power Supply
===============
Make sure the high voltage output of the DC power supply is isolated and
nothing is attached to it that could draw current or get destroyed by
high voltages. Make sure it is safe to set the maximum voltage.
.. warning::
Follow all relevant safety precautions.
Only trained personnel may execute this step.
High voltages may cause severe injury including death.
We will only check the most important features required for a minimal
CCS charger setup. Check IEC 61851-23 for a full set of requirements.
We will manually set voltages up to the maximum voltage and verify the
driver functionality and the performance of the power supply under
no-load conditions.
Create a simple BringUp configuration file that contains only the
BUPowerSupplyDC module and the actual driver module / bridge module to
your external driver. There are a couple of examples that you can
modify: ``config-bringup-huawei.yaml``, ``config-bringup-uugreen.yaml``,
``config-bringup-api-powersupply.yaml``.
Start the BringUp & Qualification session with the following command in
the build folder:
.. code-block:: bash
/etc/everest/run_tmux_helper.sh /etc/everest/bringup/config-bringup-mypowersupply.yaml /usr
Now make sure to complete the checklist:
Start with the power supply being switched off. Set the maximum output
voltage (e.g. 1000 V) and then switch the power supply to export mode
(rectifier mode, power supply outputs power to the car). Make sure to do
it in exactly this order:
- Verify that the reported capabilities are matching the
manufacturer's specifications for the power supply.
- Verify that the output voltage is reached within 6 seconds. If it
stays on a lower voltage, the power supply driver may “forget” the
voltage setting when being switched on or off. This needs to be
fixed. The driver should work independently of the order of setting
output voltage and switching on/off.
- Verify the output voltage and current is measured accurately.
Compare against an external voltage meter and a current meter (IEC
61851-1:2023 CC.6.3 requires +- 10 V for voltage and +-1.5% for
current or 0.5 A - whichever is more).
- Verify that there is no overshoot when changing voltage from lowest
to highest output voltage. This may cause problems in CableCheck
phase on some EVs.
- Verify the measured output voltage is correctly reported back to
EVerest (see measured output voltage in the BringUp module).
- Verify the measured voltage is reported frequently (e.g. every
second or more often).
- Optional: Measure the power factor. Some power supplies have
problems with power factor correction under no / light load
conditions. When charging a real car, no load happens during
*CableCheck* and *PreCharge* phases.
Now, set the minimum output voltage that the power supply supports
(e.g. 150 V). On power supplies that have high/low mode switching,
choose the minimum voltage that the high mode supports. Otherwise, it
will take quite long to switch from highest to lowest voltage, since the
power supply will need to change its mode configuration.
- Switch the power supply off and verify that the output voltage
drops to 0 V more or less immediately.
- On power supplies with automatic high/low voltage mode switching:
Switch manually between the highest and lowest voltage setting to
trigger mode switches. They should be reasonably fast, e.g. < 5
seconds. Should switching between the modes be necessary during
charging, it is ok to have a small pause in *CurrentDemand*.
- Switch on the power supply at e.g. 500 V and exit the bring up
setup, so EVerest is no longer communicating with the power supply.
It should switch off after a timeout. (Most power supplies switch off
within 10-20 seconds after communication loss.)
Isolation monitor device
========================
The isolation monitor needs to comply with IEC 61557-8 or equivalent.
EVerest will use the isolation resistance values as reported by the
driver module and decide whether to stop the charging session or not. It
is a good idea to have a redundant switch-off by e.g. using the IMDs
built in relay to directly switch the emergency off input of the PSU.
To test the correct functionality within EVerest, use the
BUIsolationMonitor BringUp module and connect it to the driver module.
An example can be found here:
.. code-block:: bash
config/config-bringup-isolation-monitor-sil.yaml
.. image:: images/bring-up-split-screen.png
:alt: BUIsolationMonitor Bring-Up
Ideally, the BringUp configuration loads both the isolation monitor and
DC Power supply modules as both are needed to run all tests.
- Switch on the “Power supply” at 500 V. Click on “Start
measurements”. Verify the isolation monitor sees the 500 V output
voltage. This is to ensure it is actually connected to the correct
wires. Verify that new measurements are coming in regularly
(e.g. every second).
- Verify that the measured resistance is very high (e.g. 1 MOhm or
so).
- Connect a 100 kOhm (or other value) resistor from the minus wire to
protective earth. Check how long it takes until the value changes on
the screen. This should be inline with the specs from the datasheet
(e.g. <10 seconds for Bender).
- Test the same with the plus wire.
- Stop the measurements and verify no more measurements are coming
in.
- Start the self-test of the IMD and wait for the result (with the
power supply still being on). It should take no more than 10 s or so
to do the self-testing. Many isolation monitors take a long time to
complete the self-testing. This may cause time-out problems in
*CableCheck* with some cars.
The time it takes until the IMD detects the isolation fault will need to
be set in the final configuration file.
Over-voltage monitor device
===========================
IEC 61851-23:2023 requires a fast over-voltage protection (OVM)
(6.3.1.106.2). For a minimal proof-of-concept setup in a lab
environment, you may want to skip this as it is not functionally needed
for charging. It will be required for certification though.
The OVM shall switch off if the DC voltage is above the required limit
for 9 ms. For bring-up purposes, we want to test if limit is transported
correctly and if the start and stop commands from EVerest are
implemented correctly in the driver.
An example bring-up configuration using APIs can be found here:
.. code-block:: bash
config/config-bringup-api-over-voltage-monitor.yaml
Add two things to this bring-up configuration:
1) A power supply driver connected to a BringUp module
2) An evse_board_support driver connected to a BringUp module
Test setup:
- Connect a CP tester to the control pilot and set state C.
- Set “Allow power on” in the evse_board_support BringUp. The relays
should close now.
- Connect a scope with one channel to the high voltage (use a 1000 V
probe!) and one channel to coil voltage of the relays. Instead of the
coil voltage, any other signal that triggers when the emergency
shutdown starts can be used.
To test the basic functionality, use the check list below. Start each
test with a fresh test setup (relays closed).
.. note::
There are more requirements in the standard - especially regarding timing.
- Switch on power supply at 500 V. Set the OVM limit to 550 V and
start the monitoring. Then, set the power supply to 565 V. The relays
should open immediately. The point in time when the voltage rises
above 550 V is t_1 - the time where the coil voltage indicates a
start of emergency shutdown. Verify with the scope that the time
between t_1 and t_2 is a maximum of 10 ms (9 ms for detection of
over-voltage and 1 ms to initate the shutdown.)
- Repeat for 825 V (limit) and 840 V (DC output voltage).
- Repeat for 935 V (limit) and 950 V (DC output voltage).
- Repeat for 1100 V (limit) and 1115 V (DC output voltage). This
measurement is probably not possible as the power supply is limited
to 1000 V. It is a test case in table 103 of IEC 61851-23:2023.
- Set a limit of 550 V. Switch on the power supply at 500 V. Start
monitoring. Stop monitoring. Set the DC output voltage to 565 V. The
relays shall remain closed as the over-voltage monitoring is not
active.
More IEC 61851-23 test cases for DC
===================================
For a minimum proof-of-concept setup, it should be enough already.
IEC 61851-23 has several more requirements (check the norm for a
complete list) that need to be fulfilled for a real product. Here is the
list of mandatory functions from IEC 61851-23:2023 6.3.1.1 that should
be tested early on as they may influence the final design:
- Verify the timing of the CableCheck phase. EVerest will print some
timing hints on the console. Several cars will timeout and not charge
if the Cable check takes more than 30 s. Recommendation is to stay
below 25 s for the complete phase. This may not be achievable with
off-the-shelf components. It is however crucial to be compatible with
all EVs. Use e.g. BYD EVs to test for the 30 s timeout in
*CableCheck*.
- Continuous continuity checking of the protective conductor
according to 6.3.1.2: This can be usually fulfilled by opening the
relays (potentially under load), but you should consider ramping down
the DC power supply before opening the relays to protect them.
- Verification that the EV is properly connected to the EV supply
equipment according to 6.3.1.3: This should be compliant
automatically if the *evse_board_support* and the safety MCU passed
the BringUp checks.
- Energization of the power supply to the EV according to 6.3.1.4:
This should be compliant if the BringUp checks are passed.
- De-energization of the power supply to the EV according to 6.3.1.5:
Similar to “Continuous continuity checking of the protective
conductor”.
- Maximum allowable current according to 6.3.1.6: This should be
compliant if the BringUp checks are passed.
- DC supply for EV according to 6.3.1.101: This should be compliant
if the BringUp checks are passed.
- Measuring current and voltage according to 6.3.1.102: For charging,
EVerest uses the voltage and current measurements as reported by the
power supply (not the power meter). This should be compliant if the
BringUp checks passed for the DC power supply.
- Latching of the vehicle coupler according to 6.3.1.103: This should
be compliant if the BringUp checks are passed.
- Compatibility check according to 6.3.1.104: This should be
compliant if the BringUp checks are passed.
- Insulation resistance check before energy transfer according to
6.3.1.105: This should be compliant if the BringUp checks are passed.
- Protection against over-voltage between DC+ and DC according to
6.3.1.106: This is a quite hard requirement (trigger shut-down in 1
ms after voltage is above limits for 9 ms). It requires both an
accurate and fast measurement. This needs to be implemented in the
safety MCU independently of EVerest.
- Verification of vehicle connector latching according to 6.3.1.107:
Not applicable for CCS.
- Control circuit supply integrity according to 6.3.1.108: Hardware
requirement.
- Short-circuit check before energy transfer according to 6.3.1.109:
Test with a 100 Ohm load resistor connected in *CableCheck* as per
the norm.
- User initiated shutdown according to 6.3.1.110: This can be
implemented either in the BSP (publish var *request_stop_transaction*
in interface *evse_board_support*) or use *stop_transaction* command
in *evse_manager* interface. Both are available via the EVerest API
modules.
- Overload protection for parallel conductors (conditional function)
according to 6.3.1.111: Pure hardware requirement.
- Voltage limitation between side B live parts (DC+ and DC) and
protective conductor according to 6.3.1.112: Hardware requirement -
choose especially the IMD wisely.
- Shutdown of EV supply equipment according to 6.3.1.113: This should
be compliant if the BringUp checks are passed.
First milestone: Connect a real car, zero power charging
========================================================
Now that all individual components are verified, start EVerest with the
full configuration that you created earlier.
Disconnect the plus wire between your prototype and the cable to the EV.
On the first tests, no power should be flowing.
Start EVerest and wait for the message “Ready for charging”. Then plug
in the vehicle. Watch the ISO traffic. It should be successful until the
*PreCharge* phase. Then the EV should stop as it cannot see the voltage.
During the start up of the session, monitor the voltage of the DC power
supply. It should be 500 V in *CableCheck* (or whatever you set in the
config) and then switch to the EV batteries voltage.
Verify that in precharge the output voltage is within +/-20 V of the
EV's battery voltage.
Second milestone: Connect a real car, limited power charging
============================================================
Connect a 5 kOhm resistor in the plus wire between EVSE output and
connector to EV. This will limit the current flow into the EV, but
allows voltages to be seen by the EV. Plug in a vehicle. You should now
get all the way to the *CurrentDemand* phase.
Some EVs will stop after some seconds as no current is flowing.
Verify all communications are correct. Use Wireshark to verify each step
of the ISO communication (see Debug chapter).
Third milestone: Connect a real car, full power charging
========================================================
If everything was correct in the previous step, remove the resistor and
connect the plug normally to the EVSE. Start a charging session.
Power should now be delivered to the EV in *CurrentDemand*.
Verify that the current delivered is what the EV requested and that it
is also reported correctly in the *CurrentDemandRes* messages. Verify it
is shown correctly on the in-vehicle display.
Verify the AC side is not getting overloaded.
.. tip::
A good habit is to monitor your prototype with a small thermal camera to
see if any component overheats.
If that works: **Congratulations!** You have successfully charged an EV
with DC for the first time.
Error cases
===========
There are many error cases that should be tested now. Listing all goes
beyond the scope of this manual. A few examples that should be tested:
- Place a 100 kOhm resistor from minus wire to PE. Start a charging
session. It should also fail in *CableCheck* state.
- Test short circuit under full load.
- Test load dump: Charge at maximum power and open the relays to zero
load. Watch the voltage overshoot. The power supplies need to survive
that multiple times. This does happen in the field with some EVs that
open their contactors during charging when the onboard controller
firmware resets.
- AC under-/over-voltage input
- Over-temperature shutdown
- On three-phase AC/DC converters, switch off one phase on the input
at full load.
----
**Authors**: Cornelius Claussen

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,147 @@
.. _htg_bring_up:
########################
BringUp & Qualification
########################
This chapter introduces the EVerest BringUp & Qualification process.
This process is all about bringing up the individual hardware components
step by step by verifying their functionality and performance in
isolated test cases. In the end, the whole charger will be brought to
life and we'll hit the most important milestone: A first complete
charging session of a real car.
.. tip::
EVerest HW Drivers should be available for all required hardware
components and a prototype hardware should be in place.
Many sections are written like a checklist that you can follow step by step.
Before we start, we recommend to set up the following development
environment:
- Working cross compiler on your PC to do fast updates of your code
(e.g. with *rsync* to the target). On Yocto, use ``bitbake -c
populate_sdk my-image`` to generate the SDK.
- SSH to the target
- Ensure the hardware interfaces to all components are up and running
- Include “tmux” in your image
- Recommended: Unicode support should work on the target terminal
BringUp modules
================
The following chapters use EVerest's BringUp helper modules.
These modules allow manual control of one or several hardware drivers.
We will use them to verify correct functionality and timing of all
components individually before assembling the complete configuration
file.
The BringUp modules should be included in the system image initially,
but later in production they should not be installed.
To get familiar with this method, you can use a pure SIL version on your
development PC. This step is optional. From the *build* folder, start
the following run script:
.. code-block:: bash
./dist/etc/everest/run_tmux_helper.sh ../config/bringup/config-bringup-isolation-monitor-sil.yaml ./dist
The first parameter specifies the config file to load, the second
parameter points to the EVerest installation to use.
Later, on the embedded target, it can be used like this:
.. code-block:: bash
/etc/everest/run_tmux_helper.sh /etc/everest/bringup/myconf.yaml /usr
The second argument now points to the EVerest version installed in the
base Yocto system. If you use a cross-compiled development version
installed under ``/var/everest``, simply set the second argument to
``/var/everest``.
You should see a split-screen setup (using tmux) similar to this:
.. image:: images/bring-up-split-screen.png
:alt: Bring-Up Split-Screen
In the left pane, EVerest is running all modules except for the two
BringUp modules. They are running in their own shell. Mouse support
should be working and you can click on the buttons.
E.g., click on “Start Measurements” and check that new isolation
measurements are coming in roughly every second.
To exit the tmux session, press ``Ctrl+B`` and then ``d``. Normally with
tmux, this puts the session running into the background but it keeps
running. The ``run_tmux_helper.sh`` script will take care of ending the
session properly here and cleaning up.
Have a look at the config file that it is using
(``config/bringup/config-bringup-isolation-monitor-sil.yaml``):
.. code-block:: yaml
active_modules:
bsp_ui:
standalone: true
module: BUIsolationMonitor
connections:
imd:
- implementation_id: main
module_id: iso_monitor
powersupply_ui:
connections:
psu:
- implementation_id: main
module_id: powersupply
module: BUPowerSupplyDC
standalone: true
iso_monitor:
module: IMDSimulator
powersupply:
module: DCSupplySimulator
The configuration file loads the two BringUp modules you saw on the
right as well as the two driver modules for isolation monitor and DC
power supply. Since it is a SIL config, both hardware drivers are
simulated only.
.. tip::
Have a look at the attribute “standalone: true” in both BringUp modules.
This tells the manager and the tmux script that these two modules should be
run in a separate tmux window.
For your hardware, you will need to create configuration files that use
the real hardware instead of the simulated ones. There are example files
for each of the following steps - just adapt them to your needs.
Now, lets use this on real hardware.
.. warning::
During the hardware bring-up, you will switch on high voltages that can
cause severe injury including death.
It must only be done by trained personnel.
Make sure to follow all necessary safety procedures.
The following chapters will guide you through the individual steps of
bringing up the hardware components one by one.
.. toctree::
:maxdepth: 1
basic
powermeter
ac
dc
----
**Authors**: Cornelius Claussen

View File

@@ -0,0 +1,44 @@
.. _htg_bring_up_powermeter:
##################
BringUp Powermeter
##################
In case your charger prototype has a power meter, it may be a good time
to verify its functionality as well now - even though it is not strictly
necessary for charging.
Write a bring-up config for your power meter and start it the same way
as described in :ref:`htg_bring_up`. There are a few examples you can copy
and modify, e.g. ``config-bringup-DZG.yaml`` or ``config-bringup-LEM.yaml``.
.. code-block:: bash
/etc/everest/run_tmux_helper.sh /etc/everest/bringup/config-bringup-mypowermeter.yaml /usr
Your setup should look like this:
.. image:: images/bring-up-powermeter.png
:alt: Bring-Up Powermeter
Complete the following steps:
- Verify that the measurements come in regularly (e.g. every second).
- Imported energy in Wh and timestamp are the only required values.
Verify they are correct. For bidirectional metering, also verify the
exported energy values.
- Validate the other reported measurements.
- If supported by the power meter (e.g. for
:doc:`German Eichrecht </how-to-guides/eichrecht>`), click
on “Start Transaction”. Verify that it replies correctly, and verify
the time to reply is less than a few seconds.
- Click on “Stop Transaction” and verify the reply. The reply should
not take more than a few seconds.
- Apply some test load if possible, and verify that the flow
direction of energy is correct. A common mistake is to swap input and
output of the meter. If you dont have a test load, you can also test
this when doing the first charging tests with a real car.
----
**Authors**: Cornelius Claussen

View File

@@ -0,0 +1,65 @@
.. _htg-choosing-version:
###########################
Choosing an EVerest Version
###########################
This guide helps you choose the right EVerest version for your use case.
Please refer to our :ref:`versioning policy <project-release-and-versioning>` for detailed information on
versioning and release cycles.
.. note::
This guide provides general guidance but is not exhaustive. Actual version and upgrade
requirements depend on your specific deployment, integrations, and customizations.
Always thoroughly review release notes and test upgrades in non-production environments.
Production Deployments
======================
For production systems, use the latest stable release (``yyyy.mm.x``). These versions provide:
- Tested and verified functionality
- Ongoing maintenance and security updates
- Stable public APIs
- Community support
Development and Testing
=======================
For development of new features or testing upcoming changes, you may use the ``main`` branch. Be aware that:
- Breaking changes may occur at any time
- APIs may be unstable
- This is not suitable for production deployments
Upgrade Planning
================
Within Stable Lines
-------------------
Upgrading within a stable release line (e.g., ``2026.01.0`` to ``2026.01.3``) should be straightforward:
- Review the release notes for the target version
- Test the upgrade in a non-production environment
- Deploy the new version
No configuration or integration changes should be required.
Across Major Releases
---------------------
Upgrading to a new stable release line (e.g., ``2026.01.x`` to ``2026.07.0``) requires more careful planning:
- Review the release notes and potential migration documentation
- Identify any breaking changes affecting your integration
- Update configurations and integrations as needed
- Thoroughly test in a non-production environment
- Plan a maintenance window for production deployment
Migration guides may be provided by the community for each major release documenting known breaking changes and upgrade paths.
Different major releases may be fully backwards compatible, partially compatible, or incompatible depending on the changes introduced.
It is recommended to review the release notes as well as versions of the public API components when upgrading across major releases.

View File

@@ -0,0 +1,111 @@
.. _howto-configure-pnc:
Configure Plug&Charge in EVerest
================================
This is a goal-oriented how-to-guide on how to configure Plug&Charge in EVerest.
To learn how Plug&Charge is implemented in EVerest, please refer to the
:doc:`Explanation of the Plug&Charge process </explanation/pnc-process>`.
The following two configuration files are relevant and require a correct setup and activation for Plug&Charge:
* EVerest configuration file (yaml)
* OCPP configuration file(s) (json) for OCPP 1.6 or OCPP 2.x
Let's start with the EVerest configuration file. If you haven't read
:ref:`Explaining the YAML files <exp-yaml-files>`,
now it's the right time to do it before you go on!
It's a good idea to start with a base of a configuration file and talk about the changes required to enable
Plug&Charge. The base config we use is the "config-sil-ocpp201.yaml", which already contains the configuration
for OCPP2.x.
Module Configurations
---------------------
We need to take a closer look at the configuration of the following modules:
* EvseManager
* EvseV2G
* Auth
* EvseSecurity
EvseManager
~~~~~~~~~~~
* In case of AC, make sure that `ac_hlc_enabled` is set to `true` in order to allow ISO15118 communication.
* Make sure `payment_enable_contract` is set to `true`.
EvseV2G
~~~~~~~~~~~
* Make sure `tls_security` is set to `allow` or `force`.
* Make sure `verify_contract_cert_chain` is set to `true`.
Auth
~~~~~~~~~~~
* Make sure the EvseManager module is listed as a connection of `token_provider`. This is important, because only
in this case the authorization request including the contract certificate is actually received by the Auth module.
* Make sure the OCPP module is configured as the single `token_validator`.
EvseSecurity
~~~~~~~~~~~~
Please refer to :ref:`Documentation of the EvseSecurity module <everest_modules_handwritten_EvseSecurity>`
for information on the ISO15118 configuration. It describes how to configure the paths to the required certificates and keys.
.. _how-to-configure-pnc-ocpp-configuration:
OCPP 1.6 and OCPP 2.x configuration
-----------------------------------
For a general introduction to how to configure OCPP in EVerest, please refer to :ref:`the OCPP1.6 tutorial <tutorial-ocpp16>`
or :ref:`the OCPP2.x tutorial <tutorial-ocpp2>`.
Since Plug&Charge has been backported from OCPP 2.x to OCPP 1.6, the
configuration options to control the process are mostly identical.
These options are described in the following section, where differences
between OCPP 1.6 and OCPP 2.x are marked.
These OCPP configuration options are relevant for the Plug&Charge process:
* ISO15118CertificateManagementEnabled (bool): Global feature flag to enable
certificate management using ISO15118. This enables the ISO15118 message handling
via the DataTransfer mechanism according the the OCPP1.6 Plug&Charge Whitepaper.
(only required for OCPP1.6, OCPP2.x does not require this option). This option
should be set to `true` in order to allow certificate management for Plug&Charge.
* ISO15118PnCEnabled (bool): Global feature flag to enable authorization using
contract certificates. This option should be set to `true`.
* CentralContractValidationAllowed (bool): If enabled and charging station can
not validate the contract locally (e.g. because no MO root certificate is
installed), the charging station provides the contract certificate as part
of the Authorize.req so that the CSMS can verfiy the contract instead.
* ContractValidationOffline (bool): If enabled, the charging station will try
to validate a contract certificate when it is offline using the authorization
cache or the local authorization list. If this is set to `false`, Plug&Charge
will fail if the charging station is offline.
* ISO15118Ctrlr::V2GCertificateInstallationEnabled (bool, only OCPP2.x):
Allows the CSMS to install an SECC leaf certificate on the charging station.
This must be enabled in case the charging station shall receive the SECC leaf
certificate from the CSMS.
* ISO15118Ctrlr::ContractCertificateInstallationEnabled (bool, only OCPP2.x):
Allows contract certificate installation installtion/update in the EV
via ISO15118.
The following configuration options control parameters of the certificate
signing request that is initiated by the charging station automatically in case
Plug&Charge is enabled and no (valid) SECC Leaf Certificate is currently installed.
* SeccLeafSubjectCommonName (string, ISO15118Ctrlr::SeccId in OCPP 2.x)
* SeccLeafSubjectCountry (string, ISO15118Ctrlr::CountryName in OCPP 2.x)
* SeccLeafSubjectOrganization (string, ISO15118Ctrlr::OrganizationName in OCPP 2.x)
These configuration keys can be configured manually or controlled by the CSMS according to its needs. If the CSMS rejects the CSR
from the charging station or does not return a certificate after the specified timeouts and retries, it is likely that the values
of these configuration keys do not match the expectations of the CSMS. Contact your CSMS partner in this case.
----
**Authors**: Piet Gömpel

View File

@@ -0,0 +1,138 @@
.. _htg_debug_iso15118:
#################
Debug ISO 15118
#################
This how-to-guide will show you how to debug ISO15118 communication
of EVerest using Wireshark.
.. _htg_wireshark:
Wireshark plugin
=================
Using the Wireshark plugin from *dspace*, it is possible to decode the
EXI messages sent over the interface of the PLC modem. The plugin uses
the same EXI decoder as EVerest.
It does even decrypt TLS-encrypted communication as EVerest sends the
keys to decrypt the packages as UDP broadcast, so the plugin can make
use of it. It is possible to live-view traffic between EV and EVSE and
to decrypt captured traffic after the fact.
Install
-------
Follow the steps in this
`README <https://github.com/dspace-group/dsV2Gshark?tab=readme-ov-file#requirements>`_
to install the plugin:
.. tip::
Make sure to copy the **whole** content of the zipped archive to the
corresponding folder!
.. _remotelivecapture:
Remote live capture
--------------------
In order to view live traffic, an *ssh* connection between the system(s)
running Wireshark and the target is required.
Making use of a VPN connection enables remote live capture from around
the globe, e.g. developers in the backoffice observing live traffic on
chargers during testivals.
To start a remote live capture session in Wireshark, click on the small
gear symbol next to:
.. figure:: images/sshdumpsetupsymbol.png
:alt: SSH remote capture: sshdump
:width: 600px
This should open a setup window. Enter the target IP address and SSH
port.
.. figure:: images/sshdumpsetup.png
:alt: Setup Window
:width: 600px
Select ``Authentication`` and enter the SSH credentials for the target.
.. figure:: images/authentication.png
:alt: Authentication
:width: 600px
Next, select ``Capture`` and type in the network interface on the target
that is used by the PLC modem. In the case of the YAK board, it is
``eth1``.
.. figure:: images/capture.png
:alt: Setup Window
:width: 600px
To start the live capture session, click on ``Start``.
.. tip::
It is recommended to establish a passwordless SSH connection, as the
password must be typed in every time a remote capture session is started.
After setting this up e.g. via `ssh-copy-id`, select the path to your
private key.
SSH credentials with an empty password are not supported for the steps
above.
However, in order to start a live capture session without any prior GUI
setup using an empty or passwordless SSH connection, simply execute:
.. code-block:: bash
wireshark -k -i <( ssh <user>@<target_ip> tcpdump -s 0 -U -n -w - -i <device to listen on>)
The remote live session should look like this:
.. figure:: images/traffic.gif
:alt: wireshark_traffic
.. _decodingafterthefact:
Decoding captured traffic after the fact
-----------------------------------------
To activate the local capturing of traffic between EV and EVSE, you need
to include the ``PacketSniffer`` module in your EVerest config:
.. code-block:: yaml
packet_sniffer:
config_module:
session_logging_path: <your desired path>
connections:
evse_manager:
- implementation_id: evse
module_id: evse_manager
module: PacketSniffer
In case you want to keep the capture files after a reboot, you need to
set the logging path, e.g. ``/var/everest-logs/sessions``; otherwise
the default logging path is ``tmp``, which will be empty on each boot.
To view the captured traffic, you can either download the ``.dump``
files via SCP and use the Wireshark GUI to open the files or in case a
passwordless *ssh* is set up (``ssh-copy-id``), you can directly open
the file using:
.. code-block:: bash
wireshark -k -i <(ssh <user>@<target ip> cat <logging path on target>/ethernet-traffic.dump)
You can now view the decoded messages, e.g.
.. figure:: images/wireshark_decoded.png
:alt: wireshark_decoded_message
----
**Authors**: Cornelius Claussen

View File

@@ -0,0 +1,94 @@
.. _htg_debug_modules:
#####################
Debug EVerest Modules
#####################
Preparation
===========
Obvious prerequisite for using a debugger is to compile the project with
debugging enabled. One easy way to achieve this is to call
.. code-block:: bash
cmake -B build -DCMAKE_BUILD_TYPE=Debug
from the root folder of EVerest, assuming you have already created the
``build`` directory.
Execution
=========
It is possible to use the GNU Debugger (GDB) to debug a single EVerest module.
The easiest way is to run the module in standalone mode. Say, for example, you
want to debug the Auth module for the SIL config (``config-sil.yaml``).
Let's assume you are in directory ``build/dist``.
Start the manager with
.. code-block:: bash
./bin/manager --config config-sil --standalone auth
This will start EVerest with the config-sil.yaml as configuration, but it
won't start the Auth module (note ``auth`` is written small because it is the
*module instance id* - this way there can be multiple Auth module instances
in your config).
Now you need to start the Auth module manual using gdb. When using
Visual Studio Code, the debug configuration (``launch.json``) looks like this:
.. code-block:: bash
{
"version": "0.2.0",
"configurations": [
{
"name": "AuthManager",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/dist/libexec/everest/modules/Auth/Auth",
"args": ["--config", "config-sil", "--module", "auth"],
"stopAtEntry": false,
"cwd": "/workspace/EVerest",
"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
}
]
},
]
}
This will then start the Auth module instance.
Also note the argument ``--module auth``, which again specifies the module
instance id and needs to match the one you've used for standalone.
Now, EVerest will continue to start and breakpoints set in the source file of
the Auth module should be taken.
.. note::
It is also possible to debug the whole manager process. This way, you will
have the disadvantage of possibly bad performance. The reason is that the
manager spawns/forks new processes, which then need to be attached to the
debugger too.
Just in case you want to go this direction, you need to
"set detach-on-fork off" and "follow-fork-mode" depending on what you want to
achieve.

View File

@@ -0,0 +1,310 @@
#####################################################
Use the EVerest Development Container (devrd managed)
#####################################################
The EVerest development container (devcontainer) provides
a consistent development environment for EVerest development
and sil testing.
With the devcontainer itself also service containers
are provided to run required services like databases,
message brokers, etc.
.. hint::
This guide describes the usage of the devcontainer
managed by the `devrd` cli tool.
For the variant using the VSCode Dev Containers extension
this is not required since everything is managed by VSCode
automatically.
Find further documentation on development containers here:
- :doc:`Tutorial: Setup the EVerest Development Container </tutorials/setup-devcontainer/index>`
- :doc:`Internals of the EVerest Development Container </explanation/devcontainer-internal/index>`
***************************
Provided Service Containers
***************************
Services are organized into logical profiles for easier management:
.. _table_of_devcontainer_services:
.. list-table::
:header-rows: 1
:widths: 20 20 20 20 20
* - Profiles
- Services
- Container Name
- URL
- Purpose
* - ``mqtt/ocpp/sil/all``
- **MQTT Server**
- `<prefix>_devcontainer-mqtt-server-1`
- localhost:1883
- Basic MQTT broker
* - ``ocpp/all``
- **OCPP DB**
- `<prefix>_devcontainer-ocpp-db-1`
- Internal
- OCPP database
* - ``ocpp/all``
- **SteVe (HTTP)**
- `<prefix>_devcontainer-steve-1`
- <http://localhost:8180>
- OCPP backend management
* - ``sil/all``
- **Node-RED UI**
- `<prefix>_devcontainer-nodered-1`
- <http://localhost:1880/ui>
- SIL simulation interface
* - ``sil/all``
- **MQTT Explorer**
- `<prefix>_devcontainer-mqtt-explorer-1`
- <http://localhost:4000>
- MQTT topic browser
* - ``ocpp/sil/all``
- **Dev Container**
- `<prefix>_devcontainer-devcontainer-1`
- Command line
- The development container
* - ``ocpp/sil/all``
- **Docker Proxy**
- `<prefix>_devcontainer-docker-proxy-1`
- Internal
- Secure Docker API access
.. note::
the ``all`` profile is a synthetic profile that includes all
services. Use ``./applications/devrd/devrd start all`` or ``./devrd start`` (default)
to start all services.
Where ``<prefix>`` is the docker compose project name prefix.
***********************************
Starting/Stopping Services/Profiles
***********************************
To start/stop service containers use the ``devrd`` cli tool,
with ``./applications/devrd/devrd start`` and ``./devrd stop`` commands.
Usage examples:
.. code-block:: bash
# Start profiles
./applications/devrd/devrd start # Start all services (generates .env if missing)
./applications/devrd/devrd start all # Start all services (same as above)
./applications/devrd/devrd start sil # Start SIL simulation tools
./applications/devrd/devrd start ocpp # Start OCPP backend
./applications/devrd/devrd start mqtt # Start only MQTT server
# Stop services
./applications/devrd/devrd stop # Stop all services
./applications/devrd/devrd stop all # Stop all services (same as above)
./applications/devrd/devrd stop sil # Stop SIL profile only
./applications/devrd/devrd stop ev-ws # Stop all containers matching pattern 'ev-ws'
*******************
Devcontainer Access
*******************
There are two ways to access the devcontainer:
1. **Open an interactive shell in the devcontainer**
To run commands inside the devcontainer, open an interactive shell
with the devrd cli:
.. code-block:: bash
./applications/devrd/devrd prompt
This will open an interactive shell inside the devcontainer.
The contents of the EVerest repository are mapped
to the ``/workspace`` directory inside the container.
You can now run all development commands inside this shell.
2. **Run a single command in the devcontainer**
To run a single command inside the devcontainer, use the
``devrd exec`` command:
.. code-block:: bash
./applications/devrd/devrd exec <command> [args...]
This will run the specified command with optional arguments
inside the devcontainer and return the output to the host terminal.
Example:
.. code-block:: bash
./applications/devrd/devrd exec ls /workspace
This will list the contents of the ``/workspace`` directory
inside the devcontainer.
********************************************
Clean Up Devcontainer and Service Containers
********************************************
To clean up the devcontainer and all service containers,
use the ``./applications/devrd/devrd stop`` and ``./devrd purge`` command:
.. code-block:: bash
./applications/devrd/devrd stop # Stop all service containers
./applications/devrd/devrd purge # Remove all service containers, images and volumes
***********************************
Manage Flows for Node-RED Container
***********************************
There are two commands one should know when working with Node-RED flows:
1. **List available flows**
To list all available flows in the Node-RED container use the command:
.. code-block:: bash
./applications/devrd/devrd flows
2. **Switch to specific flow file**
To switch to a specific flow file in the Node-RED container use the command:
.. code-block:: bash
./applications/devrd/devrd flow path/to/flow/file.json
Where ``path/to/flow/file.json`` is the path to the flow file.
**********************************
Modify Workspace Directory Mapping
**********************************
While the default mapping of the EVerest repository
is to the ``/workspace`` directory inside the devcontainer,
this can be modified by using the ``./applications/devrd/devrd env`` command:
.. code-block:: bash
./applications/devrd/devrd env -w /path/to/workspace
This will mount the EVerest repository to the specified
``/path/to/workspace`` directory inside the devcontainer.
Where ``/path/to/workspace`` is the desired path inside the container.
*********************
Example SIL Execution
*********************
Simple SIL
==========
**Prerequisites:**
- Containers are up and running
- EVerest has been build (``cmake --build ...``) + installed (``cmake --install ...``)
**Inside the devcontainer:**
Change to the build directory and run the config in question, e.g.:
.. code-block:: bash
# get a prompt inside the container:
./applications/devrd/devrd prompt
# run EVerest:
./run-scripts/run-sil.sh
.. warning::
If aiming to test OCPP, i.e. connect to the SteVe backend, further preparation
is required, see the next example, below.
**Outside the devcontainer:**
Run a NodeRED flow that matches your EVerest config:
.. code-block:: bash
# switch to basic SIL flow:
./applications/devrd/devrd flow config/nodered/config-sil-flow.json
**In your Webbrowser:**
See the :ref:`table above <table_of_devcontainer_services>` to
- open the Node-RED UI to start/stop charging.
- open the MQTT Explorer topic browser to analyse MQTT traffic between modules.
SIL with OCPP
=============
**Prerequisites:**
Similar to the simple setup above + choose a similar NodeRED flow.
In the OCPP config file: Find the key ``"CentralSystemURI"``. Change ``127.0.0.1:8180``
to ``steve:8180``. This allows EVerest's OCPP module to connect to the SteVe
instance running in its own separate container (``"steve"`` is the name of the service
defined in the ``docker-compose.yaml``).
.. hint::
Identify EVerest's OCPP config file: For the given example of ``run-sil-ocpp.sh``,
the underlying config ``config-sil-ocpp.yaml`` sets ``ChargePointConfigPath`` as
``lib/everest/ocpp/config/v16/config-docker.json``.
.. important::
When running a OCPP SIL in this containerized setup, the SteVe CMSM will
be available on ``127.0.0.1::8180`` on your host, but not from within the
container that runs EVerest! This is simply how docker/docker-compose work.
Therefore, the OCPP module will not successfully connect to the CSMS if
configured to this address.
**In your Webbrowser:**
- Login to the SteVe web UI (see :ref:`table above <table_of_devcontainer_services>`)
(default credentials are ``admin`` / ``1234``).
- Add the charger:
- choose ``DATA MANAGEMENT``/``CHARGE POINTS`` and ``Add New``
- Set ``ChargeBox ID`` to match the ``"ChargePointId"`` from EVerest's OCPP config file
- ``Add`` the charge point.
**Inside the devcontainer:**
Using the terminal, change to the build directory.
Run an OCPP-enabled config, e.g.:
.. code-block:: bash
# get a prompt inside the container:
./applications/devrd/devrd prompt
# run an OCPP enabled config:
./run-scripts/run-sil-ocpp.sh
Now you can use your webbrowser again to control the charger (NodeRED UI), analyze
MQTT traffic and control the charger via the SteVe CSMS.
***************
Troubleshooting
***************
See the :doc:`separate troubleshooting section </tutorials/setup-devcontainer/troubleshooting>` for help
on devcontainer-specific issues.
----
**Authors:** Florian Mihut, Andreas Heinrich

View File

@@ -0,0 +1,65 @@
.. _howto_document:
######################
Documenting Quickstart
######################
This is a short how-to for writing documentation in EVerest. Please refer to the
`Documentation README <https://github.com/EVerest/EVerest/blob/main/docs/README.md>`_
for build instructions for the documentation.
To get more detailed information, see
:ref:`Documenting EVerest <documenting_everest>`.
1. Decide which type of documentation you want to create:
a. If you are not familiar with the Diátaxis way of organizing documentation,
read the subsection :ref:`Structure of the Documentation <exp_the_everest_documentation_structure_of_doc>`.
b. If you cannot clearly decide which category your contribution belongs to,
consider splitting it up to align with the Diátaxis philosophy.
2. Decide where to place the documentation
a. Module documentation goes into the modules ``docs`` directory. Provide
at least an index.rst file in this directory.
b. If you want to document some partial aspects of your code (like a
specific algorithm you use), you can add a section in the ``README.md``
close to the source code. That could for example be in the ``lib/everest/...``
directory of the EVerest repository or in the corresponding GitHub repository
if the code is not part of EVerest.
c. For documentation that is required to understand an important part or
concept of EVerest, place the new documentation in a proper location in
the ``docs`` directory of the
`EVerest main repository <https://github.com/EVerest/EVerest>`_.
d. When in doubt, use the EVerest main repository.
3. Create an issue (in case of bigger documentation changes).
Consider to create a documentation issue inside of the
EVerest GitHub repository you just have chosen.
Describe the most important aspects of the topic to be documented.
4. Create a Git branch like ``docs/name-of-topic`` in the EVerest main
repository.
Put a note in the issue to inform the community that you start working on
new documentation to solve that issue.
5. Create the documentation.
You can use existing ``.rst`` files as template for creating new
documentation pages. See this page for getting an idea how to use
reStructuredText:
https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html
Also have a look at our
:ref:`best practices page for using Sphinx in EVerest <everest_doc_sphinx_style_code>`.
6. Create pull request (PR).
After having finished your work, create a PR and set a reference to the
originating issue (if existing).
The maintainers of the repository will get informed automatically.
Alternatively, you can try to find people who have the required knowledge in
and also have the time to review your PR.
You might find them via Zulip or the working groups.

View File

@@ -0,0 +1,143 @@
.. _extending_everest_documentation:
###################################
Extending the EVerest Documentation
###################################
If you want to start documenting quickly without the need of reading through
all the theory about current documentation structure and best practices, have
a look at our :ref:`How to write EVerest documentation <howto_document>`.
.. note::
For doing quick changes in existing documentation pages, the "How to" might
be a good choice. You also can use the "How to" for creating completely new
pages. But doing this, prepare for getting more change requests by other
community members during the review process. To avoid this, read through
the page you are currently reading to get more theory.
********************************
Process of EVerest documentation
********************************
Preparing a new documentation page
==================================
Let's suppose, you are aware of a brand-new EVerest feature that is still not
documented. Or you found some aspect of EVerest that still lacks a
corresponding documentation page.
This is what to do:
1. Check the existing documentation for similar sections.
a. Search https://everest.github.io/nightly/index.html
b. Is it a module that you want to add documentation to? Then have a look
at the ``EVerest`` repository in the ``modules`` directory and check
if any documentation pages already do exist there.
c. Use GitHub search with ``org:EVerest`` and your keywords to check if you
can find existing documentation snippets near the source code of the
feature.
If you can find something that is related to the topic on your mind, please
decide, whether a new documentation section should be added or the existing
page should be updated.
2. Create a GitHub issue
a. In the repository https://github.com/EVerest/EVerest, click on ``Issues``
and then ``New issue``.
b. Choose ``Feature Request`` and fill out the title and
the description fields. Answer the templated questions, which have already
been added to the description text area.
c. Also add a reference to any related documentation pages and describe how
the new documentation parts shall relate to that (new section, change of
docs, new page with reference to existing ones etc.).
3. Optionally: Inform others about the issue
Especially if you do not want to create documentation on your own (due to
lack of time or knowledge), you can inform others about this new
documentation requirement (the issue). This is optional as the maintainers
of the EVerest documentation will get informed about the newly created issue.
But by taking the topic into an appropriate working group or into the EVerest
Zulip channels, you could find the right people who have time and knowledge
to create such a new section in the documentation.
Creating a new documentation page
=================================
Creating a Git branch
---------------------
As with source code feature development, documentation is also organized with
Git branches. The scheme to name a branch should be adhered to
.. code-block:: bash
docs/name-of-topic
Optionally, to better find your own branches in a list, you could also add
your name initials.
In case your name is Abraham Braveman and you are creating a documentation
about Plug'n'Charge, you could name your branch
.. code-block:: bash
docs/ab-plug-n-charge
Choosing the type of documentation
----------------------------------
The EVerest documentation follows the Diátaxis philosphy. Find an explanation in
the :ref:`Structure of the Documentation <exp_the_everest_documentation_structure_of_doc>`
section.
Choosing a place to store the docs
----------------------------------
If you want to create a new documentation page, you should first check if
pages with similar topics are already existing. It is a good idea to place
your new page in the same location.
In general, you can decide where to put your documentation pages:
* The repository for the main documentation:
https://github.com/EVerest/EVerest in directory ``docs/sources/``
* Directly inside of the ``docs`` directory in your modules directory structure.
The ``index.rst`` in this location will be included into the auto-generated
``reference`` documentation page of this module.
* Near the source code which implements the feature that is to be documented.
.. note::
Don't be afraid to put your documentation at a "wrong" location. It is more
important that documentation does exist. The maintainers of the EVerest
documentation will help you to move your docs to a suitable place during the
PR review phase.
Writing
-------
Best practice is to look at existing documentation sources to get an idea about
how headlines or bullet points are to be handled.
You can create a ``Draft pull request`` on GitHub at an early stage of your
work to let others already get an idea how the new documentation part will look
like and give them the opportunity to comment on your work already.
.. note::
Consider referencing to existing docs with the same topic and vice versa.
Test the generated html to be correct in formatting an test all the links you
included in your text. Build instructions can be found in `docs/README.md`.
Creating a PR and merge
-----------------------
If you have finished your documentation work, you can create a pull request
for your branch. Don't forget to reference the originating issue (if existing).
The maintainers of the corresponding repository will get informed and will try
to invest time to review your work.
After merging the PR, don't forget to also close the issue and eventually
inform the community about your newly created documentation work.

View File

@@ -0,0 +1,38 @@
.. _documenting_everest:
###################
Documenting EVerest
###################
Documentation helps beginners to start with EVerest and advanced users to
be effective by quickly having required information had hand. Find out how
to help writing and keeping documentation up to date.
.. grid:: 1 2 2 3
:gutter: 2
.. grid-item-card:: Write Documentation
:link: extending-everest-documentation
:link-type: doc
How to write documentation for EVerest. A detailed description.
.. grid-item-card:: Documenting Quickstart
:link: change-documentation-quickstart
:link-type: doc
Short description with the most important steps required to change the EVerest documentation.
.. grid-item-card:: Sphinx Style Guide
:link: sphinx-style-guide
:link-type: doc
How to achieve the desired formatting with Sphinx.
.. toctree::
:hidden:
:maxdepth: 1
extending-everest-documentation
change-documentation-quickstart
sphinx-style-guide

View File

@@ -0,0 +1,656 @@
.. _everest_doc_sphinx_style_code:
##################
Sphinx Style Guide
##################
*********
Headlines
*********
Example:
.. code-block:: rst
###################
How To: Sphinx (h1)
###################
**************
Headlines (h2)
**************
Headline (h3)
=============
Headline (h4)
-------------
Headline (h5)
^^^^^^^^^^^^^
Headline (h6)
"""""""""""""
Result:
.. raw:: html
<div class="highlight-rst"><pre>
<h1>How To: Sphinx (h1)</h1>
<h2>Headlines (h2)</h2>
<h3>Headline (h3)</h3>
<h4>Headline (h4)</h4>
<h5>Headline (h5)</h5>
<h6>Headline (h6)</h6>
</pre></div>
******
Styles
******
.. code-block:: rst
**Bold text**
*Italic text*
``Inline literal/code``
:sup:`super`\ Script
:sub:`sub`\ Script
.. line-block::
**Bold text**
*Italic text*
``Inline literal/code``
:sup:`super`\ Script
:sub:`sub`\ Script
************
Bullet Lists
************
.. code-blocK:: rst
* Unordered item
* Unordered item
#. Nestes ordered item
#. Nestes ordered item
#. Nested ordered item
* Unordered item
* Unordered item
* Unordered item
#. Nestes ordered item
#. Nestes ordered item
#. Nested ordered item
* Unordered item
*****************
Targets and Links
*****************
.. code-block:: rst
.. Anchor target
.. _anchorbyref:
.. _Anchor link by text:
.. External target
.. _external_link_ref: https://example.com
.. _External link name: https://example.com
.. Footnote target
.. [1] footnote text
.. Citation target
.. [cit1] A global citation
.. External links
`External link <https://example.com>`_
`External link name`_
`Example Text <External link name>`_
`External link by ref <external_link_ref>`_
.. Internal links
`Anchor link by text`_
`Anchor <Anchor link by text>`_
`Anchor by ref <anchorbyref>`_
:ref:`Anchor <anchorbyref>`
.. Footnote
Reference a footnote [1]_
.. Citation
Reference a global citation [cit1]_
.. Section Link
Section Heading
===============
`Link <Section Heading>`_
.. anchor target
.. _anchorbyref:
.. _Anchor link by text:
.. external target
.. _external_link_ref: https://example.com
.. _External link name: https://example.com
.. footnote target
.. [1] footnote text
.. citation target
.. [cit1] A global citation
.. External links
| `External link <https://example.com>`_
| `External link name`_
| `Example Text <External link name>`_
| `External link by ref <external_link_ref>`_
.. Internal links
| `Anchor link by text`_
| `Anchor <Anchor link by text>`_
| `Anchor by ref <anchorbyref>`_
| :ref:`Anchor <anchorbyref>`
.. Footnote
Reference a footnote [1]_
.. Citation
Reference a global citation [cit1]_
.. Section Link
Section Heading
===============
`Link <Section Heading_>`_
******
Tables
******
.. code-block:: rst
+-----------------+-----------------+-----------------+
| Grid table | Header 2 | Header 3 |
| | | |
+=================+=================+=================+
| Column 1 | Column 2 | Vertical |
+-----------------+-----------------+ column +
| Horizontal span | span |
+-----------------+-----------------+-----------------+
============ ======== ========
Simple table Header 2 Header 3
============ ======== ========
Column 1 Column 2 Column 3
Horizontal column span ...
---------------------- --------
... ... ...
============ ======== ========
.. csv-table:: table title
:header: "Header 1", "Header 2", "Header 3"
:widths: 20, 20, 20
:encoding: utf-8
:header-rows: 1
"Row 1, Column 1", "Row 1, Column 2", "Row 1, Column 3"
"Row 2, Column 1", "Row 2, Column 2", "Row 2, Column 3"
The ``csv-table`` directive can be used to create tables from CSV files:
.. code-block:: rst
.. csv-table:: table title
:header: "Header 1", "Header 2", "Header 3"
:widths: 20, 20, 20
:encoding: utf-8
:header-rows: 1
:file: table.csv
Grid table
==========
+-----------------+-----------------+-----------------+
| Grid table | Header 2 | Header 3 |
| | | |
+=================+=================+=================+
| Column 1 | Column 2 | Vertical |
+-----------------+-----------------+ column +
| Horizontal span | span |
+-----------------+-----------------+-----------------+
Simple table
============
============ ======== ========
Simple table Header 2 Header 3
============ ======== ========
Column 1 Column 2 Column 3
Horizontal column span ...
---------------------- --------
... ... ...
============ ======== ========
CSV table
=========
.. csv-table:: table title
:header: "Header 1", "Header 2", "Header 3"
:widths: 20, 20, 20
:encoding: utf-8
:header-rows: 1
"Row 1, Column 1", "Row 1, Column 2", "Row 1, Column 3"
"Row 2, Column 1", "Row 2, Column 2", "Row 2, Column 3"
******************
Images and Figures
******************
Figures are images with captions. They support all image options.
.. code-block:: rst
.. image:: image.png
:alt: Image alt text
:width: 150px
:height: 150px
:align: center
:target: target_
.. figure:: image.png
:align: center
:height: 150px
:name: figure-name
Figure caption :figure:`figure-name` or `Example <figure-name>`_
Image
=====
.. image:: https://via.placeholder.com/150
:alt: Image alt text
:width: 150px
:height: 150px
:align: center
:target: https://example.com
Figure
======
.. figure:: https://via.placeholder.com/150
:alt: Figure alt text
:align: center
:target: https://example.com
:name: figure-name
Figure caption `figure-name`_ or `Example <figure-name_>`_
********
Comments
********
.. code-block:: rst
.. comment
This is a comment
**********
Directives
**********
.. code-block:: rst
.. directive:: argument
:option: value
Directive content
*****************
Table of Contents
*****************
.. code-block:: rst
.. local table of contents. The ``:local:`` option is optional.
.. contents:: Table of Contents
:local:
:depth: 2
.. defines global structure and includes all sub toc-trees and tocs
Can also be set to visible by omitting the ``:hidden:`` option
.. toc-tree:: Table of Contents
:maxdepth: 2
:numbered:
:hidden:
file.rst
second_file
directory/file
Table of Contents (this document)
=================================
.. contents:: Table of Contents
:depth: 2
:class: this-will-duplicate-information-and-it-is-still-useful-here
:backlinks: none
************************
Content Block Directives
************************
.. contents:: Content Block Directives
:depth: 1
:class: this-will-duplicate-information-and-it-is-still-useful-here
:backlinks: none
:local:
``.. topic::`` *[title]*
=========================
.. code-block:: rst
.. topic:: Topic title
Topic content
.. topic:: Topic
Topic content
``.. sidebar::`` *[title]*
===========================
.. code-block:: rst
.. sidebar:: Sidebar title
Sidebar content
.. sidebar:: Sidebar
Sidebar content
``.. admonition::`` *[title]*
=============================
.. code-block:: rst
.. admonition:: Admonition title
Admonition content
.. admonition:: Admonition title
Admonition content
``.. attention::``
==================
.. code-block:: rst
.. attention::
Attention content
.. attention::
Attention content
``.. caution::``
================
.. code-block:: rst
.. caution::
Caution content
.. caution::
Caution content
``.. danger::``
===============
.. code-block:: rst
.. danger::
Danger content
.. danger::
Danger content
``.. error::``
==============
.. code-block:: rst
.. error::
Error content
.. error::
Error content
``.. hint::``
=============
.. code-block:: rst
.. hint::
Hint content
.. hint::
Hint content
``.. important::``
==================
.. code-block:: rst
.. important::
Important content
.. important::
Important content
``.. note::``
=============
.. code-block:: rst
.. note::
Note content
.. note::
Note content
``.. tip::``
============
.. code-block:: rst
.. tip::
Tip content
.. tip::
Tip content
``.. warning::``
================
.. code-block:: rst
.. warning::
Warning content
.. warning::
Warning content
``.. seealso::``
================
.. code-block:: rst
.. seealso::
See also content
.. seealso::
See also content
``.. versionadded::`` *[version]*
==================================
.. code-block:: rst
.. versionadded:: 1.0
Version added content
.. versionadded:: 1.0
Version added content
``.. versionchanged::`` *[version]*
====================================
.. code-block:: rst
.. versionchanged:: 1.0
Version changed content
.. versionchanged:: 1.0
Version changed content
``.. deprecated::`` *[version]*
===============================
.. code-block:: rst
.. deprecated:: 1.0
Deprecated content
.. deprecated:: 1.0
Deprecated content
``.. math::``
=============
.. code-block:: rst
.. math::
\int_{-\infty}^\infty g(x) dx = 1
.. math::
\int_{-\infty}^\infty g(x) dx = 1
``.. raw::`` *output format*
============================
.. code-block:: rst
.. raw:: html
<div>Raw HTML content</div>
.. raw:: html
<div>Raw HTML content</div>
*************
Code Examples
*************
.. code-block:: rst
.. code-block:: python
:linenos:
:emphasize-lines: 2,3
:caption: Code caption
:name: code-name
// Code example
some_function();
any_var = 42;
// Do another thing
another_function();
.. literalinclude:: index.rst
:language: rst
:linenos:
:emphasize-lines: 2-5
:dedent: 4
.. code-block:: python
:linenos:
:emphasize-lines: 2,3
:caption: Code caption
:name: code-name
// Code example
some_function();
any_var = 42;
// Do another thing
another_function();
.. literalinclude:: index.rst
:language: rst
:linenos:
:emphasize-lines: 2-5
:dedent: 0

View File

@@ -0,0 +1,43 @@
.. _htg_eichrecht:
#########
Eichrecht
#########
It is recommended to use a power meter that has field-proven support for
Eichrecht implementation. The power meter should support tracking of the
transactions and signing of the OCMF packets.
EVerest transports the signed meter values from the power meter to the
OCPP CSMS and triggers the start and stop of a transaction. It does not
create, store or modify the signed meter values.
Requirements for power meter hardware and EVerest driver:
- EVerest provides information according to OCMF standard in the
*start_transaction* and *stop_transaction* commands as described
here: https://github.com/SAFE-eV/OCMF-Open-Charge-Metering-Format
- After power failure of the complete system, an ongoing transaction
before power failure shall be closed properly (including the signed
meter value) in the CSMS. For this to work, the *stop_transaction*
needs to be implemented correctly. On startup, the EvseManager will
call *stop_transaction(last_uuid)* to try to close the ongoing
transaction. The power meter driver shall return the signed meter
value for the transaction and close it. After that, it will call
*stop_transaction("")* with an empty argument. Then, the power meter
driver shall clear all pending transactions in the power meter, if
any.
- If the communication between EVerest driver and the power meter is
lost and re-established during a charging session, the charging
session shall still receive the signed meter value normally at the
end of the session.
- EvseManager should be configured to stop the charging session on
power meter failures (ensure that fail_on_powermeter_errors is set to
``true``).
----
**Authors**: Cornelius Claussen, Manuel Ziegler

View File

@@ -0,0 +1,384 @@
.. _htg_error_framework:
#########################
Using the Error Framework
#########################
Syntax in a C++ module
======================
You can find two example modules written in C++ in the `examples` folder:
`ExampleErrorRaiser` and `ExampleErrorSubscriber`.
Raise an error
--------------
Can be done in the implementation of an interface.
.. code-block:: cpp
// Create an error object
Error error_object = this->error_factory->create_error(
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
"This is an example error" // message
);
// Raise the error
raise_error(error_object);
Clear an error
--------------
Can be done in the implementation of an interface.
.. code-block:: cpp
// Clear all errors of the ErrorType "example/ExampleErrorA"
clear_error(
"example/ExampleErrorA", // ErrorType
true // clear_all
);
// Clear the error with ErrorType "example/ExampleErrorA" and ErrorSubType ""
clear_error(
"example/ExampleErrorA", // ErrorType
"" // ErrorSubType
);
clear_error(
"example/ExampleErrorA", // ErrorType
false // clear_all
);
clear_error(
"example/ExampleErrorA" // ErrorType
); // clear_all defaults to false
// Clear all errors of the current implementation
clear_all_errors_of_impl();
Subscribe to an error
---------------------
May be done in the `init` function of the implementation.
.. code-block:: cpp
// Subscribe to an error of the ErrorType "example/ExampleErrorA"
subscribe_error(
"example/ExampleErrorA", // ErrorType
[](Error error) { // callback
// Do something when the error is raised
},
[](Error error) { // clear_callback
// Do something when the error is cleared
}
);
// Subscribe to all errors of the requirement
subscribe_all_errors(
[](Error error) { // callback
// Do something when an error is raised
},
[](Error error) { // clear_callback
// Do something when an error is cleared
}
);
Subscribe to global all errors
------------------------------
Needs to be enabled in the manifest file of the module. May be done in the
`init` function of the implementation.
.. code-block:: cpp
// Subscribe to global all errors
subscribe_global_all_errors(
[](Error error) { // callback
// Do something when an error is raised
},
[](Error error) { // clear_callback
// Do something when an error is cleared
}
);
The ErrorFactory
----------------
Is used to create an error object.
.. code-block:: cpp
Error error_object_0 = this->error_factory->create_error();
Error error_object_1 = this->error_factory->create_error(
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
"This is an example error" // message
);
Error error_object_2 = this->error_factory->create_error(
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
"This is an example error", // message
Everest::error::Severity::High // severity
);
Error error_object_3 = this->error_factory->create_error(
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
"This is an example error", // message
Everest::error::State::Active // state
);
Error error_object_4 = this->error_factory->create_error(
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
"This is an example error", // message
Everest::error::Severity::High, // severity
Everest::error::State::Active // state
);
The ErrorStateMonitor
---------------------
Is used to monitor the error state of implementations and requirements.
Can be accessed in the implementation of an interface / anytime for
requirements.
Get the `ErrorStateMonitor`:
.. code-block:: cpp
// Get the ErrorStateMonitor of an implementation
std::shared_ptr<ErrorStateMonitor>& monitor = this->error_state_monitor;
// Get the ErrorStateMonitor of a requirement
std::shared_ptr<ErrorStateMonitor>& monitor = this->mod->r_example_raiser->error_state_monitor;
Check if an error is active:
.. code-block:: cpp
// Check if an error of the ErrorType "example/ExampleErrorA" is active
bool is_active = monitor->is_error_active(
"example/ExampleErrorA", // ErrorType
"" // ErrorSubType
);
Check if a specific set of errors is in a specific state:
.. code-block:: cpp
// Check if an error of the ErrorType "example/ExampleErrorA" is active
StateCondition condition = {
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
true // active
};
bool is_satisfied = monitor->is_condition_satisfied(condition);
// Check if multiple errors are active
std::list<StateCondition> conditions = {
{
"example/ExampleErrorA", // ErrorType
"", // ErrorSubType
true // active
},
{
"example/ExampleErrorB", // ErrorType
"", // ErrorSubType
true // active
}
};
bool are_satisfied = monitor->is_condition_satisfied(conditions);
Syntax in a Python module
=========================
You can find two example modules written in Python in the `examples` folder:
`PyExampleErrorRaiser` and `PyExampleErrorSubscriber`.
The error related classes need to be imported from the `everest` module.
.. code-block:: python
from everest.framework import error
Raise an error
--------------
Can be done in the implementation of an interface after initializing.
In opposite to the C++ implementation, the raise function is called on the
module object and takes additionally the `implementation_id` as argument.
.. code-block:: python
# Create an error object
error_object = self._mod.get_error_factory("example_raiser").create_error(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
"This is an example error" # message
)
# Raise the error
self._mod.raise_error(
"example_raiser", # implementation_id
error_object # error
)
Clear an error
--------------
Can be done in the implementation of an interface after raising.
In opposite to the C++ implementation, the clear function is called on the
module object and takes additionally the `implementation_id` as argument.
.. code-block:: python
# Clear all errors of the ErrorType "example/ExampleErrorA"
self._mod.clear_error(
"example_raiser", # implementation_id
"example/ExampleErrorA", # ErrorType
True # clear_all
)
# Clear the error with ErrorType "example/ExampleErrorA" and ErrorSubType ""
self._mod.clear_error(
"example_raiser", # implementation_id
"example/ExampleErrorA", # ErrorType
"" # ErrorSubType
)
self._mod.clear_error(
"example_raiser", # implementation_id
"example/ExampleErrorA", # ErrorType
False # clear_all
)
self._mod.clear_error(
"example_raiser", # implementation_id
"example/ExampleErrorA" # ErrorType
) # clear_all defaults to false
# Clear all errors of the current implementation
self._mod.clear_all_errors_of_impl(
"example_raiser" # implementation_id
)
Subscribe to an error
---------------------
Can be done in the `init` function of the implementation.
In opposite to the C++ implementation, the subscribe function is called on the
module object and takes additionally the `requirement` as argument.
.. code-block:: python
# Subscribe to an error of the ErrorType "example/ExampleErrorA"
self._mod.subscribe_error(
self._setup.connections["example_raiser"][0], # requirement
"example/ExampleErrorA", # ErrorType
lambda error: print("Error raised: ", error), # callback
lambda error: print("Error cleared: ", error) # clear_callback
)
# Subscribe to all errors of the requirement
self._mod.subscribe_all_errors(
self._setup.connections["example_raiser"][0], # implementation_id
lambda error: print("Error raised: ", error), # callback
lambda error: print("Error cleared: ", error) # clear_callback
)
Subscribe to global all errors
------------------------------
This feature is currently only available for C++ modules.
The ErrorFactory
----------------
Is used to create an error object.
.. code-block:: python
error_object_0 = self._mod.get_error_factory("example_raiser").create_error()
error_object_1 = self._mod.get_error_factory("example_raiser").create_error(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
"This is an example error" # message
)
error_object_2 = self._mod.get_error_factory("example_raiser").create_error(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
"This is an example error", # message
error.Severity.High # severity
)
error_object_3 = self._mod.get_error_factory("example_raiser").create_error(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
"This is an example error", # message
error.State.Active # state
)
error_object_4 = self._mod.get_error_factory("example_raiser").create_error(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
"This is an example error", # message
error.Severity.High, # severity
error.State.Active # state
)
The ErrorStateMonitor
---------------------
Get the `ErrorStateMonitor`:
.. code-block:: python
# Get the ErrorStateMonitor of an implementation
monitor = self._mod.get_error_state_monitor_impl(
"example_raiser" # implementation_id
)
# Get the ErrorStateMonitor of a requirement
monitor = self._mod.get_error_state_monitor_req(
self._setup.connections["example_raiser"][0] # requirement
)
Check if an error is active:
.. code-block:: python
# Check if an error of the ErrorType "example/ExampleErrorA" is active
is_active = monitor.is_error_active(
"example/ExampleErrorA", # ErrorType
"" # ErrorSubType
)
Check if a specific set of errors is in a specific state:
.. code-block:: python
# Check if an error of the ErrorType "example/ExampleErrorA" is active
condition = error.StateCondition(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
True # active
)
is_satisfied = monitor.is_condition_satisfied(condition)
# Check if multiple errors are active
conditions = [
error.StateCondition(
"example/ExampleErrorA", # ErrorType
"", # ErrorSubType
True # active
),
error.StateCondition(
"example/ExampleErrorB", # ErrorType
"", # ErrorSubType
True # active
)
]
are_satisfied = monitor.is_condition_satisfied(conditions)

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