ns3-fmi-export

Module fmi-export enables the FMI-compliant simulation coupling with ns-3 scripts.

DOI

The ns-3 FMI Export Module

About

Module fmi-export enables the FMI-compliant simulation coupling with ns-3 scripts, i.e., ns-3 script are launched and executed through an FMI-compliant co-simulation interface. In terms of FMI terminology, ns-3 is the slave application, and generated FMUs launch ns-3 and synchronize its execution during runtime (tool coupling).

Module fmu-examples provides examples for using the fmi-export module. The module comprises dedicated models (clients and servers), helpers and simulation scripts implementing example applications, whose functionality is then exported as FMU for Co-Simulation. Furthermore, test applications (written in Python) show how the resulting FMUs can be used in a simulation.

Prerequisites and installation on Ubuntu 20.04

Follow these instructions to install the fmi-export module:

  1. Install required dependencies:
    sudo apt install build-essential
    sudo apt install cmake
    sudo apt install unzip
    sudo apt install libboost1.71-all-dev
    
  2. This module relies on a lot of functionality provided by the FMI++ library. Hence, in order to install this module, the latest version of the FMI++ library (commit 10b4dbe) should be cloned from its repository:
    git clone https://github.com/fmipp/fmipp.git
    cd fmipp
    git checkout 10b4dbe
    cd ..
    
  3. Get the source code from GitHub.
    git clone https://github.com/ERIGrid/ns3-fmi-export.git
    
  4. Get the ns-3 code (tested with release version ns-3.33).
    git clone https://gitlab.com/nsnam/ns-3-dev.git
    cd ns-3-dev
    git checkout ns-3.33
    
  5. From the source code, copy the fmi-export directory (and the fmu-examples directory if you want to include examples) to the src subdirectory of ns-3, i.e., the directory with all the other ns-3 modules.
    cp -R /path/to/cloned/ns3-fmi-export/fmi-export/ src/
    cp -R /path/to/cloned/ns3-fmi-export/fmu-examples/ src/
    
  6. Configure waf with the –with-fmi-export flag set to the previously cloned FMI++ library:
    ./waf configure --with-fmi-export=/path/to/cloned/fmipp
    
  7. Build the module using waf:
    ./waf
    
  8. If you want to run all the examples (see below for more information), you can run script run-tests.sh:
    cd src/fmu-examples/examples
    chmod +x run-tests.sh
    ./run-tests.sh
    

Prerequisites and installation in a Cygwin environment (Windows)

ns-3 is mainly developed for Linux, but it can also be installed on Windows in a 32-bit Cygwin environment. Please refer to commit 122190b for details.

FMI-compliant ns-3 scripts

ns-3 scripts have to implement the abstract class SimpleEventQueueFMUBase. This class provides a simple event queue that can be synchronized via an FMI-compliant interface. Incoming/outgoing messages are associated with input/output variables. Whenever a message is sent from a network node, the associated input variable is set to a non-zero integer value, referred to as message ID. When the same message is received at another network node, the associated output variable is set to the same message ID. The sending/receiving of messages is simulated with individual ns-3 simulation runs, whose results are stored as events in the queue.

To define an FMI-compliant ns-3 script, a new class inheriting from SimpleEventQueueFMUBase has to be implemented, which provides the following two methods:

After the definition of the class, the macro CREATE_NS3_FMU_BACKEND has to be used. This macro replaces the typical main methods of ns-3 scripts.

FMU generation using Python scripts

FMUs can be generated using the Python script ns3_fmu_create.py. The FMU is created by executing the Python script from the command line:

     ns3_fmu_create.py [-h] [-v] -m <model_id> -s <ns3_script> \
        [-f <fmi_version>] [<additional_file_1> ... <additional_file_N>] \
        [var1=start_val1 ... varN=start_valN]

Optional arguments are enclosed by squared brackets […].

Mandatory input arguments

Optional input arguments

Additional files may be specified (e.g., CSV input lists) that will be automatically copied to the FMU. The specified files paths may be absolute or relative.

Start values for variables and parameters may be defined. For instance, to set variable with name var1 to value 12.34, specify var1=12.34 in the command line as optional argument.

Using an FMU generated for ns-3

During simulation, interaction with the FMU is basically limited to the use of three (types of) functions:

Examples

The example applications are test cases from the ERIGrid project:

Module fmu-examples provides the models for these test cases. These models implement dedicated clients and servers, which provide the functionality to extract the end-to-end delay of message transmissions. Based on these end-to-end delays, the ns-3 simulation scripts add events to the event queue of the FMU.

The classes ClientBase and ServerBase are the bases classes for all the clients and servers implemented for the example applications. The implemented clients and servers are examples of how callback functions can be used to calculate end-to-end delays. Helpers for including the clients and servers into simulations scripts are provided. All helpers are specializations of the template base classes ClientHelperBase and ServerHelperBase.

The examples are implemented in dedicated ns-3 scripts, which can be found in the module’s subdirectory examples/scratch. The scripts can be translated to FMUs for Co-Simulations using Python script ns3_fmu_create.py (from module fmi-export). Test applications (written in Python) using these FMUs can be found in module’s subdirectory examples/test.

To build the examples, copy directory fmu-examples to ns-3’s src directory (i.e., the directory with all the other ns-3 modules). Then change into the ns-3 root directory and build the module using waf.

Example SimpleFMU

Overview

The ns-3 script of the simple example can be found (SimpleFMU.cc). It implements a simple simulation in which one node (A) send messages to another node (B).

The script defines class SimpleFMU, which inherits from class SimpleEventQueueFMUBase:

Creating the FMU

Create the FMU with the help of Python script ns3_fmu_create.py. In the command line, go to the example directory src/fmu-examples/examples and issue the following command:

  $ ./../../fmi-export/ns3_fmu_create.py -v -m SimpleFMU -s scratch/SimpleFMU.cc -f 1 channel_delay=0.2

This command does the following:

The output of the script in the command line should be something along the following lines. (Note that waf is called twice during the process.)

    [DEBUG] Using FMI version 1
    [DEBUG] Found start value:  channel_delay = 0.2
    Waf: Entering directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Waf: Leaving directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Build commands will be stored in build/compile_commands.json
    'build' finished successfully (1.363s)

    Modules built:
    antenna			aodv					applications
    bridge				buildings				config-store
    core				csma					csma-layout
    dsdv				dsr					energy
    flow-monitor			fmi-export (no Python)	fmu-examples (no Python)
    internet			internet-apps			lr-wpan
    lte					mesh				mobility
    mpi				netanim (no Python)		network
    nix-vector-routing		olsr					point-to-point
    point-to-point-layout	propagation			sixlowpan
    spectrum			stats					test (no Python)
    topology-read		traffic-control			uan
    virtual-net-device		wave					wifi
    wimax

    Modules not built (see ns-3 tutorial for explanation):
    brite					click					fd-net-device
    openflow				tap-bridge				visualizer

    [DEBUG] successfully compiled ns-3 script
    Waf: Entering directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Waf: Leaving directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Build commands will be stored in build/compile_commands.json
    'build' finished successfully (1.349s)
    [DEBUG] successfully created JSON script
    [DEBUG] FMI model identifier:  SimpleFMU
    [DEBUG] ns-3 script:  scratch/SimpleFMU.cc
    [DEBUG] ns-3 install directory:  /cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev
    [DEBUG] Aditional files:
    [DEBUG] Added start value to model description:  channel_delay = 0.2
    [DEBUG] FMU created successfully: SimpleFMU.fmu

Using the FMU in a simulation

Python script testSimpleFMU.py uses the generated FMU in a simulation. It can be found in the module’s subdirectory examples/test. When running the simulation script, the output should be similar to the following:

    [test_sim_ict] WARNING: The path specified for the FMU's entry point does not exist: ""
    Use directory of main application as working directory instead.
    [test_sim_ict] MIME-TYPE: Wrong MIME type: application/x-waf --- expected:
    Waf: Entering directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Waf: Leaving directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Build commands will be stored in build/compile_commands.json
    'build' finished successfully (1.406s)
    ================================================
    simulation time : 0.0
    next event time : 0.0
    next send time : 1.0
    ================================================
    simulation time : 1.0
    next event time : no next event specified
    next send time : 1.0
    At time 1.00000: SEND message with ID = 1
    ================================================
    simulation time : 1.301686399
    next event time : 1.301686399
    next send time : 2.0
    At time 1.30169: RECEIVE message with ID = 1
    ================================================
    simulation time : 2.0
    next event time : no next event specified
    next send time : 2.0
    At time 2.00000: SEND message with ID = 2
    ================================================
    simulation time : 2.301686399
    next event time : 2.301686399
    next send time : 3.0
    At time 2.30169: RECEIVE message with ID = 2
    ================================================
    simulation time : 3.0
    next event time : no next event specified
    next send time : 3.0
    At time 3.00000: SEND message with ID = 3
    ================================================
    simulation time : 3.301686399
    next event time : 3.301686399
    next send time : 4.0
    At time 3.30169: RECEIVE message with ID = 3
    ================================================

Example TC3

Creating the FMU

Create the FMU with the help of Python script ns3_fmu_create.py. In the command line, go to the example directory src/fmu-examples/examples and issue the following command:

     $ ./../../fmi-export/ns3_fmu_create.py -v -m TC3 -s scratch/TC3.cc -f 1

Using the FMU in a simulation

Python script testTC3.py uses the generated FMU in a simulation. It can be found in the module’s subdirectory examples/test. When running the simulation script, the output should be similar to the following:

    [test_sim_ict] WARNING: The path specified for the FMU's entry point does not exist: ""
    Use directory of main application as working directory instead.
    [test_sim_ict] MIME-TYPE: Wrong MIME type: application/x-waf --- expected:
    Waf: Entering directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Waf: Leaving directory `/cygdrive/c/Development/erigrid/ns-3-allinone/ns-3-dev/build'
    Build commands will be stored in build/compile_commands.json
    'build' finished successfully (1.415s)
    ==========================================
    simulation time : 0.0
    next event time : 0.0
    next send time : 1.0
    ctrl_msg_id = 0
    ==========================================
    simulation time : 1.0
    next event time : 1.0
    next send time : 1.0
    At time 1.00000: SEND messages to controller with ID = 1 and  ID = -1
    ctrl_msg_id = 0
    ==========================================
    simulation time : 1.039132019
    next event time : 1.039132019
    next send time : 2.0
    ctrl_msg_id = 1
    At time 1.03913: RECEIVE message at controller with ID = 1
    At time 1.03913: SEND message from controller with ID = 10
    ==========================================
    simulation time : 1.049157658
    next event time : 1.049157658
    next send time : 2.0
    ctrl_msg_id = -1
    At time 1.04916: RECEIVE message at controller with ID = -1
    At time 1.04916: SEND message from controller with ID = -10
    ==========================================
    simulation time : 1.079941038
    next event time : 1.079941038
    next send time : 2.0
    ctrl_msg_id = 0
    At time 1.07994: RECEIVE message at transformer with ID = 10
    ==========================================
    simulation time : 1.089867677
    next event time : 1.089867677
    next send time : 2.0
    ctrl_msg_id = 0
    At time 1.08987: RECEIVE message at transformer with ID = -10
    ==========================================
    simulation time : 2.0
    next event time : 2.0
    next send time : 2.0
    At time 2.00000: SEND messages to controller with ID = 2 and  ID = -2
    ctrl_msg_id = 0
    ==========================================
    simulation time : 2.039015019
    next event time : 2.039015019
    next send time : 3.0
    ctrl_msg_id = 2
    At time 2.03902: RECEIVE message at controller with ID = 2
    At time 2.03902: SEND message from controller with ID = 20
    ==========================================
    simulation time : 2.049040658
    next event time : 2.049040658
    next send time : 3.0
    ctrl_msg_id = -2
    At time 2.04904: RECEIVE message at controller with ID = -2
    At time 2.04904: SEND message from controller with ID = -20
    ==========================================
    simulation time : 2.076761038
    next event time : 2.076761038
    next send time : 3.0
    ctrl_msg_id = 0
    At time 2.07676: RECEIVE message at transformer with ID = 20
    ==========================================
    simulation time : 2.090786677
    next event time : 2.090786677
    next send time : 3.0
    ctrl_msg_id = 0
    At time 2.09079: RECEIVE message at transformer with ID = -20
    ==========================================
    simulation time : 3.0
    next event time : 3.0
    next send time : 3.0
    At time 3.00000: SEND messages to controller with ID = 3 and  ID = -3
    ctrl_msg_id = 0
    ==========================================
    simulation time : 3.041898019
    next event time : 3.041898019
    next send time : 4.0
    ctrl_msg_id = -3
    At time 3.04190: RECEIVE message at controller with ID = -3
    At time 3.04190: SEND message from controller with ID = -30
    ==========================================
    simulation time : 3.052203658
    next event time : 3.052203658
    next send time : 4.0
    ctrl_msg_id = 3
    At time 3.05220: RECEIVE message at controller with ID = 3
    At time 3.05220: SEND message from controller with ID = 30
    ==========================================
    simulation time : 3.079644038
    next event time : 3.079644038
    next send time : 4.0
    ctrl_msg_id = 0
    At time 3.07964: RECEIVE message at transformer with ID = -30
    ==========================================
    simulation time : 3.096913677
    next event time : 3.096913677
    next send time : 4.0
    ctrl_msg_id = 0
    At time 3.09691: RECEIVE message at transformer with ID = 30
    ==========================================