.. _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 `. 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 `. 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.