Fin Python Library

Documentation of the Python Fin library

For the documentation about the Fin protocol we refer to documention page of SOARCA Fin Protocol.

Quick Start

To include the SOARCA Fin library, you can use the following command to install it via pip:

pip install soarca-fin-library

Example

An example on how to use the library is given below. For more examples and the source code, we will refer to the Github page of the SOARCA-Fin-python-library, where we provide /examples folder.

import os
from dotenv import load_dotenv

from soarca_fin_python_library.soarca_fin import SoarcaFin
from soarca_fin_python_library.models.agent_structure import AgentStructure
from soarca_fin_python_library.models.external_reference import ExternalReference
from soarca_fin_python_library.models.step_structure import StepStructure
from soarca_fin_python_library.models.capability_structure import CapabilityStructure
from soarca_fin_python_library.enums.workflow_step_enum import WorkFlowStepEnum
from soarca_fin_python_library.models.command import Command
from soarca_fin_python_library.models.result_structure import ResultStructure

from soarca_fin_python_library.models.variable import Variable
from soarca_fin_python_library.enums.variable_type_enum import VariableTypeEnum


def capability_pong_callback(command: Command) -> ResultStructure:
    print("Received ping, returning pong!")

    result = Variable(
        type=VariableTypeEnum.string,
        name="pong_output",
        description="If ping, return pong",
        value="pong",
        constant=True,
        external=False)

    context = command.command.context

    return ResultStructure(
        state="success", context=context, variables={"result": result})


def main(mqtt_broker: str, mqtt_port: int, username: str, password: str) -> None:

    finId = "soarca-fin--pingpong-f877bb3a-bb37-429e-8ece-2d4286cf326d"
    agentName = "soarca-fin-pong-f896bb3b-bb37-429e-8ece-2d4286cf326d"
    externalReferenceName = "external-reference-example-name"
    capabilityId = "mod-pong--e896aa3b-bb37-429e-8ece-2d4286cf326d"

    # Create AgentStructure
    agent = AgentStructure(
        name=agentName)

    # Create ExternalReference
    external_reference = ExternalReference(name=externalReferenceName)

    # Create StepStructure
    step_structure = StepStructure(
        name="step_name",
        description="step description",
        external_references=[external_reference],
        command="pong",
        target=agentName)

    # Create CapabilityStructure
    capability_structure = CapabilityStructure(
        capability_id=capabilityId,
        type=WorkFlowStepEnum.action,
        name="Ping Pong capability",
        version="0.0.1",
        step={
            "test": step_structure},
        agent={
            "testagent": agent})

    # Create Soarca fin
    fin = SoarcaFin(finId)
    # Set config for MQTT Server
    fin.set_config_MQTT_server(mqtt_broker, mqtt_port, username, password)
    # Register Capabilities
    fin.create_fin_capability(capability_structure, capability_pong_callback)
    # Start the fin
    fin.start_fin()


if __name__ == "__main__":
    load_dotenv()
    MQTT_BROKER = os.getenv("MQTT_BROKER", "localhost")
    MQTT_PORT = int(os.getenv("MQTT_PORT", "1883"))
    USERNAME = os.getenv("MQTT_USERNAME", "soarca")
    PASSWD = os.getenv("MQTT_PASSWD", "password")

    main(MQTT_BROKER, MQTT_PORT, USERNAME, PASSWD)

Below we have provided an example env file. Note that this changes according to your setup.

MQTT_BROKER = "localhost"
MQTT_PORT = "1883"
MQTT_USERNAME = "soarca"
MQTT_PASSWD = "password"

Env file can be exported by running:

export $(cat .env | grep -v "#" | xargs)

Architecture

The main object of the application is the SoarcaFin object, which is responsible for configuring and creating and controlling the capabilities. The SoarcaFin creates MQTTClients for each capability registered, plus one for registering, unregistering and controlling the fi itself. MQTTClients each have their own connection to the MQTT Broker and own Parser and Executor objects. The Parser object parsers the raw MQTT messages and tries to convert them to one of the objects in src/models. The Executor runs in their own thread and handles the actual execution of the messages. The Executor polls a thread-safe queue for new messages and performs IO operations, such as sending messages to the MQTT broker and calling capability callbacks.

Setup SOARCA Capabilities

To register a fin to SOARCA, first create a SoarcaFin object and pass the fin_id in the constructor. The SOARCA fin_id must be in the format of: sourca-fin-<capability>-<uuid4>. Call set_config_MQTT_server() to set the required configurations for the fin to connect to the MQTT broker. For each capability to be registered, call create_fin_capability(). The capability callback funtion should return an object of type ResultStructure. When all capabilities are initialized, call start_fin() for the SOARCA Fin to connect to the MQTT broker and register itself to SOARCA.

An example is given in this project in the file [examples/pong_example.py]

Class Overview

interface IParser {
 Message parse_on_message()
}

interface IMQTTClient {
 void on_connect()
 void on_message()
}

interface ISoarcaFin {
 void set_config_MQTTServer()
 void set_fin_capabilities()
 void start_fin()
}

interface IExecutor {
 void queue_message()
}


class SoarcaFin
class MQTTClient
class Parser
class Executor

ISoarcaFin <|.. SoarcaFin
IMQTTClient <|.. MQTTClient
IParser <|.. Parser
IExecutor <|.. Executor

IMQTTClient <- SoarcaFin
MQTTClient -> IExecutor
IParser <-MQTTClient

Sequence Diagrams

Command

Soarca -> "MQTTClient (Capability 1)" : Command Message [Capability ID Topic]

"MQTTClient (Capability 1)" -> Parser : parse_on_message(message)
"MQTTClient (Capability 1)" <-- Parser : Message.Command

"MQTTClient (Capability 1)" -> "Executor (Capability 1)" : Command message
Soarca <-- "Executor (Capability 1)" : Ack

"Executor (Capability 1)" -> "Capability Callback" : Command
"Executor (Capability 1)" <-- "Capability Callback" : Result


Soarca <- "Executor (Capability 1)" : Result
Soarca --> "MQTTClient (Capability 1)" : Ack

"MQTTClient (Capability 1)" -> Parser : parse_on_message(message)
"MQTTClient (Capability 1)" <-- Parser : Message.Ack

"MQTTClient (Capability 1)" -> "Executor (Capability 1)" : Ack message

Register

Soarca -> Soarca : Create Soarca Topic

Library -> SoarcaFin : Set MQTT Server config

Library -> SoarcaFin : Set Capability1
SoarcaFin -> "MQTTClient (Capability 1)" : Create capability

Library -> SoarcaFin : Set Capability2
SoarcaFin -> "MQTTClient (Capability 2)" : Create capability


Library -> SoarcaFin : Start Fin


SoarcaFin -> "MQTTClient (Capability 1)" : Start capability
"MQTTClient (Capability 1)" -> "MQTTClient (Capability 1)" : Register Capability Topic
SoarcaFin -> "MQTTClient (Capability 2)" : Start capability
"MQTTClient (Capability 2)" -> "MQTTClient (Capability 2)" : Register Capability Topic

SoarcaFin -> "MQTTClient (Fin)" : Register Fin
"MQTTClient (Fin)" -> "MQTTClient (Fin)" : Register SoarcaFin Topic

"MQTTClient (Fin)" -> "Executor (Fin)" : Send Register Message

Soarca <- "Executor (Fin)" : Message.Register [Soarca Topic]

Soarca --> "MQTTClient (Fin)" :  Message.Ack [Fin ID Topic]

"MQTTClient (Fin)" -> "Parser (Fin)" : parse_on_message(ack)
"MQTTClient (Fin)" <-- "Parser (Fin)" : Message.Ack

"MQTTClient (Fin)" -> "Executor (Fin)" : Message.Ack

Bugs or Contributing

Want to contribute to this project? It is possible to contribute here. Have you found a bug or want to request a feature? Please create an issue here.