Example client implementation

This section showcases two example client implementations for the basic example service. The first example is a minimal client implemented in a UNIX shell script. The second example is a more sophisticated client implemented in Python using the ESG package.

Minimal client implementation

This minimal client implementation is to be executed on the same machine as the service. The service should be reachable via a localhost port. The client is implemented using a simple UNIX shell script and requires the additional packages curl and jq. The script below can be used to simulate a client that requests a photovoltaic power generation forecast from the basic example service. Note that this interaction with the /fit-parameters/ endpoint is omitted in this example but would follow the same structure.

SERVICE_BASE_URL=${SERVICE_BASE_URL:-http://localhost:8800}
SERVICE_VERSION="test_version"

# Request a PV power generation forecast from the basic example service.
response=$(
    curl -X 'POST' \
    "${SERVICE_BASE_URL}/${SERVICE_VERSION}/request/" \
    -d '{
        "arguments": {
            "geographic_position": {
                "latitude": 49.01365,
                "longitude": 8.40444
            }
        },
        "parameters": {
            "pv_system": {
                "azimuth_angle": 0,
                "inclination_angle": 30,
                "nominal_power": 15
            }
        }
    }'
)

# Extract the ID of the task from the JSON response.
task_ID=$(echo $response | jq -r .task_ID)

# Poll status endpoint until status is ready.
status="unknown"
while [ $status != "ready" ]
do
    sleep 1
    response=$(
        curl -X 'GET' \
        "${SERVICE_BASE_URL}/${SERVICE_VERSION}/request/${task_ID}/status/"
    )
    status=$(echo $response | jq -r .status_text)
done

# Fetch and print the result.
response=$(
    curl -X 'GET' \
    "${SERVICE_BASE_URL}/${SERVICE_VERSION}/request/${task_ID}/result/"
)
echo $response | jq

Client implementation in python

The service integration in a production EMS will probably require a more sophisticated client implementation and require additional functionality. However, the concept of the client remains the same. As mentioned before, the ESG package contains a generic client providing additional functionality such as parsing the result into the format the EMS expects and network error handling.

import os

from esg.clients.service import GenericServiceClient
from esg.models.base import _BaseModel
from esg.models.datapoint import ValueMessageList
from esg.models.metadata import GeographicPosition, PVSystem
from esg.service.worker import compute_request_input_model
from pydantic import Field

SERVICE_BASE_URL = os.getenv("SERVICE_BASE_URL")


class RequestArguments(_BaseModel):
    geographic_position: GeographicPosition


class FittedParameters(_BaseModel):
    pv_system: PVSystem


RequestInput = compute_request_input_model(
    RequestArguments=RequestArguments, FittedParameters=FittedParameters
)


class RequestOutput(_BaseModel):
    power_prediction: ValueMessageList = Field(
        description="Prediction of power production in W"
    )


client = GenericServiceClient(
    base_url=SERVICE_BASE_URL,
    InputModel=RequestInput,
    OutputModel=RequestOutput,
)

client.post_obj(
    input_data_obj={
        "arguments": {
            "geographic_position": {"latitude": 49.01365, "longitude": 8.40444}
        },
        "parameters": {
            "pv_system": {
                "azimuth_angle": 0,
                "inclination_angle": 30,
                "nominal_power": 15,
            }
        },
    }
)

print(client.get_results_obj())

It is recommended that the client developer specifies the data models themself in order to document the data structure of the downstream application, i.e. the EMS. This should reduce tedious debugging deep within the downstream application and produce the error right inside the client code. However, it is possible to fetch the data models automatically from the service.

It is to be noted, that the generic client provided by the ESG package can only be used in a python environment. Nevertheless, if the client code needs to be in a different language it should be possible to implement the logic rather quickly. Swagger Codegen can help to partially automate the generation of the client program.