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
@@ -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);
|
||||
|
||||