Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

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

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

View File

@@ -0,0 +1,210 @@
.. _tutorial_bazel:
==========================
Experimental Bazel Support
==========================
Introduction
------------
EVerest offers support for `Bazel <https://bazel.build/>`.
With Bazel, you can efficiently build, test and deploy software projects of any
size.
For EVerest developers, Bazel offers a couple advantages:
* While developing features, that span multiple modules, Bazel can swiftly
rebuild only the necessary parts of the project.
* If you have already setup Bazel in your project, EVerest framework can be
integrated with it.
This tutorial will guide you through the process of setting up and using
Bazel in your EVerest projects.
Getting Started
---------------
To install Bazel, it's recommended to use bazelisk, which is a tool that
downloads and runs the correct version of Bazel for your project.
You can install bazelisk by following the
`instructions on the official GitHub repository <https://github.com/bazelbuild/bazelisk?tab=readme-ov-file#installation>`.
.. note::
Bazelisk provides a `bazel` command, and the rest of this tutorial will
refer to it as `bazel`.
C/C++ Compilers:
At the moment, Bazel will use the default C/C++ compilers on your system.
If it is not the desired compiler to build EVerest, you can set the environment
variables `CC` and `CXX` to the desired compiler.
All other dependencies are fetched by Bazel as needed.
Using Bazel Commands
--------------------
Once Bazel is configured, you can use various Bazel commands to build, test
and run.
Most useful commands are:
* `bazel build //...` - Build all targets in the project.
* `bazel test //...` - Run all tests in the project.
Dependency Management
---------------------
There are a few different ways of managing dependencies in EVerest.
* Dependencies that CMake takes from the system (e.g. boost).
These dependencies are configured in the `third_party/bazel/repos.bzl` file.
* Dependencies that are described in the `./dependencies.yaml` file. These
dependencies are pulled automatically by Bazel with help of `edm` tool.
* Rust dependencies are managed by cargo, and are described in the `Cargo.toml`
file. Cargo dependencies are automatically picked up by Bazel.
Defining C++ EVerest Modules
----------------------------
Let's assume, you have a module named `Example` with a single interface
implementation named `example`. This module depends on the `sigslot` library.
For a more realistic scenario, the module has two extra files `utils.cpp` and
`utils.hpp`.
.. code-block:: bash
modules/
└── Example
├── BUILD.bazel
├── CMakeLists.txt
├── Example.cpp
├── Example.hpp
├── manifest.yaml
├── utils.cpp
├── utils.hpp
└── example/
├── exampleImpl.cpp
└── exampleImpl.hpp
The `manifest.yaml` file for the module looks like this:
.. code-block:: yaml
description: Simple example module written in C++
provides:
example:
interface: example
description: This implements an example interface that uses multiple framework features
...
requires:
kvs:
interface: kvs
enable_external_mqtt: true
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Example Authors
To build this module with Bazel, you need to create a `BUILD.bazel` file in the
module directory, next to the `manifest.yaml` file.
In the `BUILD.bazel`, use predefined macros to define the module:
.. code-block:: python
load("//modules:module.bzl", "cc_everest_module")
cc_everest_module(
# Name of the module, must be the same as the directory name.
name = "Example",
deps = [
# List of libraries, that module depends on.
# In CMakeLists.txt these are typically added as `target_link_libraries`.
# The should be listed in the `third_party/bazel/repos.bzl` file.
# Note that header-only libraries should be added here as well.
"@sigslot//:sigslot",
],
impls = [
# List of implementations in the module.
# This should correspond to the list of keys in the
# `provides` section of the manifest.yaml file.
"example",
],
# List of additional source files of the module.
#
# Here you only have to list the files that are not autogenerated.
# The mandatory module files are added automatically.
srcs = [
"utils.cpp",
"utils.hpp",
],
# Alternatively, you can use `glob` function to list files.
# srcs = glob(
# [
# "*.cpp",
# "*.hpp",
# ],
# ),
)
Defining Rust EVerest Modules
-----------------------------
To define a Rust module in EVerest, you need to create a BUILD.bazel file in
the module directory.
Generic `rust_binary` and `rust_test` are used at the moment.
.. code-block:: python
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
load("@everest_core_crate_index//:defs.bzl", "all_crate_deps")
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
# cargo_build_script describes to Bazel how to run autogeneration of the code.
# This should be pretty-much the same for every module
cargo_build_script(
name = "build_script",
srcs = ["build.rs"],
edition="2021",
build_script_env = {
# This is the path relative to the module directory.
"EVEREST_CORE_ROOT": "../..",
},
data = [
"manifest.yaml",
"@everest-core//interfaces",
"@everest-core//types",
],
deps = all_crate_deps(build = True),
)
# The module is described as a rust_binary at the moment.
rust_binary(
# Name of the module, must be the same as the directory name.
name = "RsIskraMeterBinary",
# List of source files of the module.
# In most cases this glob should be enough.
srcs = glob(["src/*.rs"]),
# Rust language edition, used in this module.
edition="2021",
# Bazel makes distinctions between dependencies needed on different
# stages of the build. This is the list of proc_macro dependencies.
# In most cases this is enough
proc_macro_deps = all_crate_deps(proc_macro = True),
visibility = ["//visibility:public"],
# List of "normal" dependencies.
# all_crate_deps will add all the dependencies from the Cargo.toml file.
# We need as well to add framework, bridge, and the result of the build_script.
# Typically this is enough.
deps = all_crate_deps(normal = True) + [
":build_script",
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
],
)
----
**Authors**: Evgeny Petrov

View File

@@ -0,0 +1,609 @@
.. _tutorial_develop_new_everest_module:
Develop New Modules
*******************
.. hint::
The module concept will be improved in the next couple of months with
new or changed features. We will reflect those changes in the documentation,
but be aware that there might be some fragments that still show some "old
style". We would be happy if you drop us a line via the
`EVerest mailing list <https://lists.lfenergy.org/g/everest>`_ in this case.
This tutorial is meant to show how to develop your own module for the EVerest
framework. This tutorial builds on knowledge that is explained in the
:ref:`Quick Start Guide <htg_getting_started_sw>`, which is a good starting
point for understanding the central concepts of modules.
Goal Of This Tutorial
=====================
In this tutorial, we will keep everything as simple as possible. You will
learn the following:
1. Create an EVerest module that implements an interface.
2. Define an interface containing a simple command with parameter and return
value.
3. Define a configuration parameter for the new module.
4. Configure required dependencies of the EVerest framework and modules.
5. Build and run.
Install Prerequisites
=====================
Please follow the :ref:`Quick Start Guide <htg_getting_started_sw>` to install
all prerequisites into your environment.
After that, you should in particular have:
- *optional:* The EVerest dependency manager (check via ``edm --version``)
- The EVerest cli utility (check via ``ev-cli --version``)
- A running MQTT broker (e.g. started as container as described in the setup
guide; per default expected on localhost on port 1883)
Workspace Setup
===============
This section describes how to setup an EVerest workspace (or at least the
necessary parts of it) to develop EVerest modules.
First, create a directory for your workspace. In the following, we will assume
the environment variable ``EVEREST_WORKSPACE`` to hold this directory::
export EVEREST_WORKSPACE=~/ev-workspace
mkdir -p $EVEREST_WORKSPACE
Now you have two options:
Alternative 1: Clone required repositories (recommended for recent versions)
----------------------------------------------------------------------------
Just clone this, to get the required dependencies::
git clone https://github.com/EVerest/everest-cmake.git ${EVEREST_WORKSPACE}/everest-cmake
git clone https://github.com/EVerest/EVerest.git ${EVEREST_WORKSPACE}/EVerest
Alternative 2: Setup Workspace via EVerest Dependency Manager (recommended for pre-2026 version)
------------------------------------------------------------------------------------------------
Use the EVerest dependency manager to setup your workspace. This has the
benefit of allowing your to select a specific snapshot (see the corresponding
:ref:`docs <exp_dev_tools_edm>` for details), as well as it is a single command::
edm init --workspace $EVEREST_WORKSPACE
cd $EVEREST_WORKSPACE
As of 2026 the EVerest dependency manager layouts the workspace to be compatible with
older non-mono-repository versions of EVerest (or, as formerly called, `everest-core`). Assuming you want to build a recent
version of everest, you can delete the dependencies which are part of the mono-repo today.
**Remove everything except** ``EVerest/`` **and** ``everest-cmake/`` **.**
The result should look like::
$ ls
EVerest everest-cmake
Create Module Skeleton
======================
The following describes how to define the fundamental skeleton of a module.
This includes configuration files and the auto-generation of source files.
First, create an empty folder that is to contain your module. In the
following, we assume the environment variable ``EVEREST_TUTORIAL_DIR`` to hold
this directory, for example::
export EVEREST_TUTORIAL_DIR=~/everest-tutorial-module
git clone https://github.com/EVerest/everest-template.git $EVEREST_TUTORIAL_DIR
This provides you in particular with the ``.clang-format`` and ``.eslintrc.json``
files.
.. note::
You may want to change the origin remote in this git repository if you are
going to use it further on. See suitable Git documentation how to do that.
You might also want to check that you do not take the Git history of the
`everest-template` repository with you.
Interface Configuration
-----------------------
First, we define an interface that the module provides.
In this very first minimal version, the interface shall provide a single
command `command_tutorial` that can be called by another module and will
just always return the string "everest" as result. We will later give some
ideas how to extend this minimal example.
We store the following interface configuration in form of a .yaml file in
``$EVEREST_TUTORIAL_DIR/interfaces/interface_tutorial_module.yaml``:
.. code-block:: yaml
description: The interface of the tutorial module.
cmds:
command_tutorial:
description: A command the tutorial module's interface provides. It receives a simple string.
arguments:
payload:
description: An arbitrary string that can be sent to the module.
type: string
result:
description: The answer of the module (which per default will just be "everest").
type: string
Second, we use the ``ev-cli`` tool to auto-generate header files for this
interface::
cd $EVEREST_TUTORIAL_DIR && ev-cli interface generate-headers --everest-dir . --schemas-dir $EVEREST_WORKSPACE/EVerest/lib/everest/framework/schemas interface_tutorial_module
After this, you should find the following file tree structure in your module::
.
├── build
│ └── generated
│ └── include
│ └── generated
│ └── interfaces
│ └── interface_tutorial_module
│ ├── Implementation.hpp
│ ├── Interface.hpp
│ └── Types.hpp
└── interfaces
└── interface_tutorial_module.yaml
These auto-generated header files in particular provide you with static type
checks when developing against your individual interface.
.. hint::
It is also possible to work with Python or Rust. At this point
we will focus on C++.
Module Configuration
--------------------
Next, we will define our module's manifest.
For this, we create a module directory::
mkdir -p $EVEREST_TUTORIAL_DIR/modules/TutorialModule
and in it, create file
``$EVEREST_TUTORIAL_DIR/modules/TutorialModule/manifest.yaml`` with the
following content:
.. code-block:: yaml
description: The EVerest Tutorial Module
config:
config_tutorial_switch:
description: A boolean configuration parameter.
type: boolean
default: false
provides:
interface_impl_tutorial_module:
interface: interface_tutorial_module
description: An actual implementation in your module of "interface_tutorial_module" interface.
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- <Your Name>, <Your Organization>
In particular, this manifest declares the following:
- A boolean configuration parameter `config_tutorial_switch` of the module we
can define later at runtime.
- The module will implement the `interface_tutorial_module` interface (that
we have defined before). We give this implementation the name `interface_impl_tutorial_module`.
Again, we use the EVerest cli tool to auto-generate code from this
configuration::
cd $EVEREST_TUTORIAL_DIR && ev-cli module create --everest-dir . --schemas-dir $EVEREST_WORKSPACE/EVerest/lib/everest/framework/schemas TutorialModule --licenses $EVEREST_WORKSPACE/EVerest/applications/utils/ev-dev-tools/src/ev_cli/licenses
After that, you should have the following file structure::
.
├── build
│ └── generated
│ (...)
├── config
├── interfaces
│ └── interface_tutorial_module.yaml
└── modules
└── TutorialModule
├── CMakeLists.txt
├── TutorialModule.cpp
├── TutorialModule.hpp
├── doc.rst
├── docs
│ └── index.rst
├── interface_impl_tutorial_module
│ ├── interface_tutorial_moduleImpl.cpp
│ └── interface_tutorial_moduleImpl.hpp
└── manifest.yaml
Let us point out a few particularly important files:
**TutorialModule{.hpp,.cpp}:**
This provides the module's callbacks called by the EVerest framework at
startup, more precisely in initialization and system-ready state. Here,
the auto-generated implementation already calls the respective callbacks of
its interface implementations.
Among others, you'll furthermore find the module's configuration and a pointer
to the interface implementations.
**interface_impl_tutorial_module/interface_tutorial_moduleImpl{.hpp,.cpp}:**
For each interface implementation (here, only a single one is defined), the
respective header and source files are generated.
The header file declares particular spots for non-auto-generated code.
Observe that the default implementation of the handler of the
`command_tutorial` coincidentally already satisfies this tutorial's needs:
.. code-block:: cpp
std::string interface_tutorial_moduleImpl::handle_command_tutorial(std::string& payload) {
// your code for cmd command_tutorial goes here
return "everest";
}
Build Configuration & Build
===========================
This section describes the additional steps needed to build your project and
install it.
Adding CMakeLists.txt in the root directory
-------------------------------------------
The ``$EVEREST_TUTORIAL_DIR/CMakeLists.txt`` file in the root of your project
repository will need to import some build commands from the framework, as well
as link to its dependencies. A fairly simple file that includes
``everest-core`` into the build would look as follows::
cmake_minimum_required(VERSION 3.14.7)
project(everest-tutorial VERSION 0.1
DESCRIPTION "EVerest tutorial modules"
LANGUAGES CXX C)
find_package(everest-cmake 0.1 REQUIRED
COMPONENTS bundling
PATHS ../everest-cmake
)
# options
option(BUILD_TESTING "Run unit tests" OFF)
option(CMAKE_RUN_CLANG_TIDY "Run clang-tidy" OFF)
# dependencies
if (NOT DISABLE_EDM)
evc_setup_edm()
else()
find_package(everest-core)
# InfyPowerACDC uses pal-sigslot
find_package(PalSigslot REQUIRED)
endif()
ev_add_project()
# config (not needed if you do not need a run script for your configuration)
# add_subdirectory(config)
# configure clang-tidy if requested
if(CMAKE_RUN_CLANG_TIDY)
message("Enabling clang-tidy")
set(CMAKE_CXX_CLANG_TIDY clang-tidy)
endif()
# testing
if(BUILD_TESTING)
include(CTest)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
evc_include(CodeCoverage)
append_coverage_compiler_flags()
add_subdirectory(tests)
setup_target_for_coverage_gcovr_html(
NAME gcovr_coverage
EXECUTABLE test_config
DEPENDENCIES test_config everest
)
setup_target_for_coverage_lcov(
NAME lcov_coverage
EXECUTABLE test_config
DEPENDENCIES test_config everest
)
endif()
Adding ``modules/CMakeLists.txt``
---------------------------------
Next, ``$EVEREST_TUTORIAL_DIR/modules/CMakeLists.txt`` essentially tells CMake
where to look for modules, in order to add them to the build.
It contains a single line per module. To proceed, create the file and then add the following line, as per our example::
ev_add_module(TutorialModule)
Note that you could also develop several modules at once in your repository.
In that case you would add a respective ``ev_add_module(<MODULE_NAME>)`` line
for each of those.
Adding ``dependencies.yaml``
----------------------------
The ``find-package()`` CMake directive found in the previous sections is
handled by EDM.
In order for this to work, you need to add a dependency file for EDM, called
``dependencies.yaml``, in the project root directory. For example, listing
only ``EVerest`` as a dependency looks like this::
---
EVerest:
git: https://github.com/EVerest/EVerest.git
git_tag: main
With the above setup taken care of, you are now ready to build the project.
Building
--------
When you auto-generated the code for the interfaces and modules, a ``build/``
directory should have appeared.
You can also build the project there - go to it, and configure the build::
cd $EVEREST_TUTORIAL_DIR/build
CMAKE_PREFIX_PATH=$EVEREST_WORKSPACE cmake --install-prefix $EVEREST_TUTORIAL_DIR/dist ..
Here, we added two instructions to cmake:
- Setting ``CMAKE_PREFIX_PATH=$EVEREST_WORKSPACE`` allows cmake to find the
``everest-cmake`` repository in the workspace.
- Specifying the ``--install-prefix`` allows you to specify where the finished
binaries will be placed, e.g. into the ``dist/`` folder in the modules repository.
EVerest can be installed system wide (e.g. into ``users/local/bin``), but this
usually requires ``sudo`` permissions.
Then, build and install the project::
cd $EVEREST_TUTORIAL_DIR/build
make -j <number of parallel jobs>
And finally, install the binaries::
make install -j <number of parallel jobs>
If everything worked smoothly so far, your modules are installed and ready to
run.
Run Configuration & Run
=======================
EVerest configuration
---------------------
The final step to run EVerest and testing the new module is to define an
EVerest run configuration. For this, create a file
``$EVEREST_TUTORIAL_DIR/config/config-modules-tutorial.yaml`` with the following
content:
.. code-block:: yaml
active_modules:
tutorial_module_instance:
module: TutorialModule
This provides a very minimalistic run configuration for EVerest telling to run
with a single module, namely the newly created one.
Adding and activating ``config/CMakeLists.txt``
-----------------------------------------------
The EVerest cmake utils provide a function to auto-generate run scripts for
your configurations.
In order to achieve this, create the file ``$EVEREST_TUTORIAL_DIR/config/CMakeLists.txt`` with content::
generate_config_run_script(CONFIG modules-tutorial)
Here, the ``generate_config_run_script(<CONFIG_NAME>)`` expects the existence of
a file ``config-<CONFIG_NAME>.yaml``.
It will then generate a run script for this configuration.
You must then "activate" this configuraton by adapting the
``$EVEREST_TUTORIAL_DIR/CMakeLists.txt`` file removing the commenting ``#``
before the ``add_subdirectory(config)`` instruction, i.e.::
# config
# (not needed if you do not need a run script for your configuration)
add_subdirectory(config)
After that, once more run cmake::
cd $EVEREST_TUTORIAL_DIR/build
CMAKE_PREFIX_PATH=$EVEREST_WORKSPACE cmake --install-prefix $EVEREST_TUTORIAL_DIR/dist ..
make
make install
Running EVerest
---------------
The step before should have created a file
``$EVEREST_TUTORIAL_DIR/build/run-scripts/run-modules-tutorial.sh``.
Up to path substitution this will have the following content::
LD_LIBRARY_PATH=$EVEREST_TUTORIAL_DIR/dist/lib:$LD_LIBRARY_PATH \
PATH=$EVEREST_TUTORIAL_DIR/dist/bin:$PATH \
manager \
--prefix $EVEREST_TUTORIAL_DIR/dist \
--conf $EVEREST_TUTORIAL_DIR/config/config-modules-tutorial.yaml
It puts the compiled libraries and binaries into the respective paths, and
then runs EVerest by calling the `manager` binary with the respective
configuration.
Importantly, the configuration must not be known before runtime (since also in
our example it was only used to generate the run script, not to build the
project!).
Executing ``run-modules-tutorial.sh`` then should start EVerest, and provide
an output similar to::
YYYY-MM-DD 00:00:12.500139 [INFO] manager :: 8< 8< 8< ------------------------------------------------------------------------------ 8< 8< 8<
YYYY-MM-DD 00:00:12.500327 [INFO] manager :: EVerest manager starting using /home/everest/everest-module-tutorial/config/config-modules-tutorial.yaml
YYYY-MM-DD 00:00:12.500354 [INFO] manager :: EVerest using MQTT broker localhost:1883
YYYY-MM-DD 00:00:12.799618 [INFO] everest_ctrl :: everest controller process started ...
YYYY-MM-DD 00:00:12.799822 [INFO] everest_ctrl :: Launching controller service on port 8849
YYYY-MM-DD 00:00:13.120267 [INFO] tutorial_module :: Module tutorial_module_instance initialized.
YYYY-MM-DD 00:00:13.149934 [INFO] manager :: >>> All modules are initialized. EVerest up and running <<<
If your socket can't be connected, make sure that your MQTT brocker is running.
Observing the System
====================
In this final section we describe how to observe the behavior of your module
and debug it.
Exploring with MQTT Explorer
----------------------------
The open-source tool `MQTT Explorer <https://github.com/thomasnordquist/MQTT-Explorer>`_ can be utilized to
observe the module communication in EVerest.
With your MQTT broker running on localhost:1883, you should be able to connect
right away when opening MQTT explorer.
Then start (or re-start) the EVerest manager (as described above). You should
notice an `everest` topic popping up.
We can now publish a command to our self-written module. For this, choose the topic::
everest/modules/tutorial_module_instance/impl/interface_impl_tutorial_module/cmd/command_tutorial
and publish the JSON::
{
"msg_type": "Cmd",
"data": {
"args": {
"payload": "Hello World!"
},
"id": "00000000-0000-0000-0000-000000000042",
"origin": "manual_test"
}
}
Our module should return with a "everest" response (you may have to reselect
the ``everest/tutorial_module_instance/interface_impl_tutorial_module/cmd``
on the left to refresh this view.
The response should be on topic::
everest/modules/tutorial_module_instance/impl/interface_impl_tutorial_module/cmd/command_tutorial/response/manual_test
.. image:: images/mqtt_explorer_example.png
.. _tutorials_develop_new_module_debugging:
Debugging
---------
At the latest when you start developing an actual module, it might come handy
to be able to debug your code. Thus, the following shall provide some
rudimentary steps to do so.
*1) Rebuild with debug flags enabled*
Rerun Cmake, this time with `-DCMAKE_BUILD_TYPE=Debug`, and rebuild::
cd $EVEREST_TUTORIAL_DIR/build
CMAKE_PREFIX_PATH=$EVEREST_WORKSPACE cmake --install-prefix $EVEREST_TUTORIAL_DIR/dist -DCMAKE_BUILD_TYPE=Debug ..
make -j <number of parallel jobs>
*2) Start EVerest with your module with your module marked as "standalone"*
With EVerest built as described before, but with the additonal option
``--standalone tutorial_module_instance``::
LD_LIBRARY_PATH=$EVEREST_TUTORIAL_DIR/dist/lib:$LD_LIBRARY_PATH \
PATH=$EVEREST_TUTORIAL_DIR/dist/bin:$PATH \
manager --prefix $EVEREST_TUTORIAL_DIR/dist --conf $EVEREST_TUTORIAL_DIR/config/config-modules-tutorial.yaml --standalone tutorial_module_instance
This starts EVerest, but without your module. Instead, the output contains a
line::
manager :: Not starting standalone module: tutorial_module_instance
Also, so far the output should be missing the ``All modules are initialized. EVerest up and running``
notification, since it keeps waiting for your module
to spin up.
*3) Start your module with a debugger*:
Now open a second terminal (while keeping EVerest running in the frist
terminal), and start your module via ``gdb``::
cd $EVEREST_TUTORIAL_DIR/build
gdb --args ./modules/TutorialModule/TutorialModule --module tutorial_module_instance --conf $EVEREST_TUTORIAL_DIR/config/config-modules-tutorial.yaml --prefix $EVEREST_TUTORIAL_DIR/dist
In gdb, we set a break in the line that returns the payload when your test
command is hit.
We then run the program (you might need to adjust the line number)::
break modules/TutorialModule/interface_impl_tutorial_module/interface_tutorial_moduleImpl.cpp:17
run
After the ``run`` command, you should notify in your EVerest terminal that all
modules have now started. You may now again use MQTT Explorer as before and
send a command call via MQTT, this should hit your set breakpoint with a
output similar to::
Thread 4 "tutorial_module" hit Breakpoint 1, module::interface_impl_tutorial_module::interface_tutorial_moduleImpl::handle_command_tutorial (this=0xaaaaaad24fc0, payload="mock_transaction_id") at /tmp/everest-tutorial-verify/modules/TutorialModule/interface_impl_tutorial_module/interface_tutorial_moduleImpl.cpp:17
17 return "everest";
Of course, you might setup your favorite IDE in a similar way for a nicer
debugging experience.
Exemplary Module Customizations
===============================
Having prepared a buildable and runnable module, we can now extend the logic
of our implementation:
- Add a variable to your interface, and publish it;
- Add a second module which requires the ``interface_tutorial_module`` interface and sends commands or subscribes to variables.
- ...
.. hint::
This section is yet to come. Want to help us with that? Feel free and create
a suggestion for this.
--------------------------------
**Authors**: Valentin Dimov, Manuel Ziegler, Andreas Heinrich, Lukas Mertens, Martin Litre, Piet Gömpel, Christoph Burandt

View File

@@ -0,0 +1,219 @@
.. _tutorial_everest_api:
********************
Using the EVerestAPI
********************
.. note::
Find in-depth explanations about the EVerestAPI :doc:`here <../explanation/adapt-everest/apis>`.
Introduction
============
The EVerestAPI is designed to remain as stable as possible across different releases of EVerest.
It provides JSON-encoded data access via MQTT. The API serves as a stable extension point for applications
developed outside the EVerest framework that need to exchange data with it.
Architecture
============
The EVerestAPI is specified using AsyncAPI 3.0 documents: :doc:`EVerestAPI Reference </reference/api/autogenerated_api_index>`.
The individual APIs generally match the internal EVerest interfaces and are implemented as standard EVerest modules.
For more details, see the :doc:`EVerestAPI modules reference documentation </reference/modules/API/EVerestAPI/autogenerated>`.
As a rule of thumb, each internal interface is typically provided by one module.
Configuring the required APIs is done by adding the corresponding modules to your EVerest configuration;
they connect to other modules using the standard EVerest logic.
Configuration
=============
The ``config/bringup/config-bringup-EVerestAPI-entrypoint.yaml`` configuration file demonstrates the API in action.
It does not implement internal functionality, but provides several API and BringUp modules for manual interaction.
.. hint::
It is advisable to set the access specification in the configuration to ``allow_global_read: true`` for EVerestAPI modules.
This allows the modules to determine if multiple EVerestAPI modules are active, preventing the initial ``ready_beacon``
from being sent multiple times.
Example
=======
First, start an MQTT monitoring tool and subscribe to the ``everest_api/#`` topic.
Start the example configuration:
.. code-block:: bash
build $ ./run-scripts/run-bringup-EVerestAPI-entrypoint.sh
You will see the EVerest log alongside two panes showing power supply control UIs.
A ``everest_api/ready_beacon`` with an empty JSON payload indicates that an EVerestAPI is available.
Following this, messages will be periodically published to topics such as:
``everest_api/1/power_supply_DC/ps_dc_1/e2m/heartbeat``.
.. note::
To exit the tmux session, press ``Ctrl+B`` and then ``d``.
MQTT Topic Structure
====================
Topics follow the structure: ``everest_api/{api-version}/{api-type}/{instance-id}/{direction}/{api-subtopic}``.
- **api-version**: Version of the API (e.g., *1*)
- **api-type**: Type of the API (e.g., *power_supply_DC*)
- **instance-id**: Name of the API instance, allowing multiple instances of the same type (typically the module_id, e.g., *ps_dc_1*)
- **{e2m|m2e}**:
- **e2m**: everest-to-(api-client)-module — Published by EVerest, subscribed to by the client.
- **m2e**: (api-client)-module-to-everest — Published by the client, subscribed to by the EVerestAPI.
- **api-subtopic**: The actual subject of the topic (e.g., *heartbeat*).
Communication Monitoring
========================
To ensure system reliability, both ends of the communication must know if the other side is still active.
Two distinct mechanisms serve this goal.
Monitoring EVerestAPI Endpoints
-------------------------------
The EVerestAPI module periodically publishes to the topic:
``everest_api/{api-version}/{api-type}/{instance-id}/e2m/heartbeat``
The payload is a periodically incrementing integer. An API client can use this heartbeat to detect
if the API module (e.g., *ps_dc_1*) is still alive. The heartbeat interval is defined in the manifest
or configuration file via ``cfg_heartbeat_interval_ms``.
.. tip::
Look for other heartbeat signals and compare the topic structure to the designators in the configuration file.
Observe how different intervals affect the timing in the MQTT logs.
Monitoring API Clients
----------------------
To enable the EVerestAPI module to detect if a client is still alive, set ``cfg_communication_check_to_s``
to a value greater than 0. It is the client application's responsibility to periodically send ``true``
(as a raw value, not enclosed in ``{}``) to the following topic:
``everest_api/{api-version}/{api-type}/{instance-id}/m2e/communication_check``
If a client fails to check in, the API modules will raise an error within EVerest
(e.g., "Error raised, type: generic/CommunicationFault ..."), though they will continue to function on their side.
.. tip::
Try clearing errors by sending the appropriate communication check messages, and observe how they are
raised again once the timeout expires.
Request-Reply-Timeout
---------------------
Some modules offer a ``cfg_request_reply_to_s`` parameter. This is used when an EVerestAPI module
implements an internal EVerest interface and provides commands to other modules.
When another module calls a command on that interface, the following sequence occurs:
* The API module attempts to fulfill the call by writing to the respective API topic.
* The API client (having subscribed to this topic) begins the actual work.
* The API client reports completion via the reply topic.
* The API module relays this reply back to the internal interface.
If no reply arrives within the specified timeout, the API module issues a default reply
to the internal interface to unblock the calling module.
The Request-Reply Pattern
=========================
API command calls are asynchronous. Triggering a command and receiving a result are separate events in time.
To execute a command, the caller writes a message to the topic defined in the API specification using this JSON structure:
.. code:: json
{
"headers": {
"replyTo": "reply/to/address"
},
"payload": {}
}
- ``payload``: Required only when calling commands that have non-empty arguments.
- ``headers``: Required whenever the caller expects a result. The result is published to the replyTo topic provided by the caller.
.. note::
Callers may omit the headers key if they do not require a result and only wish to trigger the command.
This pattern applies to both directions (API module calling a client command and vice versa).
entrypoint_API
==============
The **entrypoint_API** allows clients to discover available API endpoints dynamically.
ready_beacon
------------
The ``everest_api/ready_beacon`` mentioned earlier is part of this discovery system.
Discovering the API
-------------------
To perform a basic query, send the following JSON to ``everest_api/discover``:
.. code:: json
{
"headers": {
"replyTo": "reply/to/address"
}
}
In the example configuration, this results in four individual responses that the client must aggregate.
Example response:
.. code:: json
{
"apis": [
{
"communication_monitoring": {
"communication_check_period_s": 10,
"heartbeat_period_ms": 10000
},
"module_id": "ps_dc_1",
"type": "power_supply_DC",
"version": 1
}
]
}
The modules reply directly from memory; therefore, a 1-second timeout is generally sufficient to gather all replies.
Note that the "apis" key contains an array. While usually containing one entry, an API module
could implement multiple interfaces and report them all within this array.
Filtered Queries
----------------
Alternatively, you can restrict responders to a specific type by sending the query to:
``everest_api/query-modules/power_supply_DC``
.. code:: json
{
"headers": {
"replyTo": "reply/to/address"
}
}
The results will be filtered by the selected API type. Use the returned module_id, type,
and version to construct topics as described in the MQTT Topic Structure section.
.. note::
Even when using this discovery mechanism, a client must have prior knowledge of the subtopics available for a given API type.

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@@ -0,0 +1,64 @@
#########
Tutorials
#########
Learn a new skill by following our step-by-step experiences.
Have a look at this categorized list of all EVerest tutorials:
.. grid:: 1 2 2 3
:gutter: 2
.. grid-item-card:: Develop New EVerest Modules
:link: develop-new-module
:link-type: doc
Write an EVerest module from scratch.
.. grid-item-card:: OCPP 1.6
:link: ocpp16
:link-type: doc
Learn how to use OCPP 1.6J in EVerest; how to configure it; how to test it with SteVe as a local CSMS on your local PC
.. grid-item-card:: OCPP 2.0.1 & 2.1
:link: ocpp2
:link-type: doc
Learn how to use OCPP 2.x in EVerest; how to configure it; or to test it on your local PC
.. grid-item-card:: Plug & Charge
:link: plug-and-charge
:link-type: doc
Learn how to run a Plug-&-Charge simulation on your PC
.. grid-item-card:: Using the Bazel Build Tool
:link: bazel
:link-type: doc
Learn how to use the Experimental Bazel Support in EVerest
.. grid-item-card:: Setup the EVerest Development Container
:link: setup-devcontainer/index
:link-type: doc
Learn how to setup a development container for EVerest development.
.. grid-item-card:: Get to know the EVerestAPI
:link: everest_api
:link-type: doc
Learn how the EVerestAPI can be used in your setup
.. toctree::
:maxdepth: 1
:hidden:
develop-new-module
ocpp16
ocpp2
plug-and-charge
bazel
EVerest devcontainer <setup-devcontainer/index>
everest_api

View File

@@ -0,0 +1,367 @@
.. _tutorial-ocpp16:
###################
OCPP 1.6 in EVerest
###################
.. note::
EVerest has an implementation of OCPP 1.6J and 2.0.1 and 2.1. This tutorial is about
the 1.6 implementation. To get documentation about all implemented versions,
see `lib/ocpp in the EVerest repository <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp>`_.
EVerest provides a complete implementation of Open Charge Point Protocol
(OCPP) 1.6J, supporting all feature profiles including Plug&Charge and the
Security Extensions.
The source code of `libocpp` is hosted as part of the EVerest repository:
`<https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp>`_.
This is a tutorial about how to set up and configure OCPP 1.6 in EVerest.
This tutorial includes:
- How to run EVerest SIL with the default OCPP 1.6J configuration connecting to
SteVe
- How to load the OCPP 1.6 module as part of the EVerest configuration
- How to configure OCPP 1.6 configuration keys
- How to connect to different CSMS
.. _tutorial_ocpp16_prerequisites:
Prerequisites
=============
If you're new to EVerest start with our :ref:`Quick Start Guide <htg_getting_started_sw>`
to get a simulation in EVerest running for the first time.
It is important that you have set up the required docker containers for
Mosquitto and SteVe, which we will use as an example CSMS.
If you have done that successfully, you can move on with this tutorial.
.. _tutorial_ocpp16_run_with_steve:
Run EVerest SIL with SteVe
==========================
The EVerest repository provides a configuration that you can use to run EVerest with OCPP.
By default this configuration is connecting to the Open Source CSMS
`SteVe <https://github.com/steve-community/steve>`_.
Make sure that SteVe is running on your machine as the CSMS we connect to.
You have to add the chargepoint id *cp001* in SteVe's webinterface to allow a
charging station to connect.
If you want to simulate charging sessions, you also need to add OCPP tags for
the authorization in SteVe.
Simply run
.. code-block:: bash
${EVEREST_WORKSPACE:?}/EVerest/build/run-scripts/run-sil-ocpp.sh
to start EVerest with OCPP 1.6J. You can start playing around with central
system-initiated messages and use the EVerest simulation to start charging
sessions.
You can find the OCPP message log in different formats in the
`/tmp/everest_ocpp_logs` directory. A new logfile is created every time EVerest
is started.
.. _tutorial_ocpp16_configure_ocpp:
OCPP configuration file
=======================
In addition to the EVerest configuration yaml file, OCPP 1.6 is configured
using a JSON configuration file.
This configuration file can contain all configuration keys from the OCPP 1.6
specification.
Examples for that can be found `here <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp/config/v16>`__.
This is the one we used to connect to SteVe:
.. code-block:: json
{
"Internal": {
"ChargePointId": "cp001",
"CentralSystemURI": "127.0.0.1:8180/steve/websocket/CentralSystemService/",
"ChargeBoxSerialNumber": "cp001",
"ChargePointModel": "Yeti",
"ChargePointVendor": "Pionix",
"FirmwareVersion": "0.1",
"AllowChargingProfileWithoutStartSchedule": true,
"UseTPM" : false,
"LogMessagesFormat": ["html","security"]
},
"Core": {
"AuthorizeRemoteTxRequests": false,
"ClockAlignedDataInterval": 900,
"ConnectionTimeOut": 30,
"ConnectorPhaseRotation": "0.RST,1.RST",
"GetConfigurationMaxKeys": 100,
"HeartbeatInterval": 86400,
"LocalAuthorizeOffline": false,
"LocalPreAuthorize": false,
"MeterValuesAlignedData": "Energy.Active.Import.Register",
"MeterValuesSampledData": "Energy.Active.Import.Register,SoC",
"MeterValueSampleInterval": 60,
"NumberOfConnectors": 1,
"ResetRetries": 1,
"StopTransactionOnEVSideDisconnect": true,
"StopTransactionOnInvalidId": true,
"StopTxnAlignedData": "Energy.Active.Import.Register",
"StopTxnSampledData": "Energy.Active.Import.Register",
"SupportedFeatureProfiles": "Core,FirmwareManagement,RemoteTrigger,Reservation,LocalAuthListManagement,SmartCharging",
"TransactionMessageAttempts": 1,
"TransactionMessageRetryInterval": 10,
"UnlockConnectorOnEVSideDisconnect": true,
"WebsocketPingInterval": 0
},
"FirmwareManagement": {
"SupportedFileTransferProtocols": "FTP"
},
"Security": {
"CpoName": "Pionix",
"AuthorizationKey": "AABBCCDDEEFFGGHH",
"SecurityProfile": 1
},
"LocalAuthListManagement": {
"LocalAuthListEnabled": true,
"LocalAuthListMaxLength": 42,
"SendLocalListMaxLength": 42
},
"SmartCharging": {
"ChargeProfileMaxStackLevel": 42,
"ChargingScheduleAllowedChargingRateUnit": "Current,Power",
"ChargingScheduleMaxPeriods": 42,
"MaxChargingProfilesInstalled": 42
},
"PnC": {
"ISO15118CertificateManagementEnabled": true,
"ISO15118PnCEnabled": true,
"ContractValidationOffline": true
},
"CostAndPrice": {
"CustomDisplayCostAndPrice": false
},
"Custom": {
"ExampleConfigurationKey": "example"
}
}
The configuration keys are split up into the feature profiles that are
specified in OCPP 1.6 plus the extra profiles *Internal*, *Security*, *PnC* and
*CostAndPrice*.
Here's a short overview of the purpose of each profile in the configuration file:
- Internal: Used for internal configuration keys that are not specified in
OCPP 1.6
- Core: Includes Core configuration keys of OCPP 1.6
- FirmwareManagement: Includes configuration keys that apply when the feature
profile FirmwareManagement is implemented
- Security: Includes configuration parameters that have been introduced within
the OCPP 1.6J Security Whitepaper
- LocalAuthListManagement: Includes configuration parameters that apply when
the feature profile LocalAuthListManagement is implemented
- SmartCharging: Includes configuration parameters that apply when the feature
profile SmartCharging is implemented
- PnC: Used for Plug&Charge and includes configuration parameters that have
been introduced within the OCPP 1.6J Plug&Charge Whitepaper
- CostAndPrice: Used for enabling support for the California Pricing Whitepaper
EVerest's `libocpp` supports almost all configuration parameters that are specified
within OCPP 1.6. Despite that, it is possible to omit configuration parameters
that are not required and it is even possible to omit a whole feature profile
in the configuration file if it is not supported. This means that the
configuration of the `libocpp` provides maximum flexibility and can be
tailored to your specific charging station.
.. note::
There is a lot to configure with OCPP. Make sure to thoroughly read through
the OCPP 1.6 specification and the
`profile schemas <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp/config/v16/profile_schemas>`_
and configure OCPP according to your needs.
.. _tutorial_ocpp16_connect_different_csms:
Connect to a different CSMS
===========================
In order to connect to a different CSMS, you have to modify the connection
details within the OCPP configuration file. The OCPP config is a separate
JSON file in which all configuration keys of OCPP 1.6 plus some internal parameters
can be configured.
You can specify the path to this configuration file inside the ``EVerest``
configuration file using the configuration parameter ``ChargePointConfigPath``
of the OCPP module within EVerest. This defaults to *ocpp-config.json*.
If this path is relative, the default path for the OCPP configuration
*dist/share/everest/modules/OCPP* will be prepended.
To connect to a different CSMS, you have to modify the connection details of
the ocpp configuration file. This includes the parameter ``CentralSystemURI``
and it might also include to change the parameters ``AuthorizationKey`` and
``SecurityProfile``. Here's a short overview of the purpose of the parameters:
- ChargePointId: Identity of the charging station
- CentralSystemURI: Specifies the endpoint of the CSMS
- Can optionally include the ChargePointId after the last back-slash of the URI
- SecurityProfile: Specifies the SecurityProfile which defines type of
transport layer connection between ChargePoint and CSMS
- Can have the value 0, 1, 2 or 3
- SecurityProfile 0: Unsecure transport without Basic Authentication (ws://)
- SecurityProfile 1: Unsecure transport with Basic Authentication (ws://)
- SecurityProfile 2: TLS with Basic authentication (wss://)
- SecurityProfile 3: TLS with client side certificates (wss://)
- AuthorizationKey: Specifies the password used for HTTP Basic Authentication
- Must be set if SecurityProfile is 1 or 2, can be omitted if
SecurityProfile is 0 or 3
- Minimal length: 16 bytes
Modify these parameters according to the connection requirements of the CSMS. Find all available configuration keys
and their descriptions in `here <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp/config/v16/profile_schemas>`_
.. note::
For TLS, it might be required to prepare the required certificates and
private keys. Please see the documentation of the
:ref:`EvseSecurity module <everest_modules_EvseSecurity>`
for more information on how to set up the TLS connection for OCPP.
.. _tutorial_ocpp16_configure_ocpp_everest:
Configuring OCPP 1.6 within EVerest
===================================
To be able to follow the further explanations, you should be familiar with the configuration of EVerest modules.
Take a look into :doc:`EVerest Module Concept </explanation/detail-module-concept>` for that.
To configure the OCPP module of EVerest, find the available configuration parameters
in the :ref:`module documentation <everest_modules_OCPP>`, and read through them
carefully in order to configure it according to your needs.
In order to enable OCPP 1.6 in EVerest, you need to load the module in the
EVerest configuration file and set up the module connections.
The interfaces provided and required by the OCPP module and its purposes are
described in the
:ref:`module documentation <everest_modules_OCPP>`.
The EVerest configuration file
`config-sil-ocpp.yaml <https://github.com/EVerest/EVerest/blob/main/config/config-sil-ocpp.yaml>`_
which was used previously serves as a good example for how the connections of
the module could be set up.
Here is a quick list of things you should remember when adding OCPP to your
EVerest configuration file:
1. **Load the OCPP module** by including it in the configuration file.
2. **Add and connect the required modules:**
- **evse_manager** (interface: ``energy_manager``, 1 to 128 connections):
OCPP requires this connection to integrate with the charge control logic of EVerest.
One connection represents one EVSE.
To manage multiple EVSEs via one OCPP connection, multiple connections must be configured
in the EVerest configuration file.
*Module implementation typically used to fulfill this requirement:*
``EvseManager``, ``implementation_id: evse``
- **evse_energy_sink** (interface: ``external_energy_limits``, 0 to 128):
OCPP optionally requires this connection to communicate smart charging limits received
from the CSMS within EVerest.
Typically, ``EnergyNode`` modules are used to fulfill this requirement.
Configure one ``EnergyNode`` module per EVSE and one extra for *evse id* zero.
The ``EnergyNode`` for *evse id* zero represents the energy sink for the
complete charging station.
*Module typically used to fulfill this requirement:*
:ref:`EnergyNode <everest_modules_EnergyNode>`
More information about the energy management setup can be found in the
:doc:`EnergyManager module documentation </explanation/energymanagement/index>`.
- **auth** (interface: ``auth``, 1):
This connection is used to set the standardized ``ConnectionTimeout`` configuration key
if configured and/or changed by the CSMS.
*Module typically used to fulfill this requirement:*
:ref:`Auth module <everest_modules_Auth>`, ``implementation_id: main``
- **reservation** (interface: ``reservation``, 1):
This connection is used to apply reservation requests from the CSMS.
*Module typically used to fulfill this requirement:*
:ref:`Auth module <everest_modules_Auth>`, ``implementation_id: reservation``
- **system** (interface: ``system``, 1):
This connection is used to execute and control system-wide operations that
can be triggered by the CSMS, like log uploads, firmware updates, and resets.
*Modules fulfilling this requirement:*
- :ref:`Linux_Systemd_Rauc module <everest_modules_Linux_Systemd_Rauc>` (``implementation_id: main``)
- For simulation purposes: :ref:`System module <everest_modules_System>` (``implementation_id: main``)
.. note::
The System module is **not meant to be used in production systems**.
- **security** (interface: ``evse_security``, 1):
This connection is used to execute security-related operations and to manage
certificates and private keys.
*Module typically used to fulfill this requirement:*
:ref:`EvseSecurity module <everest_modules_EvseSecurity>`, ``implementation_id: main``
- **data_transfer** (interface: ``ocpp_data_transfer``, 0 to 1):
This connection is used to handle **DataTransfer.req** messages from the CSMS.
A module implementing this interface can contain custom logic to handle such requests.
A custom implementation is required to add custom handling.
- **display_message** (interface: ``display_message``, 0 to 1):
This connection is used to allow the CSMS to display pricing or other information
on the display of the charging station.
To fulfill the requirements of the California Pricing whitepaper, it is required
to connect a module implementing this interface.
.. note::
EVerest currently does **not** provide a display module that implements this interface.
3. **Configure OCPP with the Auth module:**
Make sure to configure the OCPP module as part of the ``token_provider``
(``implementation_id: auth_provider``) and ``token_validator``
(``implementation_id: auth_validator``) connections of the Auth module (if you use it).
Please see the documentation of the :ref:`Auth module <everest_modules_Auth>` for more information.
4. **Enable Plug & Charge (optional):**
If you want to use the Plug&Charge feature, you must also add the
``EvseManager`` (``implementation_id: token_provider``) module to the connections
of the Auth module.
See :doc:`Configure Plug&Charge in EVerest </how-to-guides/configure-pnc>` for more
information about how to set this up.
You can also use the existing configuration examples as a guide.
----
**Authors:** Piet Gömpel

View File

@@ -0,0 +1,540 @@
.. _tutorial-ocpp2:
*****************************
OCPP 2.0.1 and 2.1 in EVerest
*****************************
.. note::
EVerest has an implementation of OCPP 1.6J and 2.0.1 and 2.1. This tutorial is about
the 2.0.1 and 2.1 implementation. To get documentation about all implemented versions,
see `lib/ocpp in the EVerest repository <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp>`_.
OCPP2.0.1 and OCPP2.1 in EVerest
=================================
EVerest provides an implementation of OCPP 2.0.1 and 2.1 based on
`libocpp` which is hosted as part of the `EVerest repository <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp>`_. Since OCPP 2.0.1 and 2.1
is mostly backwards compatible, the implementation of OCPP 2.1 is based on the
2.0.1 implementation. Every functionality that is provided as part of OCPP 2.0.1
is also available in OCPP 2.1.
In EVerest, the :ref:`OCPP201 module <everest_modules_OCPP>`
provides the OCPP 2.0.1 and 2.1 functionality.
Where applicable the following documentation uses "2.x" to refer to both versions.
.. note::
Which OCPP2.x version are supported can be controlled using the ``SupportedOcppVersions`` variable
of the ``InternalCtrlr`` component of the device model. Please see the
:ref:`Device Model Configuration <tutorial-ocpp2-configure-ocpp>` section for more information.
By default, both versions are supported. It still depends on the CSMS which version is actually used.
For more information about the version negotiation, please refer to the
**OCPP 2.1 Part 4 - JSON over WebSockets implementation guide**
-----------------------------------------------
This tutorial includes:
- How to run EVerest SIL with a `simple CSMS <https://github.com/EVerest/ocpp-csms>`_
- How to configure the OCPP 2.x device model
- How to connect to different CSMS
- How to load the OCPP 2.x module as part of the EVerest configuration
.. _tutorial-ocpp2-prerequisites:
Prerequisites
=============
If you're new to EVerest, start with our
:ref:`Quick Start Guide <htg_getting_started_sw>`
to get a simulation in EVerest running for the first time.
If you have done that successfully, you can move on with this tutorial.
.. _tutorial-ocpp2-run-with-csms:
Run EVerest SIL with OCPP 2.x and a simple CSMS
=================================================
EVerest provides a `simple CSMS <https://github.com/EVerest/ocpp-csms>`_ that
can be used for testing. It works with both OCPP 2.0.1 and 2.1.
It responds "friendly" to all OCPP messages initiated by the charging station.
Follow the instruction of its README to start up the CSMS locally.
The EVerest repository provides a configuration that you can use
to run EVerest with OCPP 2.x.
By default, this configuration is connecting to `localhost:9000` which is also
the default address and port of our simple CSMS.
Simply run
.. code-block:: bash
${EVEREST_WORKSPACE:?}/EVerest/build/run-scripts/run-sil-ocpp201-pnc.sh
to start EVerest with OCPP 2.x support and Plug&Charge enabled. You can start playing around with the EVerest
simulation to start charging sessions.
You can find the OCPP message log in different formats in the
`/tmp/everest_ocpp_logs` directory.
.. _tutorial-ocpp2-configure-ocpp:
Device Model Configuration
==========================
OCPP 2.x defines a device model structure and a lot of standardized variables
that are used within the functional requirements of the protocol.
Please see "Part 1 - Architecture & Topology" of the OCPP 2.0.1 specification
for further information about the device model and how it is composed.
You should be familiar with the terms OCPP 2.x terms **Component**,
**Variable**, **VariabeCharacteristics** and **VariableAttributes**,
**VariableMonitoring** to be able to follow the further explanations.
OCPP 2.x does not differentiate between configuration and telemetry
variables. This provides a lot of flexibility, but it also adds some overhead
to the definition of configuration variables (e.g. for configuration variables
only the ``actual`` attribute is actually relevant, but ``target``, ``minSet``,
and ``maxSet`` attribute types are never used and not needed for simple
configuration variables).
Device Model definition and configuration structure
---------------------------------------------------
In libocpp, the device model is defined and configured using JSON files.
These files serve two main purposes:
* **Definition**: the device model (including its components and variables)
* **Configuration**: the value of variable attributes
There is one JSON file for each Component.
Each Component contains the definition of its Variables.
Each Variable contains the definition of its VariableCharacteristics,
VariableAttributes and VariableMonitoring.
The actual value of a Variable can be configured as part of the
VariableAttribute(s).
This is how a definition and configuration for the ``LocalAuthListCtrlr``
component could look like:
.. code-block:: json
{
"description": "Schema for LocalAuthListCtrlr",
"name": "LocalAuthListCtrlr",
"type": "object",
"properties": {
"LocalAuthListCtrlrAvailable": {
"variable_name": "Available",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly",
"value": true
}
],
"description": "Local Authorization List is available.",
"default": true,
"type": "boolean"
},
"BytesPerMessageSendLocalList": {
"variable_name": "BytesPerMessage",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly",
"value": 4096
}
],
"description": "Maximum number of bytes in a SendLocalList message.",
"type": "integer"
},
"LocalAuthListCtrlrEnabled": {
"variable_name": "Enabled",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadWrite",
"value": true
}
],
"description": "If this variable exists and reports a value of true, Local Authorization List is enabled.",
"default": true,
"type": "boolean"
},
"LocalAuthListCtrlrEntries": {
"variable_name": "Entries",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
}
],
"description": "Amount of IdTokens currently in the Local Authorization List",
"type": "integer"
},
"ItemsPerMessageSendLocalList": {
"variable_name": "ItemsPerMessage",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly",
"value": 250
}
],
"description": "Maximum number of records in SendLocalList",
"type": "integer"
},
"LocalAuthListCtrlrStorage": {
"variable_name": "Storage",
"characteristics": {
"unit": "B",
"supportsMonitoring": true,
"dataType": "integer"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
}
],
"description": "Indicates the number of bytes currently used by the Local Authorization List. MaxLimit indicates the maximum number of bytes that can be used by the Local Authorization List.",
"type": "integer"
},
"LocalAuthListCtrlrDisablePostAuthorize": {
"variable_name": "DisablePostAuthorize",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadWrite"
}
],
"description": "When set to true this variable disables the behavior to request authorization for an idToken that is stored in the local authorization list with a status other than Accepted, as stated in C14.FR.03.",
"type": "boolean"
}
},
"required": [
"BytesPerMessageSendLocalList",
"ItemsPerMessageSendLocalList",
"LocalAuthListCtrlrEntries"
]
}
You can change the components according to your needs, but note that the
definitions for the ``variable_name`` and ``characteristics`` are usually defined
by the OCPP 2.x specifications.
To configure a variable attribute value, specify the ``value`` for the attribute
type that you would like to configure.
In the example above, the actual value of the VariableAttribute of the Variable
``Enabled`` is set to ``true``. Note that not all variables have specified variable
attributes with a ``value``, e.g. ``LocalAuthListCtrlrEntries`` does not specify a
value. ``LocalAuthListCtrlrEntries`` is rather a telemetry than configuration,
so libocpp will set the value for this at runtime and therefore it is not
required to configure a value for it.
It's an example for a variable that is only defined, but not configured.
.. _tutorial-ocpp2-device-model-init:
Device Model initialization
---------------------------
The config files are parsed at startup and used to initialize an SQLite
database. Please see
`the documentation about the device model initialization <https://github.com/EVerest/EVerest/blob/main/lib/everest/ocpp/doc/v2/ocpp_201_device_model_initialization.md>`_
for detailed information about this process.
You should specify the path to the directory of your device model definitions
using the configuration parameter ``DeviceModelConfigPath``
of the OCPP201 module within EVerest.
It shall point to the directory where the component files are located in these
two subdirectories:
* standardized
* custom
By default, the default value for ``DeviceModelConfigPath`` is pointing to the
installation directory of the component files.
You can modify the component according to your specific needs and the design of
your charging station.
Libocpp provides a device model configuration as a starting point
-----------------------------------------------------------------
You can define custom components and variables according to the requirements
and setup of your charging station. There are a lot of
standardized components and variables in OCPP 2.x that are required and used
in functional requirements of the specification. Please have
a look at the OCPP 2.x specifications for more information about each of the
standardized components and variables.
For this reason, it is recommended to use the
`device device model definitions of libocpp <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp/config/v2/component_config>`_
as a starting point. This is an examplary device model configuration for two
EVSEs.
The `device model setup from libocpp <https://github.com/EVerest/EVerest/tree/main/lib/everest/ocpp/config/v2/component_config>`_
serves as a good example.
The split between the two directories only has semantic reasons.
The **standardized** directory usually does not need to be modified since it
contains standardized components and variables that the specification refers
to in its functional requirements.
The **custom** directory is meant to be used for components that are custom
for your specific charging station.
The following sections explain important component and variables in order to
connect to a different CSMS or to enable certain features.
.. _tutorial-ocpp2-network-configuration:
Network Configuration
---------------------
OCPP 2.x uses **NetworkConfiguration** components in the device model to define
how the charging station connects to a CSMS. Each connection profile is stored
as a separate component instance (called a **slot**), and a priority list
determines the order in which slots are tried during connection and failover.
.. _tutorial-ocpp2-different-csms:
Connect to a different CSMS
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each connection profile is defined in its own JSON file under the device model
configuration directory. At least two slots must be configured; you may add as
many additional slots as you need (see :ref:`tutorial-ocpp2-adding-slots`).
The default profiles are:
- ``component_config/standardized/NetworkConfiguration_1.json`` (slot 1)
- ``component_config/standardized/NetworkConfiguration_2.json`` (slot 2)
To connect to a different CSMS, modify the following variables in the
appropriate slot's JSON file:
- ``OcppCsmsUrl``: The WebSocket endpoint of the CSMS, **without** the
charging-station identifier path segment (e.g. ``ws://csms.example.com:9000``
or ``wss://csms.example.com:443``). The identifier from ``Identity`` is
appended at connect time.
- ``SecurityProfile``: Defines the transport layer security level:
- ``0``: No security (OCPP 1.6 compatibility, disabled by default)
- ``1``: Basic authentication over ``ws://``
- ``2``: TLS with server certificate over ``wss://``
- ``3``: TLS with mutual authentication (client + server certificates) over ``wss://``
- ``OcppInterface``: The network interface to use (``Wired0``--``Wired3``, ``Wireless0``--``Wireless3``, or ``Any``)
- ``OcppTransport``: The transport protocol (``JSON`` or ``SOAP``)
- ``MessageTimeout``: Message timeout in seconds (minimum 1)
Each slot can optionally override the charging station's identity and
authentication credentials:
- ``Identity``: Per-slot identity override. If empty, falls back to ``SecurityCtrlr.Identity``.
- ``BasicAuthPassword``: Per-slot password override. If empty, falls back to ``SecurityCtrlr.BasicAuthPassword``.
The global fallback values are configured in the ``SecurityCtrlr`` component:
- ``Identity`` in ``SecurityCtrlr``: The default identity of the charging station
- ``BasicAuthPassword`` in ``SecurityCtrlr``: The default password for HTTP Basic Authentication (SecurityProfile 1 or 2)
The **connection priority** is controlled by the ``NetworkConfigurationPriority``
variable in ``OCPPCommCtrlr``. This is a comma-separated list of slot numbers
that determines the order in which profiles are tried. For example, ``"1,2"``
means slot 1 is tried first; if it fails, slot 2 is tried, then back to slot 1
(round-robin failover).
.. note::
For TLS (SecurityProfile 2 or 3), you must prepare the required certificates
and private keys. Please see the documentation of the
:ref:`EvseSecurity module <everest_modules_EvseSecurity>` for more information
on how to set up the TLS connection for OCPP.
.. _tutorial-ocpp2-adding-slots:
Adding more network configuration slots
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, libocpp ships with two NetworkConfiguration slots. To add more:
1. **Create the JSON file.** Copy an existing slot file (e.g. ``NetworkConfiguration_1.json``)
to a new file named ``NetworkConfiguration_N.json`` where ``N`` is your slot number.
2. **Update the instance number.** In the new file, change the ``"instance"`` field
at the top level to match your slot number:
.. code-block:: json
{
"name": "NetworkConfiguration",
"instance": "3",
...
}
3. **Configure the slot's variables.** Set ``OcppCsmsUrl``, ``SecurityProfile``,
and other variables as needed for this connection profile.
4. **Add the slot to the priority list.** In ``OCPPCommCtrlr.json``, append the new
slot number to the ``NetworkConfigurationPriority`` value. For example, change
``"1,2"`` to ``"1,2,3"``.
5. **Rebuild and restart.** The device model database will be re-initialized with the
new slot on next startup.
.. _tutorial-ocpp2-migration:
Migration from legacy NetworkConnectionProfiles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Older versions of libocpp stored all connection profiles as a single JSON array
blob in the ``InternalCtrlr.NetworkConnectionProfiles`` variable. The current
implementation stores each profile as individual device model variables in
per-slot ``NetworkConfiguration`` components.
**Automatic migration:** On startup, libocpp automatically detects and migrates
the legacy format. No manual action is required for existing deployments.
The migration works as follows:
1. If the ``NetworkConnectionProfiles`` blob is empty, no migration is needed.
2. If any existing ``NetworkConfiguration`` slot already has a configured
``OcppCsmsUrl``, migration is skipped (the new format is already in use).
3. Otherwise, each profile in the JSON array is written to its corresponding
slot using the ``configurationSlot`` field from the blob as the slot number.
4. If a profile in the blob does not contain a ``basicAuthPassword``, the
global ``SecurityCtrlr.BasicAuthPassword`` is used as a fallback.
5. After successful import, the blob is cleared to prevent re-running the
migration on subsequent startups.
If you are setting up a new deployment, you do not need the legacy blob format.
Configure your connection profiles directly using the per-slot
``NetworkConfiguration_N.json`` files as described above. Make sure the
``InternalCtrlr.NetworkConnectionProfiles`` blob remains empty (its default)
so the migration step is skipped on first boot.
.. _tutorial-ocpp2-enable-pnc:
Enable Plug&Charge
------------------
In order to enable Plug&Charge, adjust your component files according to the
:doc:`Plug&Charge configuration documentation </how-to-guides/configure-pnc>`.
.. _tutorial-configure-ocpp2-everest:
Configuring the OCPP201 module within EVerest
=============================================
To be able to follow the further explanations, you should be familiar with the configuration of EVerest modules.
Take a look into :doc:`EVerest Module Concept </explanation/detail-module-concept>` for that.
To configure the OCPP201 module of EVerest, find the available configuration parameters
and carefully read the further explanations in the
:ref:`OCPP201 module documentation <everest_modules_OCPP201>`
in order to configure it according to your needs.
In order to enable OCPP2.x in EVerest, you need to load the module in the EVerest configuration file and set up the module connections. The interfaces
provided and required by the OCPP module and its purposes are described in the :ref:`OCPP201 module documentation <everest_modules_OCPP201>`.
The EVerest configuration file `config-sil-ocpp201.yaml <https://github.com/EVerest/EVerest/blob/main/config/config-sil-ocpp201.yaml>`_
which was used previously serves as a good example
for how the connections of the module could be set up.
Here is a quick list of things you should remember when adding OCPP201 to your EVerest configuration file:
1. Load the OCPP201 module by including it in the the configuration file.
2. Make sure to add and connect the module requirements:
- evse_manager (interface: energy_manager, 1 to 128 connections):
OCPP201 requires this connection in order to integrate with the charge control
logic of EVerest.
One connection represents one EVSE.
In order to manage multiple EVSEs via one OCPP connection, multiple
connections need to be configured in the EVerest config file.
Module implementation typically used to fullfill this requirement:
:ref:`EvseManager <everest_modules_EvseManager>`, implementation_id: evse
- evse_energy_sink (interface: external_energy_limits, 0 to 128):
OCPP optionally requires this connection to communicate smart charging
limits received from the CSMS within EVerest.
Typically EnergyNode modules are used to fullfill this requirement.
Configure one EnergyNode module per EVSE and one extra for *evse id* zero.
The EnergyNode for *evse id* zero represents the energy sink for the
complete charging station.
Module typically used to fullfill this requirement:
:ref:`EnergyNode <everest_modules_EnergyNode>`, implementation_id: external_limits
More information about the energy management setup can be found in the
:ref:`EnergyManager module documentation <everest_modules_EnergyManager>`.
- auth (interface: auth, 1): This connection is used to set the standardized
``ConnectionTimeout`` configuration key if configured and/or changed by the
CSMS.
Module typically used to fullfill this requirement:
:ref:`Auth <everest_modules_Auth>`, implementation_id: main
- reservation (interface: reservation, 1):
This connection is used to apply reservation requests from the CSMS.
Module typically used to fullfill this requirement:
:ref:`Auth <everest_modules_Auth>`, implementation_id: reservation
- system (interface: system, 1):
This connection is used to execute and control system-wide operations that
can be triggered by the CSMS, like log uploads, firmware updates, and
resets.
The :ref:`Linux_Systemd_Rauc module <everest_modules_Linux_Systemd_Rauc>` (implementation_id: main)
can be used to fullfill this requirement.
For simulation purposes, the :ref:`System module <everest_modules_System>` (implementation_id: main)
can be used. Note that the latter is not meant to be used in production systems!
- security (interface: evse_security, 1):
This connection is used to execute security-related operations and to
manage certificates and private keys.
Module typically used to fullfill this requirement:
:ref:`EvseSecurity <everest_modules_EvseSecurity>`, implementation_id: main
- data_transfer (interface: ocpp_data_transfer, 0 to 1):
This connection is used to handle **DataTransfer.req** messages from the
CSMS.
A module implementing this interface can contain custom logic to handle the
requests from the CSMS.
A custom implementation for this interface is required to add custom
handling.
- display_message (interface: display_message, 0 to 1):
This connection is used to allow the CSMS to display pricing or other
information on the display of the charging station.
In order to fulfill the requirements of the California Pricing whitepaper,
it is required to connect a module implementing this interface.
EVerest currently does not provide a display module that implements this
interface.
3. Make sure to configure the OCPP201 module as part of the token_provider (implementation_id: auth_provider) and token_validator (implementation_id: auth_validator)
connections of the Auth module (if you use it). Please see the documentation of the auth module for more information.
4. In case you want to use the Plug&Charge feature, you must also add the EvseManager (implementation_id: token_provider) module to the connections of the
Auth module.
You can also use the existing config examples as a guide.
----
**Authors**: Piet Gömpel

View File

@@ -0,0 +1,130 @@
.. _tutorial_plug_and_charge:
Plug&Charge with EVerest Software in the Loop
=============================================
EVerest provides support for Plug&Charge within ISO15118-2 and OCPP1.6 and
OCPP2.0.1. This tutorial explains how you can set up and configure EVerest
for Plug&Charge with the software in the loop.
.. _prerequisites:
Prerequisites
-------------
If you're new to EVerest, start with our
`Quick Start Guide <02_quick_start_guide.html>`_ to get a simulation in
EVerest running for the first time.
If you have done that successfully, you can move on with this tutorial.
If you are interested in more in-depth information about how Plug&Charge is
implemented in EVerest, please refer to the
:doc:`Plug&Charge explanations </explanation/pnc-process>`
and the :doc:`Plug&Charge Configuration Howto </how-to-guides/configure-pnc>`
Packages for ISO 15118 communication
------------------------------------
To be able to build EVerest with ISO 15118 capability, you will have to
install the requirements for Josev:
.. code-block:: bash
cd {EVerest Workspace Directory}/Josev
python3 -m pip install -r requirements.txt
For ISO 15118 communication including Plug&Charge, install Josev and some CA
certificates:
.. code-block:: bash
cd {EVerest Workspace Directory}/Josev/iso15118/shared/pki
./create_certs.sh -v iso-2 -i {EVerest Workspace Directory}/EVerest
This will enable ISO 15118 communication including Plug&Charge and install the
required CA certificates inside ``config/certs/ca`` and the client certificates,
private keys and password files inside ``config/certs/client``.
.. attention::
This will generate an example PKI setup that can only be used for testing
and simulation. It will not work and is not recommended for production.
As the shell script uses the Java ``keytool``, it is required for this
procedure to have Java installed.
The script for setting up PKI can also be used with the EvseV2G module.
.. _plug_and_charge_process:
The Plug&Charge process
-----------------------
The process we are going to simulate covers a complete AC Plug&Charge process
including a CertificateInstallation request to install a virtual contract in
the simulated EV.
The components included in this setup are the following:
1. Charging Station (SECC): The EVerest stack provides the software running on
the charger. It provides the ISO 15118 and OCPP implementations.
2. Electric Vehicle (EVCC): The EV is simulated using the software in the
loop (SIL). The SIL runs as part of EVerest using separate modules that are
started alongside with the EVerest application.
3. Charging Station Management System (CSMS): The CSMS used in this setup is
an external service. It's a very simple implementation of an OCPP central
system based on the Python ocpp package from TheMobilityHouse
(https://github.com/mobilityhouse/ocpp).
Let's get started step by step
------------------------------
1. Prerequisites must be fullfilled: EVerest must be installed on your system.
By default, this includes a complete and automatic installation of a test PKI.
The certificates and keys are located under ``dist/etc/everest/certs``.
2. Let's prepare the central system that we are going to use. Follow the
instructions described here to set it up:
https://github.com/EVerest/ocpp-csms
3. Run EVerest with either OCPP1.6, OCPP2.x using the prepared run-scripts.
Make sure the the endpoint ``localhost:9000/<id>`` is specified in the respective
ocpp configuration file (OCPP2.x config defaults to this address, while for
OCPP1.6 the default is different).
.. code-block:: bash
./run-scripts/run-sil-ocpp201-pnc.sh
or
.. code-block:: bash
./run-scripts/run-sil-ocpp-pnc.sh
Make sure Node-RED is running and access the UI on ``localhost:1880/ui``.
In Node-RED select ``AC ISO 15118-2`` from the Car Simulation dropdown and click "Car Plugin". This will initiate the EV plugin and start the Plug&Charge process.
Check the EVerest console and OCPP logs. By default OCPP logs are located in ``/tmp/everest_ocpp_logs`` .
Troubleshooting
---------------
If you observe logging messages indicating a timeout of the SDP request like
.. code-block:: text
2025-10-17 17:21:47.039511 [WARN] iso15118_car pybind11_init_everestpy(pybind11::module_&)::<lambda(const std::string&)> :: A TimeoutError occurred. Waited for Timeouts.SDP_REQ s after sending an SDPRequest
you may need to adjust your firewall settings to allow communication between
the EVCC and SECC modules.
.. code-block:: bash
sudo ip6tables -I INPUT -i <your_network_interface_name> -p udp --dport 15118 -j ACCEPT
----
**Authors**: Piet Gömpel

View File

@@ -0,0 +1,142 @@
:title: Managed by devrd CLI
######################################################
Setup Variant: Service Containers Managed by devrd cli
######################################################
This variant utilizes the devrd CLI to manage service containers,
making it an ideal choice for users who do not use VSCode.
It provides dedicated control of the service containers independent
of the devcontainer lifecycle. The devcontainer itself will not start
or stop any service containers.
.. note::
When using this variant and still using VSCode, the VSCode
built-in terminal cannot execute commands directly within the devcontainer.
Instead, use the devrd CLI to open an interactive session (see below).
******************************
What to expect from this setup
******************************
If one prefers to run the devcontainer outside VSCode, this setup variant is
the right choice.
The devrd cli will help to manage the service containers independent
of the devcontainer lifecycle. So one can start and stop the service containers
at any time.
The contents of the EVerest repo are mapped inside the container
in the directory ``/workspace``.
*************
Prerequisites
*************
To install the prerequisites, please check your operating system or distribution online documentation:
- Docker installed [#docker]_
- Docker compose installed version V2 (not working with V1) [#docker_compose]_
**************
Required Steps
**************
1. **Clone the EVerest repository**
If you have not done this yet, clone the EVerest repository
from GitHub to your local machine:
.. code-block:: bash
git clone https://github.com/EVerest/EVerest.git path/to/EVerest
Where ``path/to/EVerest`` is the path where you want to
clone the repository to.
2. **Build and start the devcontainer and service containers**
Change into the cloned repository directory:
.. code-block:: bash
cd path/to/EVerest
Then build and start the devcontainer and the service containers
with the devrd cli:
.. code-block:: bash
./applications/devrd/devrd start
This will build and start the devcontainer and all service containers.
If not yet existing, devrd will generate the ``.devcontainer/.env`` file.
3. **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.
To exit the shell, simply type ``exit`` or press ``Ctrl+D``.
See the :doc:`howto </how-to-guides/devcontainer-usage/index>` to learn how to
execute EVerest in a SIL using containers.
*************************************
Optional: Install bash/zsh completion
*************************************
If you want to enable completion on your host system follow the steps below.
Install bash completion
-----------------------
Add the following lines to your ``~/.bashrc`` file:
.. code-block:: bash
# bash completion for devrd cli
source applications/devrd/devrd-completion.bash
Then reload your ``~/.bashrc`` file:
.. code-block:: bash
source ~/.bashrc
Install zsh completion
----------------------
Add the following lines to your ``~/.zshrc`` file:
.. code-block:: bash
# zsh completion for devrd cli
autoload -U compinit && compinit
source applications/devrd/devrd-completion.zsh
Then reload your ``~/.zshrc`` file:
.. code-block:: bash
source ~/.zshrc
See the :doc:`separate troubleshooting section <troubleshooting>` for help
on devcontainer-specific issues.
----
**Authors:** Florian Mihut, Andreas Heinrich
.. [#docker] `<https://docs.docker.com/engine/install/>`_
.. [#docker_compose] `<https://docs.docker.com/compose/install>`_

View File

@@ -0,0 +1,37 @@
#######################################
Setup the EVerest Development Container
#######################################
There are two variants for the devcontainer setup:
.. grid:: 1 2 2 2
:gutter: 2
.. grid-item-card:: Service Containers managed by VSCode
:link: /tutorials/setup-devcontainer/vscode
:link-type: doc
Uses the VSCode Dev Containers extension, which manages both
the devcontainer and the service containers.
.. grid-item-card:: Service Containers managed by devrd cli
:link: /tutorials/setup-devcontainer/devrd
:link-type: doc
Uses the devrd cli to manage the service containers.
This variant can be used if one is not using VSCode or needs dedicated
control over the service containers.
Also consider these documents:
- :doc:`troubleshooting section <troubleshooting>` that deals for devcontainer-specific issues.
- :doc:`How-to Guide: How to use a development container for EVerest development and sil testing </how-to-guides/devcontainer-usage/index>`
- :doc:`Internals of the EVerest Development Container </explanation/devcontainer-internal/index>`
.. toctree::
:maxdepth: 1
:hidden:
Managed by devrd CLI <devrd>
Managed by VSCode <vscode>
troubleshooting

View File

@@ -0,0 +1,110 @@
###############
Troubleshooting
###############
Port conflicts
--------------
Each instance uses the same ports (1883, 1880, 4000, etc.). Please note that only one instance can run at a time.
If another process is using the port you need here is how to detect it and make it available:
.. code-block:: bash
sudo lsof -ti:1880 \| xargs sudo kill -9
./applications/devrd/devrd start
If a system service is using a port (e.g. mosquitto) stopping/disabling this service may be required.
Regenerate environment configuration
------------------------------------
If you suspect that the environment variables in the ``.env`` file
are not correct, you can regenerate it with:
.. code-block:: bash
./applications/devrd/devrd env
Customize environment variables
--------------------------------
To customize specific environment variables, you can edit the
``.devcontainer/.env`` file directly or pass them as arguments to the ``./applications/devrd/devrd env`` command.
.. note::
If you manually edit the ``.env`` file and change ``EVEREST_TOOL_BRANCH``
or other build arguments, you must rebuild the container for changes to take effect:
Rebuild devrd setup
-------------------
The setup can be rebuild with:
Option 1: Force rebuild (recommended)
.. code-block:: bash
./applications/devrd/devrd env # Regenerate .env if you edited it manually
./applications/devrd/devrd build # Rebuild with new environment variables
Option 2: Clean rebuild (if rebuild doesn't work)
.. code-block:: bash
./applications/devrd/devrd stop # Stop all containers, images, and volumes
./applications/devrd/devrd purge # Remove all containers, images, and volumes
./applications/devrd/devrd build # Rebuild from scratch
Purge and rebuild devrd setup
-----------------------------
.. code-block:: bash
./applications/devrd/devrd purge # Remove all resources for current folder
./applications/devrd/devrd build # Will generate .env if missing
Volume conflicts
----------------
Docker volumes are shared. Use
.. code-block:: bash
./applications/devrd/devrd purge
before switching instances.
SSH keys
--------
Ensure your SSH agent has the necessary keys for all repositories.
Container naming
----------------
Docker containers are named based on the workspace directory to avoid conflicts.
Switching between instances
---------------------------
When switching between different EVerest instances (e.g., different branches or forks),
use the following commands:
.. code-block:: bash
# Stop current instance
./applications/devrd/devrd stop
# Purge if switching to different branch/project
./applications/devrd/devrd purge
# Start new instance
cd ~/different-everest-directory
./applications/devrd/devrd start
----
**Authors:** Florian Mihut, Andreas Heinrich

View File

@@ -0,0 +1,109 @@
:title: Managed by VSCode
###################################################
Setup Variant: Service Containers Managed by VSCode
###################################################
This variant makes use of the VSCode Dev Containers extension [#vscode_devcontainter]_
.
This will allow to run commands in the VSCode built-in terminal.
It will manage the service containers by starting them together with
the `devcontainer` itself. Dedicated control of individual services
is not possible with this variant.
******************************
What to expect from this setup
******************************
By following the steps in this tutorial you will setup a development
environment for EVerest using a development container (devcontainer).
VSCode will automatically build the container. After this all development
happens inside the container.
The contents of the EVerest repo are mapped inside the container
in the directory ``/workspace``
You can exit VSCode at any time, re-running it will cause VSCode to ask you
again to reopen in container.
Opening the EVerest repository in the VSCode devcontainer will
automatically start all of the provided service containers.
*************
Prerequisites
*************
To install the prerequisites, please check your operating system or distribution online documentation:
- Docker installed [#docker]_
- Docker compose installed version V2 (not working with V1) [#docker_compose]_
- VS Code with Dev Containers extension installed [#vscode_devcontainter]_
**************
Required Steps
**************
1. **Clone the EVerest repository**
If you have not done this yet, clone the EVerest repository
from GitHub to your local machine:
.. code-block:: bash
git clone https://github.com/EVerest/EVerest.git path/to/EVerest
Where ``path/to/EVerest`` is the path where you want to
clone the repository to.
2. **Open in VSCode**
Then open this repository in VSCode:
.. code-block:: bash
code path/to/EVerest
Choose **Reopen in container** when prompted by VSCode.
Now VSCode will build the devcontainer.
This can take some time depending on your machine and internet connection.
After this you can use the built-in terminal in VSCode to run commands
inside the devcontainer as for example to build EVerest.
*************
Example Usage
*************
You can now use VSCode's terminal to issue commands inside the devcontainer, e.g. for building:
.. code-block:: bash
docker@16898a21b4f1:/workspace$ mkdir build
docker@16898a21b4f1:/workspace$ cd build
docker@16898a21b4f1:/workspace$ cmake ..
docker@16898a21b4f1:/workspace$ cmake --build . --parallel 8
This will readily build EVerest regardless of your host system setup.
****************************************
Tips for VSCode Dev Containers Extension
****************************************
When the repository is opened in VSCode you can enter the devcontainer at
any time by running the command **Dev Containers: Reopen in Container** from the
command palette (F1).
You can also stop the devcontainer by running the command **Dev Containers: Reopen Folder Locally**.
***************
Troubleshooting
***************
See the :doc:`separate troubleshooting section <troubleshooting>` for help
on devcontainer-specific issues.
----
**Authors:** Florian Mihut, Andreas Heinrich
.. [#docker] `<https://docs.docker.com/engine/install/>`_
.. [#docker_compose] `<https://docs.docker.com/compose/install>`_
.. [#vscode_devcontainter] `<https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers>`_