Basic Concepts

In the following, we introduce core concepts of the platform including the corresponding APIs and programming models. This information is essential if you plan to design and implement custom overlays and services using the Simonstrator platform.

Time

To support discrete event-based simulations, all time-related calculations need to utilize the methods and constants provided in api.Time. Access to the current time is provided via Time.getCurrentTime(), which returns a long value. This value does not reflect the actual physical (system) time in most runtime environments. To perform relative calculations with the provided long, you need to utilize the respective constants:

  • Time.MICROSECOND, the highest resolution you can get
  • Time.MILLISECOND
  • Time.SECOND
  • Time.MINUTE
  • Time.HOUR

Now, assume you want to delete a routing table entry after the respective contact has been idle for more than ten seconds. You might have stored the Time.getCurrentTime() of the last communication as entry.lastSeen. Within your code, you can delete the contact if:

entry.lastSeen + 10 * Time.SECOND <= Time.getCurrentTime();

Time provides some auxiliary functions to print the current time in a human-readable format, which is useful for debugging - have a look at Time.getFormattedTime() and its extensions.

Scheduling Events

As a consequence of the event-based nature of the platform, parallelism and concurrency also needs to be encapsulated by the API. As a rule of thumb: never use threads or runnables in your code. Now, assume you want to execute an action after ten seconds. In Java, a (primitive) approach would be something like this:

class PeriodicStuff extends Thread {
	public void run() {
		Thread.sleep(10000);
		// do something after ten seconds
	}
}

When using the Simonstrator-API, you instead use the api.Event class. This class allows you to execute a given action after some time. The action is provided as a callback directly to the Event class:

EventHandler handler = new EventHandler() {
	public void eventOccurred(Object content, int type) {
		// do something
		// use the object/type fields to pass additional data
	}
}
Event.scheduleWithDelay(10*Time.SECOND, handler, null, 0);

Note the signature of Event.scheduleWithDelay: Besides the delay and the handler that is to be called, you can also pass an arbitrary object as content and an integer value as type. This enables easy distinction between different events if a common api.EventHandler is used. In this case, it is good coding practice to define the allowed types, i.e., the values you use for the type field, as constants.

Reoccurring Tasks

When you want to execute a given task periodically, this can of course be done by always scheduling a new event within the previous EventHandler’s eventOccured method. However, for convenience, you may also consider extending the api.operation.PeriodicOperation that provides a higher level of abstraction for periodic tasks.

Hosts

A host represents an entity such as a PC, a smartphone, a router, or any other networked device. The corresponding api.Host simply acts as a container for an arbitrary number of configured api.component.HostComponent instances. Every functional aspect of a host is realized as a host component: the network interfaces, its physical layer properties, any sensors, and, of course, overlays, services, and applications running on the host. Consequently, the actual functionality and role of a host within a scenario is solely defined by its configured host components.

Host Components

An api.component.HostComponent provides two basic lifecycle methods, initialize() and shutdown(), which are invoked by the respective runtime – no surprise – during initialization and shutdown. Additionally, it provides a reference to the current Host container via getHost(). Using this reference, a host component retrieves other host components to utilize their services via:

T otherComponent = getHost().getComponent(Class<T extends HostComponent> componentClass);

A variant of this method is able to return a list of components, if the interface you provided is implemented by multiple components. As you most likely need to retrieve components that enable network communication, two shorthands within Host are provided for that purpose:

public TransportComponent getTransportComponent();
public NetworkComponent getNetworkComponent();

In the following, we briefly discuss two common use cases: obtaining access to a network interface to send and receive messages and obtaining a host’s current position from a location sensor.

Network I/O

Assuming you want to communicate via UDP on a given port, you should bind the respective protocol within the initialize-method as follows (note, this method is provided for your convenience within the AbstractOverlayNode that is available in the SimonstratorOverlays project):

NetInterface net = getHost().getNetworkComponent().getByName(NetInterfaceName.WIFI);
UDP udp = host.getTransportComponent().getProtocol(UDP.class, net.getLocalInetAddress(), 12345);

In the example, we just created a UDP-socket on the wireless interface, listening on (and sending from) port 12345. Just keep a reference to the UDP object you just received to send and receive messages:

udp.setTransportMessageListener(/* Your Message Listener here! */);
udp.send(/* [...] */);

Usually, you will create a separate class as MessageHandler, pass the UDP object to that class and register that class as a TransportMessageListener as shown in the code snippet. Take a look at the existing overlays and services in the simonstrator-overlays project for more examples and best practices. Of course, you can also use TCP via the TCPMessageBased interface.

Location Sensor

Access to device-specific sensors is provided via a api.component.sensor.SensorComponent, which is essentially just a marker interface for a regular host component. Using the location sensor provided in api.component.sensor.location.LocationSensor, a host can determine its current position and request periodical updates.

Updated: