Skip to main content

OTcl and C++ linkages in ns2 | NS2 Basics | Lecture 4

Understanding the Link Between OTcl and C++ in ns-2

Welcome! This post explores the fundamental connection between OTcl and C++ in the Network Simulator 2 (ns-2). We'll cover how these two languages work together to create a powerful simulation environment and examine the key TclCL classes that make this architecture possible.


The Dual-Language Architecture of ns-2

Network Simulator 2 (ns-2) is built on a clever combination of two distinct languages: OTcl (an interpreted language) and C++ (a compiled language). This design strategically uses the unique strengths of each.

OTcl (The Frontend)

OTcl acts as the user-facing frontend of the simulator.

  • User-Friendly: It's ideal for beginners, as you can start running simulations without deep C++ knowledge.
  • Flexible and Fast to Modify: Since OTcl is interpreted, you can easily change network scenarios and parameters in your scripts without waiting for a lengthy re-compilation. This makes it perfect for testing and analysis.
  • Slower Execution: The trade-off is that interpreted code runs more slowly, which is why it's used for configuration rather than core processing.

C++ (The Backend)

C++ is the high-performance engine running in the background.

  • Fast Execution: As a compiled language, C++ code runs very quickly, which is essential for handling the complex, repetitive tasks of a network simulation.
  • Core Implementation: It's used to implement the detailed logic of network components like queues, protocols (TCP, UDP), packets, agents, and routing algorithms.
  • Slower Compilation: The downside is that any changes to the C++ source code require a full recompilation, which can be time-consuming.

By combining the two, ns-2 provides both a flexible interface for users (OTcl) and a fast, powerful simulation core (C++).


How OTcl Links to C++

When you write a command in an OTcl script, you are often creating and interacting with an object that is actually implemented in C++. Consider this OTcl code:

set tcp1 [new Agent/TCP]
set sink1 [new Agent/TCPSink]
set udp1 [new Agent/UDP]

In this snippet, you are creating three different agents. While the commands are written in OTcl, they instantiate C++ objects that run in the backend. The connection between the OTcl "name" and the C++ "class" is predefined within ns-2.

OTcl Object C++ Class Location of C++ File
Agent/TCP TcpClass ~ns-2.35/tcp/tcp.cc
Agent/TCPSink TcpSinkClass ~ns-2.35/tcp/tcp-sink.cc
Agent/UDP UdpAgentClass ~ns-2.35/apps/udp.cc

This link is managed by a special interface called TclCL.


The TclCL Interface: The Bridge Between Worlds 🌉

The connection between OTcl and C++ is made possible by the TclCL interface (~tclcl/), which contains a set of C++ classes that map OTcl objects to their C++ counterparts.

Here are the most important classes in this interface:

Class Purpose
Tcl Provides methods for C++ to access the OTcl interpreter, execute commands, and return results.
TclObject The fundamental base class for all C++ objects that need to be accessible from OTcl.
TclClass The core mapping tool. It links an OTcl class name to a C++ class and knows how to create instances of it.
TclCommand Used to define new global commands in the OTcl interpreter that are implemented in C++.
InstVar Allows C++ member variables to be exposed and modified directly from OTcl scripts.

A Closer Look: The Tcl Class

The Tcl class is the primary gateway for C++ to interact with the interpreter.

  • Get an Instance: First, you get a reference to the interpreter.
    Tcl& tcl = Tcl::instance();
  • Invoke Procedures: You can execute Tcl commands directly from C++.
    tcl.eval("some tcl command")
    tcl.evalf("puts \"Packet ID is %d\"", packet_id); (similar to printf)
  • Pass Results: You can send results from C++ back to the OTcl interpreter.
    tcl.result("Operation successful"); or tcl.resultf("%d", value);

The Core Mechanism: TclClass and TclObject

The magic of creating a C++ object from OTcl lies in the TclClass. It's a special C++ class that serves two main functions:

  1. It maps an interpreted class (in OTcl) to a compiled class (in C++).
  2. It provides a method to create an instance of the C++ object.

Let's look at the C++ code that links Agent/TCP in OTcl to the TcpAgent class in C++.

// From ~ns-2.35/tcp/tcp.cc
static class TcpClass : public TclClass {
public:
    TcpClass() : TclClass("Agent/TCP") {}
    TclObject* create(int, const char*const*) {
        return (new TcpAgent());
    }
} class_tcp;

Here's what this code does:

  • Mapping: The constructor TcpClass() : TclClass("Agent/TCP") tells ns-2 that the OTcl class Agent/TCP is handled by this TcpClass.
  • Hierarchy: The slash / in Agent/TCP defines a class hierarchy. Agent/TCP is a subclass of Agent, which is ultimately a subclass of TclObject.
  • Creation: When a user runs new Agent/TCP in an OTcl script, ns-2 finds this TcpClass and calls its create() method. This method then returns a new TcpAgent(), creating the actual C++ object. This linked C++/OTcl object is often called a "shadow object."

Practical Example: Building a Custom "Amplifier" Agent

Let's walk through the example of creating a simple agent that amplifies a number.

C++ Code (ampagent.cc)

This agent, named TSPAgent, will be accessible in OTcl as Agent/AMPLIFY.

/* A simple agent that amplifies a given value. */

// Header file declaration
#include <stdio.h>
#include <string.h>
#include "agent.h"

// 1. Define the Agent's C++ class
class TSPAgent : public Agent {
public:
    TSPAgent();
protected:
    int command(int argc, const char*const* argv);
private:
    int   amp1, amp2, amp3; // C++ member variables
    void  APrivateFunc(void);
};

// 2. Create the TclClass to link OTcl and C++
static class TSPAgentClass : public TclClass {
public:
    TSPAgentClass() : TclClass("Agent/AMPLIFY") {} // Map OTcl name to C++
    TclObject* create(int, const char*const*) {
        return (new TSPAgent()); // Create a new TSPAgent object
    }
} class_tsp_agent;

// 3. Implement the Agent's constructor and bind variables
TSPAgent::TSPAgent() : Agent(PT_UDP) {
    // Link C++ variables to OTcl instance variables
    bind("Ampli1", &amp1);
    bind("Ampli2", &amp2);
    bind("Ampli3", &amp3);
}

// 4. Implement the command function to handle OTcl calls
int TSPAgent::command(int argc, const char*const* argv) {
    if (argc == 2) {
        if (strcmp(argv[1], "call-the-private-function") == 0) {
            APrivateFunc();
            return (TCL_OK);
        }
    }
    return (Agent::command(argc, argv));
}

// 5. Implement the agent's core logic
void TSPAgent::APrivateFunc(void) {
    Tcl& tcl = Tcl::instance();
    tcl.eval("puts \"Message From the Private Function\"");
    tcl.evalf("puts \"  The Amplified Value is %d and the factor is 2\"", amp1 * amp1);
    tcl.evalf("puts \"  The Amplified Value is %d and the factor is 3\"", amp2 * amp2 * amp2);
    tcl.evalf("puts \"  The Amplified Value is %d and the factor is 4\"", amp3 * amp3 * amp3 * amp3);
}

The OTcl Test Script (agent.tcl)

This script creates the agent, sets its variables, and calls its custom function.

# Create a new instance of our custom agent
set myagent [new Agent/AMPLIFY]

# Set the C++ member variables from OTcl
$myagent set Ampli1 3 
$myagent set Ampli2 4
$myagent set Ampli3 5

# Call the custom command we defined in C++
$myagent call-the-private-function

How to Compile and Run the Agent

  1. Save the C++ code as ampagent.cc in a new directory, for example, ~ns-allinone-2.35/ns-2.35/newagent/.
  2. Open ~ns-2.35/Makefile.in and add an entry for the new object file under OBJ_CC:
    newagent/ampagent.o \
  3. Navigate to the ~ns-2.35/ directory in your shell and run the following commands to recompile ns-2:
    ./configure
    make
    
  4. Run the Tcl script:
    ns agent.tcl
    

Expected Output

Message From the Private Function
  The Amplified Value is 9 and the factor is 2 
  The Amplified Value is 64 and the factor is 3 
  The Amplified Value is 625 and the factor is 4

Conclusion

This post has detailed the essential linkage between OTcl and C++ in ns-2. By using OTcl for flexible frontend configuration and C++ for high-performance backend processing, ns-2 provides a powerful and extensible simulation environment. Understanding the role of TclCL, especially the TclClass and TclObject Relationship, is the key to creating and integrating your own custom C++ components into the simulator.

Comments

Popular posts from this blog

Installing ns3 in Ubuntu 22.04 | Complete Instructions

In this post, we are going to see how to install ns-3.36.1 in Ubuntu 22.04. You can follow the video for complete details Tools used in this simulation: NS3 version ns-3.36.1  OS Used: Ubuntu 22.04 LTS Installation of NS3 (ns-3.36.1) There are some changes in the ns3 installation procedure and the dependencies. So open a terminal and issue the following commands Step 1:  Prerequisites $ sudo apt update In the following packages, all the required dependencies are taken care and you can install all these packages for the complete use of ns3. $ sudo apt install g++ python3 python3-dev pkg-config sqlite3 cmake python3-setuptools git qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3 openmpi-bin openmpi-common openmpi-doc libopenmpi-dev autoconf cvs bzr unrar gsl-bin libgsl-dev libgslcblas0 wireshark tcpdump sqlite sqlite3 libsqlite3-dev  libxml2 libxml2-dev libc6-dev libc6-dev-i386 libc...

Installation of NS2 in Ubuntu 22.04 | NS2 Tutorial 2

NS-2.35 installation in Ubuntu 22.04 This post shows how to install ns-2.35 in Ubuntu 22.04 Operating System Since ns-2.35 is too old, it needs the following packages gcc-4.8 g++-4.8 gawk and some more libraries Follow the video for more instructions So, here are the steps to install this software: To download and extract the ns2 software Download the software from the following link http://sourceforge.net/projects/nsnam/files/allinone/ns-allinone-2.35/ns-allinone-2.35.tar.gz/download Extract it to home folder and in my case its /home/pradeepkumar (I recommend to install it under your home folder) $ tar zxvf ns-allinone-2.35.tar.gz or Right click over the file and click extract here and select the home folder. $ sudo apt update $ sudo apt install build-essential autoconf automake libxmu-dev gawk To install gcc-4.8 and g++-4.8 $ sudo gedit /etc/apt/sources.list make an entry in the above file deb http://in.archive.ubuntu.com/ubuntu/ bionic main universe $ sudo apt update Since, it...

Installation of NS2 (ns-2.35) in Ubuntu 20.04

Installation of NS2 (ns-2.35) in Ubuntu 20.04 LTS Step 1: Install the basic libraries like      $] sudo apt install build-essential autoconf automake libxmu-dev Step 2: install gcc-4.8 and g++-4.8 open the file using sudo mode $] sudo nano /etc/apt/sources.list Include the following line deb http://in.archive.ubuntu.com/ubuntu bionic main universe $] sudo apt update $] sudo apt install gcc-4.8 g++-4.8 Step 3:  Unzip the ns2 packages to home folder $] tar zxvf ns-allinone-2.35.tar.gz $] cd ns-allinone-2.35/ns-2.35 Modify the following make files. ~ns-2.35/Makefile.in Change @CC@ to gcc-4.8 change @CXX@ to g++-4.8 ~nam-1.15/Makefile.in ~xgraph-12.2/Makefile.in ~otcl-1.14/Makefile.in Change in all places  @CC@ to gcc-4.8 @CPP@ or @CXX@ to g++-4.8 open the file: ~ns-2.35/linkstate/ls.h Change at the Line no 137  void eraseAll() { erase(baseMap::begin(), baseMap::end()); } to This void eraseAll() { this->erase(baseMap::begin(), baseMap::end()); } All changes ...