Skip to content

FCAPI - Overview

Gateway

API Documentation

feral.Gateway

The FERAL gateway module enables the connection to external simulators. External simulators may attach to FERAL and allow the following:

  • Communication with simulation components using value messages
  • Communication with simulated networks using values and raw messages
  • Access and change simulation properties
  • Synchronization to the FERAL clock

FERAL Gateway Concept

A FERAL gateway supports several simultaneous connections to remote applications. Multiple independent remote applications are supported, they don't need to run in the same process or even on the same physical machine. Every connection is passing through an individual worker port of the gateway, which are identified by unique names. The same application may create multiple connections to the same or to multiple FERAL gateways. A simulation script can instantiate at most one FERAL gateway.

FERAL.Documentation-FERAL.Gateway.Conceptv2.png

FERAL Gateway Concept

Above figure illustrates the static architecture of a FERAL gateway usage scenario. The gateway operates as a bridge to connect worker ports that are visible and attachable by outside clients to internal FERAL ports of the simulation. It allows clients to:

  • control and synchronize with the simulation time AND
  • access gateway and simulation properties AND
  • exchange data with FERAL workers

The details of these interaction levels are described in the Features chapter.

The communication channel between clients and gateway can be either network-bound (TCP or UDP) or via shared memory (limited to clients on the same machine). The clients must be implemented using the client libraries that are provided with a FERAL release. See chapter Client Libraries for more on that topic.

The gateway implementation can be found in the class de.fraunhofer.iese.feral.ext.pcc.fcapi.gateway.Gateway. It is a FERAL event-triggered worker that can be instantiated at most once in any FERAL simulation.

Example

simulation_script.py
import feral
from feral import BackendConfig, GatewayConfig, nanos, Sink

gateway_config = GatewayConfig(
expected_clients=1,
worker_ports=["Apple", "Banana", "just_another_port"],
backends=[BackendConfig(protocol=feral.TransmissionBackend.UDP, port=0)]
)

if __name__ == '__main__':
    gateway = feral.create_gateway(gateway_config)
    sink = Sink("sink")
    feral.link(gateway, "Apple", sink, "input")
    feral.start(nanos(100))
ExampleGatewayScenario_UDP.java
package de.fraunhofer.iese.feral.ext.pcc.fcapi.client.examples;

import de.fraunhofer.iese.feral.ext.pcc.fcapi.backend.TransmissionBackend;
import de.fraunhofer.iese.feral.ext.pcc.fcapi.client.integration.DelayedEchoDevice;
import de.fraunhofer.iese.feral.ext.pcc.fcapi.gateway.FeralPort;
import de.fraunhofer.iese.feral.ext.pcc.fcapi.gateway.Gateway;
import de.fraunhofer.iese.feral.ext.pcc.fcapi.gateway.scriptprocessor.DynamicPropertyProcessor;
import de.fraunhofer.iese.feral.krnl.scf.core.link.Link;
import de.fraunhofer.iese.feral.krnl.scf.core.mocc.discreteevent.DiscreteEventDirector;
import de.fraunhofer.iese.feral.krnl.scf.core.mocc.discreteevent.DiscreteEventMOCC;
import de.fraunhofer.iese.feral.krnl.scf.core.scenario.SimulationScenario;
import de.fraunhofer.iese.feral.krnl.scf.core.time.SimulationDuration;

public class ExampleGatewayScenario_UDP {

    public static void main(String... args) {
        SimulationScenario scenario = new SimulationScenario("Test Simulation");
        DiscreteEventDirector director = new DiscreteEventMOCC("event triggered director", scenario);

        Gateway gw = new Gateway(director, TransmissionBackend.UDP, 44444 , 1);

        DelayedEchoDevice echoDevice = new DelayedEchoDevice(director);

        FeralPort gwPortApple = gw.addNetworkPort("Apple");

        new Link(gwPortApple.getTxPort(), echoDevice.getInputPort());

        DynamicPropertyProcessor dpp = new DynamicPropertyProcessor();
        gw.setPropertyProcessor(dpp);
        final long[] nwProperties = { 23, 42 };
        dpp.registerNetworkDeviceProperty("Apple", "AppleProperty", () -> nwProperties[0],
                (i) -> nwProperties[0] = Long.parseLong((String) i));

        final long[] bitRate = { 100 };
        dpp.registerNetworkDeviceBitrateAccessors("Apple", () -> bitRate[0], (br) -> bitRate[0] = br);

        scenario.startSimulation(SimulationDuration.ns(100));
    }  
}

Client Libraries

API Documentation

feral.FCAPIClient

FERAL Gateway Client Libraries

FERAL gateway client libraries simplify the connection of applications to FERAL. Client libraries provide functions that encapsulate FERAL message based communication and provide communication backends.

Feral.Documentation-ClientLib.Overview.png

FERAL Client Libraries Overview

The Figure illustrates the usage of the C client library. The usage of the Java client library works very similar.

On the server side, the worker ports of the FERAL simulation's FERAL workers are connected to a FERAL Gateway. For the connect UDP, TCP and SHM (shared memory) are supported. For TCP and UDP, the client does not need to run on the same machine as the FERAL server. On the client, a client library implementation provides access to the functionality of the FERAL simulation. The client library is responsible for:

  • Managing connections between client and server, i.e. connection handling
  • Transmitting data between client and server in both directions
  • Transmitting Properties between client and server in both directions
  • Synchronization of Simulation Time between client and server

Example

demo_fcapi_client.py
import feral as feral
from feral.fcapi.client import FCAPIClient
from feral.fcapi.sync_cb_listener import SyncCBListener
from feral import nanos


class Listener(SyncCBListener):
    def callback(self, handle: int, sim_time: int):
        print("CB called at sim time " + str(sim_time) + " on handle " + str(handle))


if __name__ == '__main__':

    listener = Listener()

    client = FCAPIClient(feral.TransmissionBackend.UDP)
    handle = client.connect("localhost", 65327, "localhost", "Apple")

    sync_end_time = nanos(90)
    tx_data = [1, 2, 3, 4]

    client.syncCB(handle, nanos(42), listener, nanos(1))
    client.startSim(handle)
    client.wait_(handle)

    while True:
        current_time = client.getSimTime(handle)
        print("current time: " + str(current_time))

        if current_time >= sync_end_time:
            break

        client.tx(handle, tx_data)

        client.continue_(handle)
        client.wait_(handle)

    client.disconnectAll()
ExampleFCAPI_JavaClient_UDP.java
package de.fraunhofer.iese.feral.ext.pcc.fcapi.client.examples;

import static de.fraunhofer.iese.feral.krnl.scf.core.console.Loglevel.DEBUG;

import java.util.Arrays;

import de.fraunhofer.iese.feral.ext.pcc.fcapi.backend.TransmissionBackend;
import de.fraunhofer.iese.feral.ext.pcc.fcapi.client.ClientControl;
import de.fraunhofer.iese.feral.ext.pcc.fcapi.client.ClientException;
import de.fraunhofer.iese.feral.krnl.scf.core.console.DefaultConsole;

public class ExampleFCAPI_JavaClient_UDP {

    public static void main(String... args) throws ClientException {

        DefaultConsole console = new DefaultConsole(null);
        console.setLogLevel(DEBUG);

        ClientControl client = new ClientControl(console, TransmissionBackend.UDP);

        try {
            final int idTransmitter = client.connect("localhost", 44444, "localhost", "Apple");

            String[] appleProps = client.getNetworkProperties(idTransmitter);
            System.out.println("Properties of \"Apple\": " + Arrays.toString(appleProps));

            long sync_end_time = 90L; 
            final byte[] txData = { 1, 2, 3, 4 };

            client.sync(idTransmitter, 42L);
            client.startSim(idTransmitter);
            client.wait_(idTransmitter);

            while(true){
                long current_sim_time = client.getSimDuration(idTransmitter);
                System.out.println("current time: " + current_sim_time);

                if(current_sim_time >= sync_end_time){
                    break;
                }

                client.tx(idTransmitter,txData);
                client.continue_(idTransmitter);
                client.wait_(idTransmitter);
            }

            client.disconnect(idTransmitter);

        } catch(ClientException e) {
            System.out.println("ClientException: " + e);
        }

    }

}
ExampleFCAPI_CPP_Client_UDP.cpp
#include "client/ClientControl.h"
#include "client/ClientException.h"
#include "client/Log.h"
#include <iostream>
#include "include/feralcapi.h"


int main(){
    const uint8_t txData[] = {0, 1, 2, 3};

    fcapi_client::Log::setLevel(fcapi_client::LogLevel::LOG_DEBUG);
    try {

        fcapi_client::ClientControl cc(UDP);
        int idT = cc.connect("localhost", 44444, "localhost", "Apple");

        cc.sync(idT, nullptr, 42);
        cc.startSim(idT);
        cc.wait_(idT);

        while(true){

            if(cc.getSimDuration(idT) >= 90){
                break;
            }
            cc.tx(idT, txData, 4);
            cc.continue_(idT);
            cc.wait_(idT);

        }

        cc.disconnect(idT);

    } catch (fcapi_client::ClientException& e) {
        std::cerr << "ClientException: " << e.type << ": " << e.what() << std::endl;
        return 1;
    }

    return 0;

}