More advanced task graphs

By ProFuN

This tutorial shows how to make a single task run in multiple runtime copies, how to partition the network in non-overlapping regions, and how to limit the scopes of data flow paths between communicating tasks.

Table of Contents

Setting up a background image

In this tutorial we will work with a network placed on a real floor plan. We have prepared a sample apartment plan and a sample floor plan (based on a CC-BY licensed SVG file of a 4 apartment floor).

  • Tip: The tool does support SVG files directly, but scale of such a file is likely to be wrong. So the recommended option is to pre-create and use bitmap images (PNG files) with the desired size.

To import a background image, open the visual frontend, go to “Visual options” configuration dialog, browse for the target .png image file, and click on “Upload and set” (or just click OK).

For this tutorial, start with sample_apartment.png The result should look like this:

  • Tip: use zoom-out and zoom-on buttons at the lower right corner to get to the right size.

Start to populate the rooms with a few sensor nodes. Put at least two nodes in each of rooms 1..5:

Configuring copies

Let’s start designing a temperature-sensing network.

Switch to task sheet and drag temperature sensing task to the editor area. Press “Allocate”. What you see is likely something like this:

There’s just one temperature sensing task per whole network. Most likely, this is not what you want.

The other extreme is to have a task on each capable node. Doubleclick one the temperature task in task sheet, switch to “Frequency” tab, and configure “On each node” as the frequency value. The result:

What are the other options? You can specify the number of tasks of the same type to create - open task configuration again, select “Network-wide” in frequency field, and enter the desired number in “Number per area” field. The tasks are now seemingly randomly placed in the network.

  • Tip: the seemingly random task mapping may turn out to be exactly the same next time. To get a guaranteed pseudorandom allocation every time, use custom branching order or a custom objective function.

  • Tip: running in multithreaded mode also produces non-deterministic effects.
    Note: The allocator runs in multithreaded mode
    by default on multicore systems. To get consistent results, explicitly set the number of threads to one.

Partitioning the network

A more practical design of a sensor network may require that some temperature-sensing nodes are placed in each room. This invariant - that each room is going to get it’s share of sensing tasks - cannot be easily achieved just by multiplying the number of tasks required.

To solve this problem, ProFuN TG allows to partition the network; that is, to split it in non-overlapping regions.

First, network nodes must be configured with some properties. Then these properties can be used by the partitioning algorithm. A natural way to partition the network in our case is to assign a “room” property for each node, with value equal to the room number of the node.

To save some labor, it is convenient to assign properties to node templates rather than nodes themselves. In this example, five templates have to be created, and then all nodes in a specific room have to be assigned to that specific template.

In template configuration dialog configure a new property named “room”:

The result: The colors in which nodes are displayed has also been changed in this image, with larger room number corresponding to darker shade.

Now go to temperature node configuration, change “Frequency” to “On each partition”, change “Number per area” to 2 (not all rooms have more than two nodes), and open the partition configuration dialog.

The “New partition” dialog is very simple. In the “Name” field, enter an arbitrary non-empty name for the partition (must be unique within the model). In the “Expression” field, enter an expression on the nodes properties that evaluates to some value. The expression should be in Python syntax - it will be evaluated by Python interpreter. Typically, the value of this expression is going to be of a scalar type (integer or Boolean); however, that is not a requirement. It can be of of any type that defines a comparison operator, such as a string or an object.

For this tutorial, however, we are going to use an expression with integer value equal to room number:

The result is the desired - two tasks per each room:

More advanced regional divisions of the network (for example, divisions in overlapping regions) cannot be achieved by the ProFuN TG natively. Still, they can be emulated by the user through node properties and predicates. For example, a set of nodes may have a property named region with the same value, which then can be tested in a manually written predicate.

Similarly, dynamic partitioning can be achieved by extending ProFuN TG a third party tool that keeps track of dynamic properties for each node, and triggers task reallocation whenever these properties change.

Communication between task copies

For a new iteration of the example application, add a task that wants to read the temperature sensor an input. For example, the sensor may have to be connected to a heater actuator.

When you add link between the tasks, you may notice that the link automatically gets a non-default attribute named “scope” and set to “partitionLocal”:

The idea behind this: heaters ought to receive inputs only from sensors in their own room.

Also change the network. Either add new nodes with the “heater” component, or configure a different template for the existing nodes.

The resulting allocation:

If, instead, global communication is allowed (change link scope to “Global”), the result is this mess:

Don’t do this! Every copy of temperature task now talks to every copy; many-to-many communication is present. This pattern requires many active data flows and may spend significant amount of energy.

Inheritance rules

Some tasks usually do not have any specific hardware requirements. For example, an algorithmically simple data processing task can be executed on any node in the network. It makes sense to always put these tasks on nodes as close to the source task as possible, or as close to the destination task as possible.

For example, data filtering task should be put on the same node as its source task, because filtering messages before sending them can only reduce the total energy spent in the network. In contrast, a task that creates multiple copies of its input data should be put on the same node as its destination task.

For this tutorial, let’s use threshold function and print actuator. The threshold node compares the received value with a predefined threshold, and outputs either 1 or 0 depending on whether the binary predicate specified in the actuator is true. The actuator simply prints the values received and does not have any pre-defined component requirements.

Set “Inherit from upstream” as print actuators frequency (the threshold node already has that by default). Also explicitly set “Partition-local” as the scope for the connection between the threshold and the actuator.

The resulting allocation has a chain of connected tasks instantiated on each node. No node-to-node data flows were required by these additional tasks.

Handling large networks

ProFuN TG is visual interface quite capable of handling much larger networks. Here are some test results:

Download

The resulting model files are available: