////
  // Copyright (c) 2021, 2026 Contributors to the Eclipse Foundation
  //
  // This program and the accompanying materials are made
  // available under the terms of the Eclipse Public License 2.0
  // which is available at https://www.eclipse.org/legal/epl-2.0/
  //
  // SPDX-License-Identifier: EPL-2.0
////

include::_initCommon.adoc[]

== Application Programming Interface (API)

{lsat} is equipped with a REST API which can be used to e.g., start a timing analysis. The available REST calls are
described in the section <<rest-calls>> below.
The results of a timing analysis are returned in a JSON format as described <<api-json-format,below>>.
Note that the same results can also be exported as a JSON file by manually starting a timing analysis via the GUI
(see <<scheduling,Scheduling & Configurations>>).

The {lsat} API data format is specified using OpenAPI. From this specifications clients for various programming
languages are generated which can be used to parse the JSON file:

  * Java
  * Python
  * C++

The Java and Python clients will automatically resolve references in the JSON file. For example, instead
of writing `task_id = task_ids[0]; task = lsat_data.tasks[task_id]` it is possible to write `task = tasks[0]`.
For other clients, like the C++ client, this shorthand notation is not available.

The OpenAPI specification and the clients can be found in the _api-clients_ folder of the LSAT installation. Other
clients can be generated from the OpenAPI specifications with tools like https://openapi-generator.tech/[OpenAPI Generator].
An example usage for the Python client is given in <<api-python-client,Python API client>>.

[[rest-calls]]
=== REST calls

The {LSAT} API can be accessed via HTTP on port 4560. The following REST calls are available:

* `/api/health`: check if the API is available
* `/api/list`: list all projects present in LSAT
* `/api/motioncalculator`: make a request to the currently configured motion calculator. 
  The single argument is a URL-encoded JSON string which must follow the JSON schema for motion calculator requests as described
  in the link:{lsat-website}/development/#json-schema-for-request-and-response[design documentation] of {lsat}. 
  A minimal example to query the supported profiles by the motion calculator is as follows: `http://localhost:4560/api/motioncalculator?request=%7B%22requestType%22%3A%20%22SupportedProfiles%22%7D`  

* `/api/reload`: reload a specific project. The name of the project should be provided via the mandatory argument __project__. 
* `/api/reloadall`: reload all projects
* `/api/timinganalysis`: start a timing analysis on one of the projects in LSAT

+
Mandatory arguments for running a timing analysis:

  ** project: name of the project e.g., bowling
  ** setting: name of the settings file (without extension), e.g., bowling
  ** dispatch: name of the dispatching file (without extension), e.g., bowlingbowling_alt1

+
Optional arguments for running a timing analysis:

  ** goal: select the timing analysis goal, can be __normal__, __critical_path__ or __stochastic_impact__ (default: __normal__)
  ** debug: run timing analysis in debug mode (default: __false__)
  ** sampleLength: number of samples to use when running a stochastic analysis (default: 100)
  ** colorErroneousMoves: when there are erroneous moves, color them in the Gantt chart (default: __false__)
  ** strictProductTracing: enforce strict tracing of products, will return a list of unprocessed product changes if the product flow is incomplete (default: __false__)
  ** exportMotionPlots: enable motion plots, can be __true__ or __false__ (default: __false__)
  ** motionPlotSampleFrequency: sample frequency for the motion plots (default: 1000)
  ** motionPlotFilter: selection of peripherals for which motion plots will be exported (default: empty, which implies motion plots for all peripherals will be exported)

* `/api/validate`: run model validation on the selected project

+
Mandatory argument for validation:

  ** project: name of the project e.g., bowling

+
Optional arguments for validation:

  ** errors: report validation errors (default: __true__)
  ** warnings: report validation warnings (default: __true__)
  ** format: return validation in either __JSON__ or __TEXT__ (case-insensitive, default: __JSON__)

A minimal example REST call for running the timing analysis on the bowling ball example is as follows:
`http://localhost:4560/api/timinganalysis?project=bowling&setting=bowling&dispatch=bowling_alt1`

To export motion plots for the __XY__ peripheral of the __LoadRobot__ and __UnloadRobot__, the REST call can be extended like this:
`http://localhost:4560/api/timinganalysis?project=bowling&setting=bowling&dispatch=bowling_alt2&exportMotionPlots=true&motionPlotSampleFrequency=1000&motionPlotFilter=LoadRobot.XY%20UnloadRobot.XY` 


[[api-json-format]]
=== JSON format

The API's timing analysis JSON response contains both the model used in the timing analysis as well as the results.
The model follows the specification as described in <<logistics-specification,Logistics Specification>>.

The JSON response starts with a list of all peripheral types, followed by all resources and peripherals. Next, variable
declarations and their corresponding values are given. Activities contain a list of (references to) actions, which can 
be looked-up in the actions section. There are various types of actions, such as move or peripheral actions, claim or
release actions, or events. Different type of actions have different properties, such as the start and end position and
the movement profile for a move, or the resource and peripheral for a peripheral-action. 
Also dispatch groups contain a list of (references to) dispatches, which can be looked-up in the dispatches section.
Each dispatch has a unique number, a (reference to) its activity and a set of (user defined) attributes.

The result of the timing analysis is split in three parts: all the tasks which are scheduled, the (dependency) graph
of these tasks, and the final schedule. All tasks have a unique number and properties like their name, start time,
duration, which action and which dispatch they belong to, etc. In case motion plots are enabled in the timing analysis
this data is also exported for move-tasks. The graph section contains all dependencies between claim-and-release tasks.
Finally the sequence section contains a list of all sequences (i.e., the swim lanes in the Gantt chart) with the 
corresponding resources and peripherals, and a list of all tasks scheduled in this sequence. The sequence section also
lists all dependencies between all types of tasks.

Finally the product tracing results are provided. This includes the life cycle of each projects, i.e., when it enters and
exits the system, and when it is transfered between peripherals, and all the updates of the properties of the products.


[[api-python-client]]
=== Python API client

The Python API client can be used to parse results generated by the timing analysis, either directly from a REST call or from a file. 
After parsing it is possible to e.g., iterate over all tasks in the resulting schedule, as is shown below:

[source,python]
----
from lsat_client import LsatData <1>
import requests

if USE_REST_CALL:
    r = requests.get('http://localhost:4560/api/timinganalysis?project=bowling&setting=bowling&dispatch=bowling_alt1') <2>
    json_str = r.text
else:
    with open('bowling_alt1.json', 'r') as f: <3>
        json_str = f.read()

lsat_data = LsatData.from_json(json_str) <4>

for seq in lsat_data.schedule.sequences: <5>
    if seq.peripheral is not None:
        print(f'{seq.resource.name}.{seq.peripheral.name}')
    else:
        print(f'Event {seq.resource.name}')
    for task in seq.tasks: <6>
        print('  - ', task.name)
----

<1> Import the LSAT API client.
<2> Either perform a REST call to start a timing analysis and collect the results.
<3> Or read the results from the file `bowling_alt1.json` which is generated by the timing analysis of the bowling example.
<4> Parse the contents of the file to an `LsatData` object.
<5> Iterate over all sequences in the schedule and print the corresponding resource and peripheral name.
<6> Iterate over all tasks in a sequence and print the task's name.

The Python client requires the Python packages __pydantic__, __python-dateutil__, and __urllib3__.
The Python package __requests__ can be used to perform REST calls.


[[api-java-client]]
=== Java API client

The Java API client can be used to parse JSON files generated by the timing analysis, similar to the Python API client.
An example function to convert the JSON data into an LsatData object is shown below:
 
[source,java]
----
import org.eclipse.lsat.external.api.model.LsatData; <1>
import org.eclipse.lsat.external.api.util.LsatDataUtil; 

public LsatData convert(String json) {
    return LsatDataUtil.fromJson(json); <2>
}
---- 

<1> Import the LsatData data class and the LsatDataUtil utility class.
<2> Parse a JSON string to an LsatData object.

The Java client has the following dependencies: __Jakarta Annotations__ and __Gson__.


[[api-cpp-client]]
=== C{plus}{plus} API client

The C{plus}{plus} API client can be used to parse JSON files generated by the timing analysis, similar to the Java and Python API client.
However, the C{plus}{plus} client does not automatically resolve references in the JSON file. 

The C{plus}{plus} client requires https://github.com/nlohmann/json[JSON for Modern C{plus}{plus}], a JSON parser in a single header file,
and (optionally) https://github.com/mrtazz/restclient-cpp[restclient-cpp] for performing REST requests. 

An example C{plus}{plus} application to either perform a REST call to start a timing analysis, or to process results from a JSON file, 
and print the start and end time of the first task in the first sequence is given below:

[source,cpp]
----
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <restclient-cpp/restclient.h>

#include "LsatData.h"

int main(int argc, char* argv[])
{
#ifdef USE_REST_CALL
    auto response = RestClient::get("http://localhost:4560/api/timinganalysis?project=bowling&setting=bowling&dispatch=bowling_alt1");
    nlohmann::json data = nlohmann::json::parse(response.body);
#else
    std::ifstream jsonFile(argv[1]);
    nlohmann::json data = nlohmann::json::parse(jsonFile);
#endif
    lsat::model::LsatData lsatData;
    from_json(data, lsatData);

    auto tasks = lsatData.getTasks();
    auto schedule = lsatData.getSchedule();
    auto sequences = schedule.getSequences();
    auto taskIds = sequences[0].getTaskIds();
    auto taskId = taskIds[0];
    auto task = tasks[taskId];

    std::cout << "Task " << task.getName()
            << " starts at " << task.getStartTime()
            << " and ends at " << task.getEndTime()
            << std::endl;
    return 0;
}
----
