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
13
tools/EVerest-main/docs/source/404.rst
Normal 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
|
||||
21
tools/EVerest-main/docs/source/_ext/staticpages/LICENSE
Normal 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.
|
||||
@@ -0,0 +1 @@
|
||||
__version__ = "0.2.0"
|
||||
329
tools/EVerest-main/docs/source/_ext/staticpages/extension.py
Normal 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,
|
||||
}
|
||||
79
tools/EVerest-main/docs/source/_ext/staticpages/utils.py
Normal 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]
|
||||
BIN
tools/EVerest-main/docs/source/_static/everest_icon-color.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
tools/EVerest-main/docs/source/_static/favicon.png
Normal file
|
After Width: | Height: | Size: 803 B |
@@ -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 %}
|
||||
102
tools/EVerest-main/docs/source/conf.py.in
Normal 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/'
|
||||
@@ -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.
|
||||
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 231 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 438 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 402 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 166 KiB |
@@ -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
|
||||
@@ -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, we’ll 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
|
||||
@@ -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
|
||||
487
tools/EVerest-main/docs/source/explanation/dev-tools/edm.rst
Normal 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
|
||||
229
tools/EVerest-main/docs/source/explanation/dev-tools/ev-cli.rst
Normal 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
|
||||
@@ -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
|
||||
@@ -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="<mxfile><diagram id="LkL3k23o4wMPku4sNzY7" name="Page-1">5ZfBcpswEIafhjsgm9jH2CXpITl5pj2rsEVqBcvIwth9+opoZaySNJnpxM64F4/07Qppf/3egYit6/295q14xBJUlMblPmKfojRdJjP7O4CDAxmbO1BpWTqUjGAjfwHBmGgnS9gGiQZRGdmGsMCmgcIEjGuNfZj2HVW4a8srmIBNwdWUfpWlEY4u5vHIP4OshN85iSlSc59MYCt4if0JYnnE1hrRuFG9X4MatPO6uHV3L0SPB9PQmLcsSN2CHVcd1UbnMgdfLJS2dpqiNgIrbLjKR7rS2DUlDE+M7WzMeUBsLUws/AHGHOgieWfQImFqRVFoytvhWuy0wQYcuZNK0SOnVVGhW+x0QeckTxmuK6CszKGhgpNlpMQ9YA1GH2yCBsWN3IW3y8kk1TFv1NEOSMrnZWVXLeviUrLOnpE1U/ZUq292UA2DtdRFJ43NWmngP0EfM7RP8cTudlyWsdsotfvHrBWTqwovohfSwKblTwL1tr+For8o6g60gf1fBaMom1Oh1B5n1Cz6sdckvoGIkz6Txf8u8fx6nJtNnbu8lHOzd3MuSz+Qc1MWOjdZntG6N9dj3cXUuv7d5/zeXfwf3p3dXNC7y9c1zr9s8uRtuibZB9L1z57A2Bl19f+a14RNr8Cw7ymsnY4fJU+xky87lv8G</diagram></mxfile>">
|
||||
<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 |
@@ -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="<mxfile><diagram id="LkL3k23o4wMPku4sNzY7" name="Page-1">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</diagram></mxfile>">
|
||||
<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 |
@@ -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="<mxfile><diagram id="LkL3k23o4wMPku4sNzY7" name="Page-1">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==</diagram></mxfile>">
|
||||
<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 |
@@ -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="<mxfile><diagram id="LkL3k23o4wMPku4sNzY7" name="Page-1">jZNNb4QgEIZ/DXeFXbM9ttbdXnoyac9UpkKCYlh2dfvri2VQ7KZJL2Z45gveGQkru+lk+SBfjQBNaCYmwp4JpQ/5zn9ncAugYPsAWqtEQPkKavUFCDOkFyXgvAl0xminhi1sTN9D4zaMW2vGbdin0duuA2/hDtQN1/f0XQknAz3ss5W/gGpl7Jxn6Ol4DEZwllyYMUGsIqy0xrhgdVMJetYu6hLyjn94l4tZ6N1/EmhIuHJ9wbcRWmif+vThjXY2qre6itAXWvgSaH8Toa4RMfpIaOk7sEEmRZII1MHdorjWXHoB8/0y7x6lclAPvJm9o98mz6TrtD/lS3b6XpTgCtbBlCB8/wlMB87efAh62R63EZfxsMPZjOto8zgvmYy1QMZxm9ql9Cq4N1DzeFxn++NLfhBWfQM=</diagram></mxfile>">
|
||||
<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 |
@@ -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="<mxfile><diagram id="LkL3k23o4wMPku4sNzY7" name="Page-1">zZRNb6MwEIZ/DXfABXWPTUqzh+4pUnt28RR71zBoMoRkf/2aMoSg9EuqKu0F2c+8tmfesYnUuj5sSLf2FxrwURqbQ6RuozT9kVyF7wCOI8hVNoKKnBlRMoOt+wsCY6GdM7BbCBnRs2uXsMSmgZIXTBNhv5Q9o1+e2uoKLsC21P6SPjrDdqTXWTzzn+AqO52cxBKp9SQWsLPaYH+GVBGpNSHyOKoPa/CDd5Mv47q7N6KnxAga/syCVNLg41QbmFCqTJHYYoWN9sVMV4RdY2DYIA6zWXOP2AaYBPgbmI/SN90xBmS59hKFxtwMXQjTBhsYyZ3zXra8LELq2mFHpeSppO+aKhCV3KqhgrNlUvgGsAamYxAQeM1uv2ymljtRnXSzbWEgzr3uouSy176TTaM09yGr1VMYVMNg7ajsHAfVikD/ATopaJJMJJx2WpbkN1Eazo9Vay9atWxEbx3DttUvBvXh3S1Nf9PUPRDD4V3DJKoycViebTbd4n5+BMnE7NkDyOOve3z1scfFw7b4nK0q/Y9tzbPvszVM55/LS+zsD62Kfw==</diagram></mxfile>">
|
||||
<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 |
@@ -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="<mxfile><diagram id="Z5-atGQC2fyeHDTmyhzX" name="Page-1">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</diagram></mxfile>">
|
||||
<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 |
@@ -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.
|
||||
179
tools/EVerest-main/docs/source/explanation/error-framework.rst
Normal 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).
|
||||
@@ -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
|
||||
395
tools/EVerest-main/docs/source/explanation/hardware-drivers.rst
Normal 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>`
|
||||
@@ -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.
|
||||
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 109 KiB |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
After Width: | Height: | Size: 80 KiB |
117
tools/EVerest-main/docs/source/explanation/index.rst
Normal 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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
149
tools/EVerest-main/docs/source/explanation/pnc-process.rst
Normal 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
|
||||
@@ -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
|
||||
@@ -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>`_, ...)
|
||||
@@ -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);
|
||||
|
||||
181
tools/EVerest-main/docs/source/how-to-guides/bringup/ac.rst
Normal 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
|
||||
396
tools/EVerest-main/docs/source/how-to-guides/bringup/basic.rst
Normal 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 tool’s *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
|
||||
386
tools/EVerest-main/docs/source/how-to-guides/bringup/dc.rst
Normal 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 IMD’s
|
||||
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
|
||||
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 534 KiB |
|
After Width: | Height: | Size: 579 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 240 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 62 KiB |
147
tools/EVerest-main/docs/source/how-to-guides/bringup/index.rst
Normal 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, let’s 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
|
||||
@@ -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 don’t have a test load, you can also test
|
||||
this when doing the first charging tests with a real car.
|
||||
|
||||
----
|
||||
|
||||
**Authors**: Cornelius Claussen
|
||||
@@ -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.
|
||||
111
tools/EVerest-main/docs/source/how-to-guides/configure-pnc.rst
Normal 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
|
||||
138
tools/EVerest-main/docs/source/how-to-guides/debug-iso15118.rst
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
43
tools/EVerest-main/docs/source/how-to-guides/eichrecht.rst
Normal 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
|
||||
@@ -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)
|
||||
@@ -0,0 +1,104 @@
|
||||
.. _htg_getting_started_hw:
|
||||
|
||||
#########################
|
||||
Get started with Hardware
|
||||
#########################
|
||||
|
||||
There are different approaches for getting started with running EVerest
|
||||
on hardware. For each approach we have collected some starting points
|
||||
and best practices to help you get up and running with EVerest.
|
||||
|
||||
For each of the following starting scenarios, you can find more detail
|
||||
in the sections below:
|
||||
|
||||
- **Using an EVerest-compatible development kit and add the BSP.** This
|
||||
is a great path to start learning about the benefits of EVerest. A
|
||||
device including a deployed EVerest image can let you experience
|
||||
charging in a more practical manner moving beyond software simulation.
|
||||
|
||||
- **Start with your own hardware.** This is a less out-of-the-box
|
||||
start, but a more direct path to your own system environment. We will
|
||||
explain how to get there below.
|
||||
|
||||
Using an EVerest-compatible development kit
|
||||
===========================================
|
||||
|
||||
The easiest way to get started with hardware is to use one of the development kits.
|
||||
They can charge a real car out of the box and you can evaluate all features of
|
||||
EVerest before building your own product.
|
||||
|
||||
Additionally, they come with a ready-to-use Yocto image with RAUC OTA updates, OCPP,
|
||||
ISO 15118 and all other features of EVerest.
|
||||
|
||||
They will help you to:
|
||||
|
||||
* parallelize HW and SW developments for new charger projects,
|
||||
* test OCPP backends (CSMS) against EVerest,
|
||||
* explore new charging algorithms without the need of doing all the groundwork and
|
||||
* rapidly integrate EV charging with other applications.
|
||||
|
||||
Currently, there are three development kits available. Choose the one that matches
|
||||
your product the closest:
|
||||
|
||||
1. **AC all-integrated PCB development kit: The BelayBox.**
|
||||
It is available at https://shop.pionix.com with a touch screen display,
|
||||
up to 22 kW 3ph AC charging, RCD, PCB-integrated power meter, RFID reader and
|
||||
a Raspberry Pi CM4 compute module.
|
||||
|
||||
Schematics and MCU firmware are open source:
|
||||
https://github.com/PionixPublic/reference-hardware
|
||||
|
||||
Yocto for the BelayBox is available at:
|
||||
https://github.com/PionixPublic/dev-hardware-yocto
|
||||
|
||||
Find the manual here: https://pionix.com/user-manual-belaybox
|
||||
|
||||
2. **AC DIN Rail / Dual public charging**
|
||||
Dual socket AC charging with DIN rail contactors and power meters can be realized
|
||||
with the phyVERSO available from Phytec:
|
||||
https://www.phytec.de/ladeelektronik/komplettloesung
|
||||
|
||||
The advantage of this solution is the seamless transition to production: The phyVERSO
|
||||
is production-ready and can be used as is in volume production. Customization service
|
||||
is also available from Phytec to build custom derivatives with different interfaces
|
||||
and form factors to perfectly meet your requirements.
|
||||
|
||||
3. **DC DIN Rail / Dual public charging**
|
||||
Similar to (2), the phyVERSO can be used in a DC configuration (both ports can be
|
||||
configured for AC or DC; also a mixed configuration is possible). Phytec offers a
|
||||
DC development kit as well, which includes a 40 kW DC power supply, isolation monitor,
|
||||
power meter and everything else needed to make it a complete charger ready for
|
||||
evaluation.
|
||||
|
||||
.. note::
|
||||
|
||||
Keep in mind that the development kits were not designed to be a certifiable product.
|
||||
They are optimized to be easily accessible for developers.
|
||||
|
||||
Start with your own hardware
|
||||
----------------------------
|
||||
|
||||
If you already want to start integrating EVerest with
|
||||
your existing charger hardware, we recommend to start reading through
|
||||
the sections about setting up your Linux/Yocto and cross-compiling.
|
||||
You will find these sections in the :doc:`Linux / Yocto overview </explanation/linux-yocto/index>`.
|
||||
|
||||
.. note::
|
||||
|
||||
This is not the easiest way to start.
|
||||
But should you choose this mission, you will go the most direct way to use
|
||||
EVerest for production-ready charger development.
|
||||
Tell us about your experience and where you get stuck on the way.
|
||||
The mountain top can best be reached together.
|
||||
|
||||
We recommend to start your journey by copying an already existing image
|
||||
(like the phyVERSO or Yeti/Yak ones) and change this according to your
|
||||
needs and HW setup.
|
||||
|
||||
This will give you an overview on which in- and outputs are required,
|
||||
the dependencies per module and how to set up the MQTT communication
|
||||
accordingly.
|
||||
|
||||
-----------------------------------
|
||||
|
||||
**Authors**: Cornelius Claussen, Manuel Ziegler
|
||||
@@ -0,0 +1,548 @@
|
||||
.. _htg_getting_started_sw:
|
||||
|
||||
#######################
|
||||
Get started in Software
|
||||
#######################
|
||||
|
||||
Prepare Your Development Environment
|
||||
====================================
|
||||
|
||||
This guide will help you to set up a development environment for EVerest on
|
||||
your local machine.
|
||||
|
||||
For a native build, EVerest requires a Linux based system.
|
||||
|
||||
To build on a Windows or Mac system, you can use WSL2 (Windows) or
|
||||
Docker / Podman (Mac).
|
||||
|
||||
System Requirements and Dependencies
|
||||
====================================
|
||||
|
||||
This section lists all dependencies and supported environments required to set up
|
||||
your development environment and build EVerest.
|
||||
|
||||
.. attention::
|
||||
|
||||
As an alternative approach you can make use of development containers.
|
||||
EVerest comes with a ready to use configuration. Consult the associated
|
||||
:doc:`tutorial </tutorials/setup-devcontainer/index>` to find out how.
|
||||
|
||||
General Requirements
|
||||
--------------------
|
||||
|
||||
* Python (greater than 3.6)
|
||||
* Jinja2
|
||||
* PyYAML
|
||||
* Compiler:
|
||||
|
||||
* GCC 9 (lower versions could work with some tweaking but are not
|
||||
recommended)
|
||||
* Clang (starting with version 12) has been used with EVerest but is not
|
||||
officially supported.
|
||||
|
||||
Tested Linux Distributions
|
||||
--------------------------
|
||||
|
||||
**Ubuntu**:
|
||||
|
||||
.. warning::
|
||||
|
||||
Ubuntu 20.04 and 22.04 may not be supported anymore.
|
||||
|
||||
Tested with Ubuntu 24.04 or newer.
|
||||
|
||||
Use `apt` to get your needed libraries installed:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y python3-pip python3-venv git rsync wget cmake doxygen \
|
||||
graphviz build-essential clang-tidy cppcheck openjdk-17-jdk npm docker.io \
|
||||
docker-compose libboost-all-dev nodejs libssl-dev libsqlite3-dev \
|
||||
clang-format curl rfkill libpcap-dev libevent-dev pkg-config libcap-dev \
|
||||
libsystemd-dev
|
||||
|
||||
**OpenSUSE**:
|
||||
|
||||
Use `zypper` to get your needed libraries installed:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
zypper update && zypper install -y sudo shadow
|
||||
zypper install -y --type pattern devel_basis
|
||||
zypper install -y git rsync wget cmake doxygen graphviz clang-tools cppcheck \
|
||||
boost-devel libboost_filesystem-devel libboost_log-devel \
|
||||
libboost_program_options-devel libboost_system-devel libboost_thread-devel \
|
||||
java-17-openjdk java-17-openjdk-devel nodejs nodejs-devel npm python3-devel \
|
||||
python3-pip gcc-c++ libopenssl-devel sqlite3-devel libpcap-devel \
|
||||
libevent-devel libcap-devel
|
||||
|
||||
**Fedora**:
|
||||
|
||||
Tested with Fedora 40, 41 and 42. Here is how to get your needed libraries with
|
||||
`dnf`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo dnf update
|
||||
sudo dnf install make automake gcc gcc-c++ kernel-devel python3-pip python3-devel \
|
||||
git rsync wget cmake doxygen graphviz clang-tools-extra cppcheck java-21-openjdk \
|
||||
java-21-openjdk-devel boost-devel nodejs nodejs-devel npm openssl openssl-devel \
|
||||
libsqlite3x-devel curl rfkill libpcap-devel libevent-devel libcap-devel
|
||||
|
||||
.. _htg_getting_started_sw_download_install:
|
||||
|
||||
Download And Install EVerest
|
||||
=============================
|
||||
|
||||
EVerest's main application code is located in `EVerest <https://github.com/EVerest/EVerest>`_. It makes use of various
|
||||
libraries which are included in the same repository for the most part. External dependencies
|
||||
are dynamically loaded via `CPM <https://github.com/cpm-cmake/CPM.cmake>`_.
|
||||
|
||||
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>`_.
|
||||
|
||||
Make sure to set the ``PATH`` and the ``CPM_SOURCE_CACHE`` variable in your shell profile:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export CPM_SOURCE_CACHE=$HOME/.cache/CPM
|
||||
export PATH=$PATH:/home/$(whoami)/.local/bin
|
||||
|
||||
.. note::
|
||||
In the past the EVerest Dependency Manager - short ``edm`` - helped you with orchestrating
|
||||
and pulling in the dependencies in the build process of ``EVerest``. Since EVerest has
|
||||
been restructered into a (quasi)-mono-repository, manual installation of ``edm`` is no longer
|
||||
recommended for building recent releases of EVerest (2026 and later).
|
||||
For more details about ``edm``, see the dedicated :doc:`edm documentation </explanation/dev-tools/edm>`.
|
||||
|
||||
We can now continue to build ``EVerest``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/EVerest/EVerest
|
||||
mkdir build && cd build
|
||||
cmake ../EVerest/
|
||||
make -j$(nproc) install
|
||||
|
||||
.. _htg_getting_started_sw_simulate:
|
||||
|
||||
Simulating EVerest
|
||||
==================
|
||||
|
||||
The following sections explains how to get EVerest running in a software-in-the-loop.
|
||||
|
||||
.. _htg_getting_started_sw_helpers:
|
||||
|
||||
Prepare The Helpers
|
||||
-------------------
|
||||
|
||||
EVerest provides some Docker containers that help with the simulation.
|
||||
One container is used to run an MQTT Broker (mosquitto), which is required to run EVerest.
|
||||
This documentation section shows the necessary steps to start the simulation and get a
|
||||
simple Node-RED user interface running.
|
||||
|
||||
.. hint::
|
||||
To get all this working, make sure you have docker and docker-compose installed during the previous install phase.
|
||||
If not, see install instructions for `docker <https://docs.docker.com/engine/install/#server>`_ and
|
||||
`docker-compose <https://docs.docker.com/compose/install/#install-compose>`_.
|
||||
|
||||
In order for custom or local containers being able to talk to the services,
|
||||
provided by the docker-compose containers, we need to create a common docker
|
||||
network. It is called ``infranet_network`` and needs to be created by the
|
||||
following command (IPv6 is enabled for containers which might need it):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker network create --driver bridge --ipv6 --subnet fd00::/80 infranet_network --attachable
|
||||
|
||||
Now, start the mosquitto broker, which is deployed as built docker image.
|
||||
It is used for the communication between the EVerest modules:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -d --name mqtt-server --network infranet_network -p 1883:1883 -p 9001:9001 ghcr.io/everest/containers/mosquitto:docker-images-v0.1.0
|
||||
|
||||
|
||||
That makes us ready for entering the simulation phase described in the next
|
||||
chapter.
|
||||
|
||||
Software in a loop
|
||||
------------------
|
||||
|
||||
In the following, we will start EVerest as a simple AC charging station with
|
||||
software-in-the-loop configuration. This means that all hardware related
|
||||
parts like Powermeter, RFID-Reader are loaded as simulated modules.
|
||||
Also the Electric Vehicle simulations run as part of EVerest.
|
||||
|
||||
Change to the directory ``EVerest/build``, which has been created during
|
||||
EVerest install.
|
||||
|
||||
Since the EVerest config we are going to use includes ISO15118 functionality on the EV
|
||||
side, we need to source the preinstalled virtual environment and install the respective
|
||||
python requirements for ISO15118 using a make target:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
source venv/bin/activate
|
||||
make iso15118_pip_install_dist
|
||||
|
||||
Now we can start EVerest with a software-in-the-loop configuration via script.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./run-scripts/run-sil.sh
|
||||
|
||||
This script starts EVerest with EVerest with a pre-defined
|
||||
configuration for software-in-the-loop simulation. Every module specified in
|
||||
that configuration is started as an independent process.
|
||||
|
||||
In a new terminal window, run the following Node-RED script:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./run-scripts/nodered-sil.sh
|
||||
|
||||
For a user interface, just direct your browser to `<http://localhost:1880/ui>`_
|
||||
- the required web-server has already been started via the shell scripts.
|
||||
|
||||
This will let us control the simulation in a very simple GUI.
|
||||
|
||||
You can analyse the output of the two shell scripts in the terminal windows to
|
||||
get a little bit of insights about what is going on and which ports are used
|
||||
etc.
|
||||
|
||||
In the GUI, you can simulate car charging sessions using the available buttons,
|
||||
e.g. `CAR PLUGIN`, `PAUSE`, `RESUME` and so on:
|
||||
|
||||
.. image:: images/quick-start-sil-gui.png
|
||||
:width: 200px
|
||||
|
||||
Having a very first basic feeling for that will be enough for now.
|
||||
|
||||
.. _htg_getting_started_sw_admin_panel:
|
||||
|
||||
Admin panel and simulations
|
||||
===========================
|
||||
|
||||
.. important::
|
||||
|
||||
Be aware, that the Admin Panel is currently under development.
|
||||
The former version of the Admin Panel, which was integrated in EVerest,
|
||||
will be removed. See the new standalone version which runs without an
|
||||
EVerest instance here:
|
||||
`Admin Panel GitHub repository <https://github.com/EVerest/everest-admin-panel>`_
|
||||
.
|
||||
|
||||
You can glue together the modules of EVerest (and also your custom ones) with
|
||||
the help of EVerest's framework mechanisms. This way, you define simulation
|
||||
flows with which you can test and analyze complicated systems.
|
||||
|
||||
As EVerest is a modular framework, you can define connections and data flows
|
||||
in a nice network of modules. As it would be a little bit exhausting to
|
||||
configure everything via code or config files, there is a nice helper: The
|
||||
admin panel.
|
||||
|
||||
It gives you an overview of modules and connections between them. In a
|
||||
diagram, you can see and understand the simulation with all interfaces,
|
||||
configs, data flows and so on.
|
||||
|
||||
.. note::
|
||||
|
||||
See
|
||||
`Admin Panel GitHub repository <https://github.com/EVerest/everest-admin-panel>`_
|
||||
for information about how to start the Admin Panel. The screenshots and the
|
||||
documentation in this subsection might be different than what you see on
|
||||
your screen. This is due to the currently ongoing changes in the Admin
|
||||
Panel. This will be updated soon.
|
||||
|
||||
You should see a rather empty page like that:
|
||||
|
||||
.. image:: images/quick-start-admin-panel-1.png
|
||||
:width: 360px
|
||||
|
||||
Click on the menu symbol on the upper left corner of the page, then click on
|
||||
config. A left side column with further menu items opens. Choose `Available
|
||||
configs`:
|
||||
|
||||
.. image:: images/quick-start-admin-panel-2.png
|
||||
:width: 360px
|
||||
|
||||
If you are here for the first time, you will see all pre-configured Node-RED
|
||||
flows here. For a first intro, you may want to take a look at *config-sil*.
|
||||
|
||||
After opening it, you can see a diagram representation of some modules with
|
||||
connections between them.
|
||||
|
||||
Next, let's see how fast we can create a new module in EVerest.
|
||||
|
||||
.. _htg_getting_started_sw_understand_modules:
|
||||
|
||||
Understanding EVerest Modules
|
||||
=============================
|
||||
|
||||
You reached the phase of writing a new EVerest module. Congrats!
|
||||
|
||||
For this Quick Start Guide, we will give you a rocket start of understanding
|
||||
the basic elements of the EVerest module concept.
|
||||
|
||||
.. note::
|
||||
|
||||
Modules can be implemented in C++, Python or Rust.
|
||||
We will stick to C++ in the examples below.
|
||||
|
||||
So, let's dig into the overview:
|
||||
|
||||
EVerest is a modular framework. So, there are lots of modules for different
|
||||
entities in ``EVerest/modules``:
|
||||
|
||||
- EvseManager (a charging port as part of a charging station)
|
||||
- Hardware driver modules
|
||||
- Protocol implementations
|
||||
- Car simulation modules
|
||||
- Authentication modules
|
||||
- Energy management modules
|
||||
|
||||
and so on.
|
||||
|
||||
Of course, you can change the functionality of those modules or add your
|
||||
custom ones to the whole module stack.
|
||||
|
||||
In simple terms, a new module can be created by describing its structure
|
||||
via a manifest file and interface files. After that, an EVerest helper tool (:ref:`ev-cli <exp_dev_tools_evcli>`)
|
||||
will create the necessary files as stub files, so that you can implement the
|
||||
details. The EVerest framework will also know how the modules can be connected
|
||||
by the restrictions you set in the manifest.
|
||||
|
||||
How does all that look like? Read the next section!
|
||||
|
||||
.. _htg_getting_started_sw_basic_elements:
|
||||
|
||||
Basic elements of a module
|
||||
---------------------------
|
||||
|
||||
Module manifest
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Let's look at the first step: Describing the structure of a new module.
|
||||
Starting with the manifest file, which could look like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
:linenos:
|
||||
|
||||
description: Describing what this module does.
|
||||
config:
|
||||
some_key:
|
||||
description: Describe the effect of this config key.
|
||||
type: boolean
|
||||
default: false
|
||||
provides:
|
||||
main:
|
||||
interface: myinterface
|
||||
description: Describe what the implementation of this interface does.
|
||||
requires:
|
||||
some_implementation:
|
||||
interface: externalinterface
|
||||
min_connections: 0
|
||||
max_connections: 2
|
||||
enable_external_mqtt: true
|
||||
metadata:
|
||||
license: https://spdx.org/licenses/Apache-2.0.html
|
||||
authors:
|
||||
- Your name, your company
|
||||
|
||||
Most of this should be self-explanatory. Just a few words:
|
||||
|
||||
The config section gives you the possbility to define some config keys for the
|
||||
module to re-use it for different scenarios in your workspace.
|
||||
|
||||
In line 7, the *provides* section let's you tell other modules what your
|
||||
module is able to do. You tell the EVerest module framework which interfaces
|
||||
have been implemented - for example, a power meter. Of course, you can
|
||||
implement more than one interface and list all of that in the *provides*
|
||||
section.
|
||||
|
||||
Line 11 starts with the requirements of your own module. This is the other
|
||||
side: Your module tells the EVerest module framework which implementations it
|
||||
will require to work.
|
||||
|
||||
With the ``min_connections`` and ``max_connections`` keys you can configure how
|
||||
many connections are required or allowed for your module.
|
||||
|
||||
In EVerest, you find a manifest file for each module. See the module
|
||||
directories in *EVerest/modules*.
|
||||
|
||||
Interfaces
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
An interface describes - like a kind of construction manual - which information
|
||||
it delivers and which functionality it provides for other modules to use.
|
||||
|
||||
A module, that implements an interface, publishes information via **VARs** (short
|
||||
for variables). **VARs** can be consumed by connected modules. Functionality is
|
||||
provided by **CMDs** (commands, that can be called from other modules).
|
||||
|
||||
VARs and CMDs are defined in the interface files. Remember the manifest file?
|
||||
The previous section showed that the manifest file defines which interfaces your
|
||||
module implements. Those interfaces could already exist. If not, you would have
|
||||
to create a new one. EVerest contains a rich set of interfaces defining common
|
||||
functionality of a charging station software stack.
|
||||
|
||||
You can find all interface source files in the directory
|
||||
*EVerest/interfaces* as yaml files or their
|
||||
respective documentation in the :doc:`EVerest Reference Documentation </reference/types_index>`.
|
||||
|
||||
This is an easy interface as an example:
|
||||
|
||||
.. code-block:: yaml
|
||||
:linenos:
|
||||
|
||||
description: Describe why we need this interface.
|
||||
cmds:
|
||||
get_id:
|
||||
description: Describe what this command does when called.
|
||||
arguments:
|
||||
verbose:
|
||||
description: An example for a method argument.
|
||||
type: boolean
|
||||
result:
|
||||
description: Explain the return value.
|
||||
type: integer
|
||||
vars:
|
||||
temperature:
|
||||
description: Describe this value that gets published.
|
||||
type: integer
|
||||
limits:
|
||||
description: Describe this struct that gets published.
|
||||
$ref: /typedef#/Limits
|
||||
|
||||
A short interface file, but lots to learn here:
|
||||
|
||||
You can see one CMD defined, which has the name *get_id*. If you want to
|
||||
implement this interface (and *provide* the functionality of the interface
|
||||
to other modules), this is the method you will have to fill with code in your
|
||||
implementation later.
|
||||
|
||||
There is one argument defined for the method called *verbose*. A return value
|
||||
of type *integer* rounds things up for the one CMD of this interface.
|
||||
|
||||
VARs are pieces of information which get published for the network of
|
||||
listening modules regularly. We have two VARs in this example. The first one
|
||||
is of type *integer*, the second one is a reference to a type definition.
|
||||
|
||||
This way, you can create structs or classes (however you would call a bunch
|
||||
of simple data-types grouped inside of one logical unit) for publishing.
|
||||
|
||||
.. note::
|
||||
|
||||
In some yaml interface files in the EVerest GitHub project, you will still
|
||||
find an additional type attribute besides a ``$ref`` attribute. In most cases,
|
||||
the type will be of value ``object``. This is considered bad practice and will
|
||||
be deprecated in future versions.
|
||||
|
||||
Let's have a look at a type definition in the next section.
|
||||
|
||||
Types
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
As you have seen in the example interface yaml, you can use *types* instead
|
||||
of primitive data types (like boolean, integer, string).
|
||||
|
||||
In the interface, you saw a reference to an EVerest type definition.
|
||||
|
||||
You can find the type definitions as yaml files in the following directory:
|
||||
|
||||
*EVerest/types* or their respective
|
||||
documentation in the :doc:`EVerest Reference Documentation </reference/index>`.
|
||||
|
||||
An easy definition of a type could look like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
:linenos:
|
||||
|
||||
description: Describe which group of types will follow.
|
||||
types:
|
||||
SomeType:
|
||||
description: Describe this type.
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
property_1:
|
||||
description: Describe the first property.
|
||||
type: boolean
|
||||
property_2:
|
||||
description: Describe the second property.
|
||||
type: number
|
||||
|
||||
You can see one defined type here. It has two properties. A property could
|
||||
again be another type reference.
|
||||
|
||||
Now, as we have defined everything, it is time to let the EVerest command line
|
||||
interface - the :ref:`ev-cli tool <exp_dev_tools_evcli>` - generate the implementation stubs.
|
||||
|
||||
Generate the stub files
|
||||
---------------------------
|
||||
|
||||
You can use ``ev-cli`` to generate stub files for a module. Everything that you need
|
||||
is a module directory within ``EVerest/modules`` containing a ``manifest.yaml`` file
|
||||
described above.
|
||||
|
||||
Assuming the module is defined inside the ``EVSE`` directory you can use :ref:`ev-cli <exp_dev_tools_evcli>` to create
|
||||
the module skeleton like this:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
ev-cli mod create EVSE/MyModuleName
|
||||
|
||||
Your main cpp file will have two special functions:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void MyModuleName::init() {}
|
||||
void MyModuleName::ready() {}
|
||||
|
||||
When initialising, the EVerest framework will call all ``init()`` functions of all
|
||||
modules one after the other. After having initialised all modules in that way,
|
||||
the framework calls the ``ready()`` functions.
|
||||
|
||||
This allows you to do setup things that relate only to your current module in
|
||||
the ``init()`` function and all stuff requiring other modules being initialised in
|
||||
your ``ready()`` function.
|
||||
|
||||
Furthermore, you will get generated files for all interfaces that you
|
||||
declared to be implemented in your module. Those interface files will contain
|
||||
handler methods for the CMDs you have declared in the interface files.
|
||||
|
||||
You can walk through the generated files in your new module directory and
|
||||
have a look at the prepared classes.
|
||||
|
||||
Please see the documentation about :ref:`exp_dev_tools_evcli` for further documentation.
|
||||
|
||||
Or - if you rather would like to have more theoretical input about EVerest
|
||||
modules - continue with the
|
||||
:doc:`EVerest Module Concept page </explanation/detail-module-concept>`.
|
||||
|
||||
One Deep Breath And Next Steps
|
||||
===============================
|
||||
|
||||
You made it. Great!
|
||||
|
||||
Probably, now is a good time to take a deep breath and review what you have
|
||||
learnt about EVerest.
|
||||
|
||||
You might have generated stub files now but still are not sure how to procede
|
||||
with implementing your specific scenarios?
|
||||
|
||||
Good news: A tutorial about developing EVerest modules is waiting for you.
|
||||
|
||||
:ref:`Continue with the tutorial here! <tutorial_develop_new_everest_module>`
|
||||
|
||||
See you in our :ref:`weekly tech meetings <project-community>` and thanks for
|
||||
being a part of the EVerest community!
|
||||
|
||||
----
|
||||
|
||||
**Authors**: Manuel Ziegler, Piet Gömpel, Dominik Kolmann, Andreas Heinrich, Philip Molares, Tobias Marzell
|
||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 86 KiB |