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