How to make a campaign of simulation?¶
A campaign of simulation usually involve making a variable change through several run or several nodes.
Makesense leverages Jinja2 to create very easily as many firmware as necessary by using templating. Instead of writing a C code directly we will write C code replacing the variables and function by templates variables that jinja2 will remplace by variables existing in a python code. By doing so we can for instance create very easily a loop iterating through a list of desired values for a variable and let makesense handle all the trouble of creating those files, compile them and deploy them on a testbed.
Step 1: Creating a C template¶
First we will create a simple C code that print a message through a loop.
#include <stdio.h>
#include "contiki.h"
static int my_value = {{ my_value }};
/*---------------------------------------------------------------------------*/
PROCESS(dummy_hello_world_process, "Hello world process");
AUTOSTART_PROCESSES(&dummy_hello_world_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(dummy_hello_world_process, ev, data)
{
PROCESS_BEGIN();
while(1) {
printf("Hello, world. My value is %d\n", my_value);
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
static int my_value = {{ my_value }};
It’s at this place that jinja2 will put the my_value variable. For more information check out the Jinja2 documentation.
This makefile will compile all the nodes.
SRC=$(wildcard [!symbols]*.c)
PROGS = $(patsubst %.c,%,$(SRC))
all: $(PROGS)
CONTIKI={{ contiki }}
TARGET={{ target }}
include $(CONTIKI)/Makefile.include
# vi:filetype=make:ts=4:sw=4:et
Step 2: Let’s loop¶
Suppose that you want to create firmware for 42 different values we would do it in the fabfile:
import os
import json
from os.path import join as pj
from fabric.api import task
from jinja2 import Environment, FileSystemLoader
# Default values
ROOT_DIR = os.path.dirname(__file__)
EXPERIMENT_FOLDER = pj(ROOT_DIR, "experiments")
TEMPLATE_FOLDER = pj(ROOT_DIR, "templates")
TEMPLATE_ENV = Environment(loader=FileSystemLoader(TEMPLATE_FOLDER))
@task
def my_special_function(name):
""" This function will create 42 C files. """
path = pj(EXPERIMENT_FOLDER, name)
if not os.path.exists(path):
os.makedirs(path)
c_template = TEMPLATE_ENV.get_template("dummy_template.c")
# We make the id start at 1 and finish at 42
for value in range(1, 43):
with open(pj(path, "dummy_%d.c" % value), "w") as f:
f.write(c_template.render(my_value=value))
# If you change the platform target and want to push to iotlab
# don't forget to update the nodes names
makefile_template = TEMPLATE_ENV.get_template("dummy_makefile")
with open(pj(path, "Makefile"), "w") as f:
f.write(makefile_template.render(contiki=CONTIKI_FOLDER,
target="iotlab-m3"))
config_template = TEMPLATE_ENV.get_template("dummy_iotlab.json")
res = [
{"nodes": ["m3-%d.grenoble.iot-lab.info" % num],
"firmware_path": pj(path, "dummy_%d.iotlab-m3" % num)
} for num in range(1, 43)]
with open(pj(path, "iotlab.json"), "w") as f:
f.write(json.dumps(res, sort_keys=True,
indent=4, separators=(',', ': ')))
Then we would call this function like any other fabric function
fab my_special_function:dummy
You should have files like:
- dummy_1.iotlab-m3
- ...
- dummy_42.iotlab-m3
created in experiments/dummy
You should also have an iotlab.json looking like:
[
{
"firmware_path": "/home/sieben/Dropbox/workspace/makesense/experiments/prout/dummy_1.iotlab-m3",
"nodes": [
"m3-1.grenoble.iot-lab.info"
]
},
...
]