This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

How to

Interact with the platform

  • Creating and reading entities and characteristics → Orchestrator REST API
  • Saving and reading real-time metrics and states → IIoT Middleware
  • Reading historical metrics and states → HDM REST API

1 - Orchestrator REST API

Create and read entities

The Orchestrator REST API enables to create and read all the different kinds of Digital Twin entities described in Model section.

This API is available through a dedicated Swagger interface for each Clawdite instance. Furthermore, it is possible to generate API clients for several languages, such as Python, Java and JavaScript. The mentioned clients are already generated and available within each Clawdite instance and make it possible to interact with Clawdite’s Orchestrator from external components.

In the following a few examples on entities management will be provided.

Install the Orchestrator client dependencies

In order to use the generated API clients inside external components it is needed to correctly setup and install the dependencies. Note that you need a personal GitLab token for accessing the registries. In case you don’t have it please contact the project’s maintainers.

pip install orchestrator-python-client --index-url https://__token__:<your_personal_token>@gitlab-core.supsi.ch/api/v4/projects/86/packages/pypi/simple
<dependencies>
    <dependency>
      <groupId>ch.supsi.dti.isteps.hdt</groupId>
      <artifactId>orchestrator-java-client</artifactId>
      <version>0.2.3</version>
    </dependency>
</dependencies>

<repositories>
  <repository>
    <id>gitlab-maven</id>
    <url>https://gitlab-core.supsi.ch/api/v4/projects/86/packages/maven</url>
  </repository>
</repositories>

Create a Worker

In order to create a Worker entity (or a FactoryEntity in general) it is mandatory to define the associated FactoryEntityModelCategoty and FactoryEntityModel in advance.

import os
from datetime import datetime
import orchestrator_python_client as hdt_client
from orchestrator_python_client import ApiClient, FactoryEntityModelCategoryDto, FactoryEntityModelDto, WorkerDto

# NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables

configuration = hdt_client.Configuration(host=os.getenv('HDT_ENDPOINT'))
configuration.api_key['apiKeyAuth'] = os.getenv('HDT_API_KEY')
api_client = hdt_client.ApiClient(configuration)

factory_entity_model_category_api = hdt_client.FactoryEntityModelCategoryApi(api_client)
factory_entity_model_api = hdt_client.FactoryEntityModelApi(api_client)
worker_api = hdt_client.WorkerApi(api_client)

operator_category = factory_entity_model_category_api.create_factory_entity_model_category(
                        FactoryEntityModelCategoryDto(name="Operator",
                            description="description")).payload

worker_model = factory_entity_model_api.create_factory_entity_model(
                        FactoryEntityModelDto(name="Worker",
                            description="description",
                            factory_entity_model_categories_id=[operator_category.id])).payload

worker = worker_api.create_worker(
                        WorkerDto(creation_date=datetime.now().isoformat(),
                            factory_entity_model_id=worker_model.id)).payload
import java.util.Collections;
import java.time.LocalDateTime;
import org.openapitools.client.ApiClient;
import org.openapitools.client.api.FactoryEntityModelCategoryApi;
import org.openapitools.client.api.FactoryEntityModelApi;
import org.openapitools.client.api.WorkerApi;
import org.openapitools.client.model.FactoryEntityModelCategoryDto;
import org.openapitools.client.model.FactoryEntityModelDto;
import org.openapitools.client.model.WorkerDto;

private void createWorker() {
    // NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables
    ApiClient apiClient = new ApiClient().setBasePath(System.getenv("HDT_ENDPOINT"));
    String apiKey = System.getenv("HDT_API_KEY");
    if(apiKey != null && !apiKey.isEmpty())
        apiClient.addDefaultHeader("x-api-key", apiKey);

    final FactoryEntityModelCategoryApi factoryEntityModelCategoryApi = new FactoryEntityModelCategoryApi(apiClient);
    final FactoryEntityModelApi factoryEntityModelApi = new FactoryEntityModelApi(apiClient);
    final WorkerApi workerApi = new WorkerApi(apiClient);

    FactoryEntityModelCategoryDto operatorCategory = factoryEntityModelCategoryApi.createFactoryEntityModelCategory(
            new FactoryEntityModelCategoryDto()
                    .setName("Operator")
                    .setDescription("description"));

    FactoryEntityModelDto workerModel = factoryEntityModelApi.createFactoryEntityModel(
            new FactoryEntityModelDto()
                    .setName("Worker")
                    .setDescription("description")
                    .setFactoryEntityModelCategoriesId(Collections.singletonList(operatorCategory.getId())));

    WorkerDto worker = workerApi.createWorker(
            new WorkerDto()
                    .setCreationDate(LocalDateTime.now().toString())
                    .setFactoryEntityModelId(workerModel.getId()));

}

Read a Worker

In order to retrieve a Worker entity (or an entity in general) it is mandatory to specify the associated entity ID.

import os
import orchestrator_python_client as hdt_client
from orchestrator_python_client import ApiClient, WorkerDto

# NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables

configuration = hdt_client.Configuration(host=os.getenv('HDT_ENDPOINT'))
configuration.api_key['apiKeyAuth'] = os.getenv('HDT_API_KEY')
api_client = hdt_client.ApiClient(configuration)

worker_api = hdt_client.WorkerApi(api_client)

# NOTE: you need to specify the worker_id UUID
worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"

worker = worker_api.get_worker_by_id(worker_id)
import java.util.Collections;
import java.time.LocalDateTime;
import org.openapitools.client.ApiClient;
import org.openapitools.client.api.WorkerApi;
import org.openapitools.client.model.WorkerDto;

private void readWorker() {
    // NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables
    ApiClient apiClient = new ApiClient().setBasePath(System.getenv("HDT_ENDPOINT"));
    String apiKey = System.getenv("HDT_API_KEY");
    if(apiKey != null && !apiKey.isEmpty())
        apiClient.addDefaultHeader("x-api-key", apiKey);

    final WorkerApi workerApi = new WorkerApi(apiClient);

    // NOTE: you need to specify the workerId UUID
    workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";

    WorkerDto worker = workerApi.getWorkerById(workerId);
}

Create a FunctionalModule

Despite the creation of a FunctionalModule entity in not dependent on other entities, it is important to define the associated FunctionalModuleInput, FunctionalModuleOutput and Block in order to correctly create the StateDescriptor related to the FunctionalModule (i.e. its output that will be sent to the IIoT Middleware).

import os
from datetime import datetime
import orchestrator_python_client as hdt_client
from orchestrator_python_client import ApiClient, FunctionalModuleDto, FunctionalModuleInputDto,
    FunctionalModuleOutputDto, NumberBasedDto, StateDescriptorDto

# NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables

configuration = hdt_client.Configuration(host=os.getenv('HDT_ENDPOINT'))
configuration.api_key['apiKeyAuth'] = os.getenv('HDT_API_KEY')
api_client = hdt_client.ApiClient(configuration)

functional_module_api = hdt_client.FunctionalModuleApi(api_client)
functional_module_input_api = hdt_client.FunctionalModuleInputApi(api_client)
functional_module_output_api = hdt_client.FunctionalModuleOutputApi(api_client)
number_based_api = hdt_client.NumberBasedApi(api_client)
state_descriptor_api = hdt_client.StateDescriptorApi(api_client)

custom_module = functional_module_api.create_functional_module(
                        FunctionalModuleDto(name="CustomModule", description="description")).payload

custom_module_input = functional_module_input_api.create_functional_module_input(
                        FunctionalModuleInputDto(input_param_name="hr", path="HR", functional_module_id=custom_module.id,
                                    descriptor_id=hr_descriptor.id)).payload

custom_module_output = functional_module_output_api.create_functional_module_output(
                        FunctionalModuleOutputDto(functional_module_id=custom_module.id,
                                    output_param_name="CustomPrediction")).payload

number_based_dto = number_based_api.create_number_based(NumberBasedDto()).payload

custom_module_state_descriptor = state_descriptor_api.create_state_descriptor(
                        StateDescriptorDto(functional_module_output_id=custom_module_output.id,  # to know the origin of the State
                                    blockId=number_based_dto.id,  # link the data structure
                                    name="CustomModuleState",
                                    description="prediction",
                                    factory_entity_model_id=worker_model.id)  # the prediction is related workers
                    ).payload
import java.util.Collections;
import java.time.LocalDateTime;
import org.openapitools.client.ApiClient;
import org.openapitools.client.api.FactoryEntityModelCategoryApi;
import org.openapitools.client.api.FactoryEntityModelApi;
import org.openapitools.client.api.WorkerApi;
import org.openapitools.client.model.FactoryEntityModelCategoryDto;
import org.openapitools.client.model.FactoryEntityModelDto;
import org.openapitools.client.model.WorkerDto;

private void createFunctionalModule() {
    // NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables
    ApiClient apiClient = new ApiClient().setBasePath(System.getenv("HDT_ENDPOINT"));
    String apiKey = System.getenv("HDT_API_KEY");
    if(apiKey != null && !apiKey.isEmpty())
        apiClient.addDefaultHeader("x-api-key", apiKey);
    
    final FunctionalModuleApi functionalModuleApi = new FunctionalModuleApi(apiClient);
    final FunctionalModuleInputApi functionalModuleInputApi = new FunctionalModuleInputApi(apiClient);
    final FunctionalModuleOutputApi functionalModuleOutputApi = new FunctionalModuleOutputApi(apiClient);
    final NumberBasedApi numberBasedApi = new NumberBasedApi(apiClient);
    final StateDescriptorApi = new StateDescriptorApi(apiClient);

    FunctionalModuleDto customModule = FunctionalModuleApi.createFunctionalModule(
            new FunctionalModuleDto()
                    .setName("CustomName")
                    .setDescription("description"));

    FunctionalModuleInputDto customModuleInput = FunctionalModuleInputApi.createFunctionalModuleInput(
            new FunctionalModuleInputDto()
                    .setInputParameterName("hr")
                    .setPath("HR")
                    .setFunctionalModuleId(customModule.getId())
                    .setDescriptorId(hrDescriptor.getId()));

    FunctionalModuleOutputDto customModuleOutput = FunctionalModuleOutputApi.createFunctionalModuleOutput(
            new FunctionalModuleOutputDto()
                    .setFunctionalModuleId(customModule.getId())
                    .setOutputParameterName("CustomPrediction"));

    NumberBasedDto numberBased = NumberBasedApi.createNumberBased(new NumberBasedDto());

    StateDescriptorDto customModuleStateDescriptor = StateDescriptorApi.createStateDescriptor(
            new FunctionalModuleOutputDto()
                    .setFunctionalModuleOutputId(customModuleOutput.getId()) // to know the origin of the State
                    .setBlockId(numberBased.getId()) // link the data structure
                    .setName("CustomModuleState")
                    .setDescription("prediction")
                    .setFactoryEntityModelId(workerModel.getId())); // the prediction is related workers
}

2 - IIoT Middleware

Save and read real-time metrics and states

The IIoT Middleware enables dynamic data published from Gateways and Functional Modules to be collected and made available to other interested modules, such as the HDM which is in charge of persisting them.

Currently only the MQTT protocol is supported, so modules need to publish and subscribe to specific topics. MQTT clients are available for different languages, in the following a few examples in Python and Java will be provided. Please refer to the official documentation to learn more about how to use the client in your project.

The topic schema is HDT/{factory_entity_id}/{measurement/state}/{descriptor_id}/{value}.

The message schema is {timestamp}#{value}.

Publish a Metric

In order to publish a metric value to the IIoT Middleware for making it available to other components, specific topics structure and message format need to be used.

Note that, if the metric to be saved is composed by multiple fields, then the associated values need to be separated by a pipe "|". For example: "1752760778790#1|2|3.

import os
from datetime import datetime
from paho.mqtt.client import Client
from paho.mqtt.enums import CallbackAPIVersion

def on_connect(client, userdata, flags, reason_code, properties):
    loguru.logger.debug(f'Connected with result code "{reason_code}"')


# NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER and MQTT_PASSWORD environment variables
mqtt_client = Client(client_id='unique-id', callback_api_version=CallbackAPIVersion.VERSION2)
mqtt_client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD"))
mqtt_client.on_connect = on_connect
mqtt_client.connect(os.getenv('MQTT_HOST'), int(os.getenv("MQTT_PORT")))
quality_of_service = 1

# NOTE: you need to specify the measurement_descriptor_id and worker_id UUIDs
measurement_descriptor_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
value = 15.0

mqtt_client.publish(f'HDT/{worker_id}/measurement/{measurement_descriptor_id}',
                    f'{int(datetime.now().timestamp() * 1000)}#{value}', quality_of_service)

time.sleep(1)  # wait for the messages to be sent
mqtt_client.disconnect()
                           
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

private void publishMetric() {
    // NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER and MQTT_PASSWORD environment variables
    String broker = "tcp://" + System.getenv("MQTT_HOST") + ":" + System.getenv("MQTT_PORT");
    String clientId = "unique-id";
    int qos = 1;
    
    // NOTE: you need to specify the measurement_descriptor_id and worker_id UUIDs
    String measurementDescriptorId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    String workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    double value = 15.0;

    try {
        MqttClient mqttClient = new MqttClient(broker, "unique-id");
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setUserName(System.getenv("MQTT_USER"));
        connOpts.setPassword(System.getenv("MQTT_PASSWORD").toCharArray());

        // Set callback for connection
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                System.out.println("Connected to " + serverURI);
            }

            @Override
            public void connectionLost(Throwable cause) {
                System.out.println("Connection lost: " + cause.getMessage());
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                // Not used in this case, since we are publishing only
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                System.out.println("Message delivered successfully");
            }
        });

        mqttClient.connect(connOpts);
        
        long timestamp = System.currentTimeMillis();
        String message = timestamp + "#" + value;
        mqttClient.publish("HDT/" + workerId + "/measurement/" + measurementDescriptorId,
                new MqttMessage(message.getBytes()));

        TimeUnit.SECONDS.sleep(1);
        mqttClient.disconnect();


    } catch (MqttException | InterruptedException e) {
        e.printStackTrace();
    }
}

Publish a State

In order to publish a state value to the IIoT Middleware for making it available to other components, specific topics structure and message format need to be used.

import os
from datetime import datetime
from paho.mqtt.client import Client
from paho.mqtt.enums import CallbackAPIVersion

def on_connect(client, userdata, flags, reason_code, properties):
    loguru.logger.debug(f'Connected with result code "{reason_code}"')


# NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER and MQTT_PASSWORD environment variables
mqtt_client = Client(client_id='unique-id', callback_api_version=CallbackAPIVersion.VERSION2)
mqtt_client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD"))
mqtt_client.on_connect = on_connect
mqtt_client.connect(os.getenv('MQTT_HOST'), int(os.getenv("MQTT_PORT")))
quality_of_service = 1

# NOTE: you need to specify the state_descriptor_id and worker_id UUIDs
state_descriptor_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
value = {"fatigue": [5, 10]}

mqtt_client.publish(f'HDT/{worker_id}/state/{state_descriptor_id}',
                    f'{int(datetime.now().timestamp() * 1000)}#{value}', quality_of_service)

time.sleep(1)  # wait for the messages to be sent
mqtt_client.disconnect()

                               
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

private void publishState() {
    // NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER and MQTT_PASSWORD environment variables
    String broker = "tcp://" + System.getenv("MQTT_HOST") + ":" + System.getenv("MQTT_PORT");
    String clientId = "unique-id";
    int qos = 1;

    // NOTE: you need to specify the state_descriptor_id and worker_id UUIDs
    String stateDescriptorId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    String workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    String value = "{\"fatigue\": [5, 10]}";

    try {
        MqttClient mqttClient = new MqttClient(broker, "unique-id");
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setUserName(System.getenv("MQTT_USER"));
        connOpts.setPassword(System.getenv("MQTT_PASSWORD").toCharArray());

        // Set callback for connection
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                System.out.println("Connected to " + serverURI);
            }

            @Override
            public void connectionLost(Throwable cause) {
                System.out.println("Connection lost: " + cause.getMessage());
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                // Not used in this case, since we are publishing only
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                System.out.println("Message delivered successfully");
            }
        });

        mqttClient.connect(connOpts);

        long timestamp = System.currentTimeMillis();
        String message = timestamp + "#" + value;
        mqttClient.publish("HDT/" + workerId + "/state/" + stateDescriptorId,
                new MqttMessage(message.getBytes()));

        TimeUnit.SECONDS.sleep(1);
        mqttClient.disconnect();


    } catch (MqttException | InterruptedException e) {
        e.printStackTrace();
    }
}

Read a Metric

In order to read real-time metric values published to the IIoT Middleware, it is mandatory to subscribe to the topics of interest and to specify a callback for managing the received message.

import os
import time
from paho.mqtt.client import Client
from paho.mqtt.enums import CallbackAPIVersion

# NOTE: you need to specify the measurement_descriptor_id and worker_id UUIDs
def on_connect(client, userdata, flags, reason_code, properties):
    measurement_descriptor_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    topic = f'HDT/{worker_id}/measurement/{measurement_descriptor_id}'
    client.subscribe(topic, qos=1)

def on_message(client, userdata, msg):
    print(f'Received message: {msg.payload.decode()} on topic {msg.topic}')

# NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER, and MQTT_PASSWORD environment variables
mqtt_client = Client(client_id='unique-id', callback_api_version=CallbackAPIVersion.VERSION2)
mqtt_client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD"))
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message

mqtt_client.connect(os.getenv('MQTT_HOST'), int(os.getenv("MQTT_PORT")))
mqtt_client.loop_start()
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import java.util.UUID;

private void readMetric() {
    // NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER, and MQTT_PASSWORD environment variables
    String broker = "tcp://" + System.getenv("MQTT_HOST") + ":" + System.getenv("MQTT_PORT");
    String clientId = UUID.randomUUID().toString();
    
    // NOTE: you need to specify the measurementDescriptorId and workerId UUIDs
    String measurementDescriptorId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    String workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    topic = "HDT/"+workerId+"/measurement/"+measurementDescriptorId;

    try {
        MqttClient mqttClient = new MqttClient(broker, clientId);
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setUserName(System.getenv("MQTT_USER"));
        connOpts.setPassword(System.getenv("MQTT_PASSWORD").toCharArray());
    
        // Set callback for connection and message reception
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                System.out.println("Connected to " + serverURI);
            }
    
            @Override
            public void connectionLost(Throwable cause) {
                System.out.println("Connection lost: " + cause.getMessage());
            }
    
            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                String receivedMessage = new String(message.getPayload());
                System.out.println("Message received on topic " + topic + ": " + receivedMessage);
            }
    
            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                // Not used in this case, since we are subscribing only
            }
        });
    
        mqttClient.connect(connOpts);
        mqttClient.subscribe(topic);
    
    } catch (MqttException e) {
        e.printStackTrace();
    }
}

Read a State

In order to read real-time state values published to the IIoT Middleware, it is mandatory to subscribe to the topics of interest and to specify a callback for managing the received message.

import os
import time
from paho.mqtt.client import Client
from paho.mqtt.enums import CallbackAPIVersion

# NOTE: you need to specify the state_descriptor_id and worker_id UUIDs
def on_connect(client, userdata, flags, reason_code, properties):
state_descriptor_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    topic = f'HDT/{worker_id}/state/{state_descriptor_id}'
    client.subscribe(topic, qos=1)

def on_message(client, userdata, msg):
    print(f'Received message: {msg.payload.decode()} on topic {msg.topic}')

# NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER, and MQTT_PASSWORD environment variables
mqtt_client = Client(client_id='unique-id', callback_api_version=CallbackAPIVersion.VERSION2)
mqtt_client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD"))
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message

mqtt_client.connect(os.getenv('MQTT_HOST'), int(os.getenv("MQTT_PORT")))
mqtt_client.loop_start()
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import java.util.UUID;

private void readState() {
    // NOTE: you need to specify the MQTT_HOST, MQTT_PORT, MQTT_USER, and MQTT_PASSWORD environment variables
    String broker = "tcp://" + System.getenv("MQTT_HOST") + ":" + System.getenv("MQTT_PORT");
    String clientId = UUID.randomUUID().toString();

    // NOTE: you need to specify the stateDescriptorId and workerId UUIDs
    String stateDescriptorId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    String workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    topic = "HDT/"+workerId+"/state/"+stateDescriptorId;

    try {
        MqttClient mqttClient = new MqttClient(broker, clientId);
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setUserName(System.getenv("MQTT_USER"));
        connOpts.setPassword(System.getenv("MQTT_PASSWORD").toCharArray());
    
        // Set callback for connection and message reception
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                System.out.println("Connected to " + serverURI);
            }
    
            @Override
            public void connectionLost(Throwable cause) {
                System.out.println("Connection lost: " + cause.getMessage());
            }
    
            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                String receivedMessage = new String(message.getPayload());
                System.out.println("Message received on topic " + topic + ": " + receivedMessage);
            }
    
            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                // Not used in this case, since we are subscribing only
            }
        });
    
        mqttClient.connect(connOpts);
        mqttClient.subscribe(topic);
    
    } catch (MqttException e) {
        e.printStackTrace();
    }
}

3 - HDM REST API

Read historical metrics and states

The HDM REST API enables to get all the historical data related to metrics and states (i.e. dynamic data) that were published to the IIoT Middleware and persisted through the HDM, as described in Architecture section.

This API is available through a dedicated Swagger interface for each Clawdite instance. Furthermore, it is possible to generate API clients for several languages, such as Python, Java and JavaScript. The mentioned clients are already generated and available within each Clawdite instance and make it possible to interact with Clawdite’s HDM from external components.

In the following a few examples on data retrieval will be provided. Independently on the type of data, there are 4 retrieval modalities:

  • Get the last X data
  • Get the first X data from a specific point in time
  • Get the data included in a specific time-range (i.e. 1 hour, 1 day, 1 week…)
  • Get the data included in a specific time-interval (i.e. from date X to date Y)

Install the HDM client dependencies

In order to use the generated API clients inside external components it is needed to correctly setup and install the dependencies. Note that you need a personal GitLab token for accessing the registries. In case you don’t have it please contact the project’s maintainers.

pip install hdm-web-python-client --index-url https://__token__:<your_personal_token>@gitlab-core.supsi.ch/api/v4/projects/137/packages/pypi/simple
<dependencies>
    <dependency>
      <groupId>ch.supsi.dti.isteps.hdt</groupId>
      <artifactId>hdm-web-java-client</artifactId>
      <version>0.2.2</version>
    </dependency>
</dependencies>

<repositories>
  <repository>
    <id>gitlab-maven</id>
    <url>https://gitlab-core.supsi.ch/api/v4/projects/137/packages/maven</url>
  </repository>
</repositories>

Read Metrics

In this example the last 10 metrics are retrieved. The use of the other retrieval modalities is similar.

import os
from datetime import datetime
import hdm_web_python_client as hdm_client
from orchestrator_python_client import ApiClient


# NOTE: you need to specify the HDM_ENDPOINT and HDM_API_KEY environment variables
configuration = hdm_client.Configuration(host=os.getenv('HDM_ENDPOINT'))
configuration.api_key['apiKeyAuth'] = os.getenv('HDM_API_KEY')
api_client = hdm_client.ApiClient(configuration)
hdm_api = hdm_client.HdmControllerApi(api_client)

# NOTE: you need to specify the measurement_descriptor_id and worker_id UUIDs
measurement_descriptor_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"

values = hdm_api.read_last_values(abstract_descriptor_id=measurement_descriptor_id,
                                          factory_entity_id=worker_id,
                                          number_of_last_values=10)                            
import java.util.Collections;
import java.time.LocalDateTime;
import org.openapitools.client.ApiClient;
import org.openapitools.client.api.HdmApi;
import org.openapitools.client.model.GenericValueDto;
import org.springframework.data.domain.Page;

private void readMetrics() {
    // NOTE: you need to specify the HDM_ENDPOINT and HDM_API_KEY environment variables
    ApiClient apiClient = new ApiClient().setBasePath(System.getenv("HDM_ENDPOINT"));
    String apiKey = System.getenv("HDM_API_KEY");
    if (apiKey != null && !apiKey.isEmpty())
        apiClient.addDefaultHeader("x-api-key", apiKey);
    final HdmApi hdmApi = new HdmApi(apiClient);
    
    // NOTE: you need to specify the measurementDescriptorId and workerId UUIDs
    measurementDescriptorId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    numberOfLastValues = 10;

    Page<GenericValueDto> values = hdmApi.readLastValues(measurementDescriptorId, workerId, numberOfLastValues);
}

Read States

In this example the last 10 states are retrieved. The use of the other retrieval modalities is similar.

import os
from datetime import datetime
import hdm_web_python_client as hdm_client
from orchestrator_python_client import ApiClient


# NOTE: you need to specify the HDM_ENDPOINT and HDM_API_KEY environment variables
configuration = hdm_client.Configuration(host=os.getenv('HDM_ENDPOINT'))
configuration.api_key['apiKeyAuth'] = os.getenv('HDM_API_KEY')
api_client = hdm_client.ApiClient(configuration)
hdm_api = hdm_client.HdmControllerApi(api_client)

# NOTE: you need to specify the state_descriptor_id and worker_id UUIDs
state_descriptor_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
worker_id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"

values = hdm_api.read_last_values(abstract_descriptor_id=state_descriptor_id,
                                          factory_entity_id=worker_id,
                                          number_of_last_values=10)
import java.util.Collections;
import java.time.LocalDateTime;
import org.openapitools.client.ApiClient;
import org.openapitools.client.api.HdmApi;
import org.openapitools.client.model.GenericValueDto;
import org.springframework.data.domain.Page;

private void readStates() {
    // NOTE: you need to specify the HDM_ENDPOINT and HDM_API_KEY environment variables
    ApiClient apiClient = new ApiClient().setBasePath(System.getenv("HDM_ENDPOINT"));
    String apiKey = System.getenv("HDM_API_KEY");
    if (apiKey != null && !apiKey.isEmpty())
        apiClient.addDefaultHeader("x-api-key", apiKey);
    final HdmApi hdmApi = new HdmApi(apiClient);

    // NOTE: you need to specify the stateDescriptorId and workerId UUIDs
    stateDescriptorId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    workerId = "3fa85f64-5717-4562-b3fc-2c963f66afa6";
    numberOfLastValues = 10;

    Page<GenericValueDto> values = hdmApi.readLastValues(stateDescriptorId, workerId, numberOfLastValues);
}

4 - Skills modelling

Model operators skills

The Orchestrator REST API enables to create and read all the Digital Twin entities described in Model section, in particular the characteristics (i.e. quasi-static data). CharacteristicDescriptor and CharacteristicValue are used in order to model operator skills (e.g., work experience, manual dexterity), besides the general features (e.g., sex, age, handedness).

This API is available through a dedicated Swagger interface for each Clawdite instance. Furthermore, it is possible to generate API clients for several languages, such as Python, Java and JavaScript. The mentioned clients are already generated and available within each Clawdite instance and make it possible to interact with Clawdite’s Orchestrator from external components.

In the following a few examples about skills modelling will be provided.

Install the Orchestrator client dependencies

In order to use the generated API clients inside external components it is needed to correctly setup and install the dependencies. Note that you need a personal GitLab token for accessing the registries. In case you don’t have it please contact the project’s maintainers.

pip install orchestrator-python-client --index-url https://__token__:<your_personal_token>@gitlab-core.supsi.ch/api/v4/projects/86/packages/pypi/simple
<dependencies>
    <dependency>
      <groupId>ch.supsi.dti.isteps.hdt</groupId>
      <artifactId>orchestrator-java-client</artifactId>
      <version>0.2.3</version>
    </dependency>
</dependencies>

<repositories>
  <repository>
    <id>gitlab-maven</id>
    <url>https://gitlab-core.supsi.ch/api/v4/projects/86/packages/maven</url>
  </repository>
</repositories>

Create a Skill

In order to define a skill it is needed to create a CharacteristicDescriptor with an associated CharacteristicValue. Note that a CharacteristicDescriptor is uniquely linked to an already existing FactoryEntityModel. Moreover, the Worker (or FactoryEntity in general) which possesses the skill (i.e., CharacteristicValue) needs to be defined in advance.

import os
from datetime import datetime
from collections import OrderedDict
import orchestrator_python_client as hdt_client
from orchestrator_python_client import ApiClient, FactoryEntityModelCategoryDto, FactoryEntityModelDto, WorkerDto, 
    CharacteristicDescriptorDto, CharacteristicValueDto, FieldType

# NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables

configuration = hdt_client.Configuration(host=os.getenv('HDT_ENDPOINT'))
configuration.api_key['apiKeyAuth'] = os.getenv('HDT_API_KEY')
api_client = hdt_client.ApiClient(configuration)

factory_entity_model_category_api = hdt_client.FactoryEntityModelCategoryApi(api_client)
factory_entity_model_api = hdt_client.FactoryEntityModelApi(api_client)
worker_api = hdt_client.WorkerApi(api_client)
characteristic_descriptor_api = hdt_client.CharacteristicDescriptorApi(api_client)
characteristic_value_api = hdt_client.CharacteristicValueApi(api_client)

operator_category = factory_entity_model_category_api.create_factory_entity_model_category(
                        FactoryEntityModelCategoryDto(name="Operator",
                            description="description")).payload

worker_model = factory_entity_model_api.create_factory_entity_model(
                        FactoryEntityModelDto(name="Worker",
                            description="description",
                            factory_entity_model_categories_id=[operator_category.id])).payload

worker = worker_api.create_worker(
                        WorkerDto(creation_date=datetime.now().isoformat(),
                            factory_entity_model_id=worker_model.id)).payload

# "Gender" characteristic creation
gender_descriptor = characteristic_descriptor_api.create_characteristic_descriptor(
                        CharacteristicDescriptorDto(name="Gender",
                            description="Operator gender",
                            factory_entity_model_id=worker_model.id)).payload

worker_gender = characteristic_value_api.create_characteristic_value(
                        CharacteristicValueDto(values=OrderedDict({datetime.now().isoformat(): "Male"}),
                            type=FieldType.STRING,
                            characteristic_descriptor_id=gender_descriptor.id,
                            factory_entity_id=worker.id)).payload

# "Manual dexterity" skill creation
manual_dexterity_descriptor = characteristic_descriptor_api.create_characteristic_descriptor(
                        CharacteristicDescriptorDto(name="Manual dexterity",
                            description="Operator manual dexterity",
                            factory_entity_model_id=worker_model.id)).payload

worker_manual_dexterity = characteristic_value_api.create_characteristic_value(
                        CharacteristicValueDto(values=OrderedDict({datetime.now().isoformat(): "41"}),
                            type=FieldType.NUMBER,
                            characteristic_descriptor_id=manual_dexterity_descriptor.id,
                            factory_entity_id=worker.id)).payload
import java.util.Collections;
import java.time.LocalDateTime;
import org.openapitools.client.ApiClient;
import org.openapitools.client.api.FactoryEntityModelCategoryApi;
import org.openapitools.client.api.FactoryEntityModelApi;
import org.openapitools.client.api.WorkerApi;
import org.openapitools.client.api.CharacteristicDescriptorApi;
import org.openapitools.client.api.CharacteristicValueApi;
import org.openapitools.client.model.FactoryEntityModelCategoryDto;
import org.openapitools.client.model.FactoryEntityModelDto;
import org.openapitools.client.model.WorkerDto;
import org.openapitools.client.model.CharacteristicDescriptorDto;
import org.openapitools.client.model.CharacteristicValueDto;
import org.openapitools.client.model.FieldType

private void createSkill() {
    // NOTE: you need to specify the HDT_ENDPOINT and HDT_API_KEY environment variables
    ApiClient apiClient = new ApiClient().setBasePath(System.getenv("HDT_ENDPOINT"));
    String apiKey = System.getenv("HDT_API_KEY");
    if(apiKey != null && !apiKey.isEmpty())
        apiClient.addDefaultHeader("x-api-key", apiKey);

    final FactoryEntityModelCategoryApi factoryEntityModelCategoryApi = new FactoryEntityModelCategoryApi(apiClient);
    final FactoryEntityModelApi factoryEntityModelApi = new FactoryEntityModelApi(apiClient);
    final WorkerApi workerApi = new WorkerApi(apiClient);
    final CharacteristicDescriptorApi characteristicDescriptorApi = new CharacteristicDescriptorApi(apiClient);
    final CharacteristicValueApi characteristicValueApi = new CharacteristicValueApi(apiClient);

    FactoryEntityModelCategoryDto operatorCategory = factoryEntityModelCategoryApi.createFactoryEntityModelCategory(
            new FactoryEntityModelCategoryDto()
                    .setName("Operator")
                    .setDescription("description"));

    FactoryEntityModelDto workerModel = factoryEntityModelApi.createFactoryEntityModel(
            new FactoryEntityModelDto()
                    .setName("Worker")
                    .setDescription("description")
                    .setFactoryEntityModelCategoriesId(Collections.singletonList(operatorCategory.getId())));

    WorkerDto worker = workerApi.createWorker(
            new WorkerDto()
                    .setCreationDate(LocalDateTime.now().toString())
                    .setFactoryEntityModelId(workerModel.getId()));

    // "Gender" characteristic creation

    CharacteristicDescriptorDto genderDescriptor = characteristicDescriptorApi.createCharacteristicDescriptor(
            new CharacteristicDescriptorDto()
                    .setName("Gender")
                    .setDescription("Worker gender")
                    .setFactoryEntityModelId(workerModel.getId()));

    CharacteristicValueDto workerGender = characteristicValueApi.createCharacteristicValue(
            new CharacteristicValueDto()
                    .setValues(new TreeMap<>() {{
                        put(LocalDateTime.now().toString(), "Male");
                    }})
                    .setType(FieldType.STRING)
                    .setCharacteristicDescriptorId(genderDescriptor.getId())
                    .setFactoryEntityId(worker.getId()));

    // "Manual dexterity" skill creation

    CharacteristicDescriptorDto manualDexterityDescriptor = characteristicDescriptorApi.createCharacteristicDescriptor(
            new CharacteristicDescriptorDto()
                    .setName("Manual dexterity")
                    .setDescription("Worker anual dexterity")
                    .setFactoryEntityModelId(workerModel.getId()));

    CharacteristicValueDto workerManualDexterity = characteristicValueApi.createCharacteristicValue(
            new CharacteristicValueDto()
                    .setValues(new TreeMap<>() {{
                        put(LocalDateTime.now().toString(), "41");
                    }})
                    .setType(FieldType.NUMBER)
                    .setCharacteristicDescriptorId(manualDexterityDescriptor.getId())
                    .setFactoryEntityId(worker.getId()));

}