Exercise with Veins
The goal of this exercise is to customize the Veins DEMO Scenario, which is well described in the Veins Tutorial by P. B. Stahorszki
Essentially, we just want the cars to communicate to each other, without the need of any Road Side Unit (RSU). Then, we want to change the “message propagation strategy”, i.e., messages should become multihop, with the number of hops defined through a Time To Live (TTL) field. Moreover, we want cars to propagate only messages they never propagated before, which means we want to implement a mechanism to avoid duplicates. Finally, we want see how much the implemented strategies influence the amount of generated traffic. Let’s start!
1. Update SUMO Configuration
As a preliminary step, let’s reduce the number of vehicles going around during our simulation, so it will be easier to track what each car is doing.
erlangen.rou.xml
<routes>
...
<flow id="flow0" type="vtype0" route="route0" begin="0" period="20" number="5"/>
</routes>
Then, similarly to what done in the SUMO authobahn tutorial, define the GUI settings for SUMO, save them in a file called guisettings.xml and instruct Veins-launchd to use them when running the simulation.
erlangen.sumo.cfg
...
<additional-files value="erlangen.poly.xml"/>
<gui-settings-file value="guisettings.xml"/>
</input>
erlangen.launchd.xml
...
<copy file="erlangen.poly.xml" />
<copy file="guisettings.xml" />
<copy file="erlangen.sumo.cfg" type="config" />
</launch>
2. Simplify Scenario
Let’s get rid of the RSU, as we want the communication to happen only among vehicles (V2V), but not towards any infrastructure (V2X). This is an easy step.
- Define a new scenario identical to the original RSUExampleScenario but remove the RSU declared internally as a submodule. Let’s call this file demoVNCDScenario.ned and let the network inside be called, again, DemoVNCDScenario
demoVNCDScenario.ned
network demoVNCDScenario extends Scenario
{
// submodules:
// rsu[1]: RSU {
// @display("p=150,140;i=veins/sign/yellowdiamond;is=vs");
// }
}
- Update the omnetpp.ini. You must change the network name and you can delete all the RSU configuration details
omnetpp.ini
network = RSUExampleScenario
network = demoVNCDScenario
Delete these lines
##########################################################
# RSU SETTINGS #
# #
# #
##########################################################
*.rsu[0].mobility.x = 2000
*.rsu[0].mobility.y = 2000
*.rsu[0].mobility.z = 3
*.rsu[*].applType = "TraCIDemoRSU11p"
*.rsu[*].appl.headerLength = 80 bit
*.rsu[*].appl.sendBeacons = false
*.rsu[*].appl.dataOnSch = false
*.rsu[*].appl.beaconInterval = 1s
*.rsu[*].appl.beaconUserPriority = 7
*.rsu[*].appl.dataUserPriority = 5
*.rsu[*].nic.phy80211p.antennaOffsetZ = 0 m
Delete also these lines as we don’t want to handle anymore the channelSwitching and beaconing cases
[Config WithBeaconing]
*.rsu[*].appl.sendBeacons = true
*.node[*].appl.sendBeacons = true
[Config WithChannelSwitching]
*.**.nic.mac1609_4.useServiceChannel = true
*.node[*].appl.dataOnSch = true
*.rsu[*].appl.dataOnSch = true
3. Meet TraCI in Veins
Now (in theory) our simulation works exactly as in the original Veins DEMO Scenario, just without the assistance of the RSU repeating the received messages with a 2 second delay for those vehicles that approach later the location of the accident.
Unfortunately, in TraCIDemo11p.cc the car color feature —cars with accident turning red, while cars that received at least one Wave Short Message turn green— is implemented only for the Omnet GUI, but not for the SUMO GUI. Let’s start learning how to interact with SUMO via the VEINS traci interface by turning to RED the cars that suffer an accident.
BTW, Did you understand already how accidents are “forced to happen” in the simulation? It’s through the use of the startAccidentMsg inside TraCIMobility.cc Let’s add there some code to change the vehicle color.
We will need first to get the vehicle command interface (there is a standard method to do this)
Then the vehicle command interface supports the method
setColor(TraCIColor(red,green,blue,alpha))
SOLUTION
void TraCIMobility::handleSelfMsg(cMessage* msg)
{
if (msg == startAccidentMsg) {
getVehicleCommandInterface()->setSpeed(0);
// turn RED the car with accident
getVehicleCommandInterface()->setColor(TraCIColor(255, 0, 0, 255));
simtime_t accidentDuration = par("accidentDuration");
scheduleAt(simTime() + accidentDuration, stopAccidentMsg);
accidentCount--;
}
else if (msg == stopAccidentMsg) {
getVehicleCommandInterface()->setSpeed(-1);
// turn back to YELLOW when accident is over
getVehicleCommandInterface()->setColor(TraCIColor(255, 255, 0, 255));
if (accidentCount > 0) {
simtime_t accidentInterval = par("accidentInterval");
scheduleAt(simTime() + accidentInterval, startAccidentMsg);
...
4. Customize the Application Logic
If we want to implement a propagation-limit based on TTLs we need to add a TTL field to our messages. Furthermore, if we want to avoid duplicates we need a mechanism to uniquely identify each “generated packet”. Since every car can generate at least one packet in a given instant of time, the tuple <senderAddress, timestamp>
is unique for each packet. Customize the TraCIDemo11pMessage.msg so to include the 3 required fields just discussed.
TraCIDemo11pMessage.msg
packet TraCIDemo11pMessage extends BaseFrame1609_4 {
string demoData;
LAddress::L2Type senderAddress = -1;
int TTL = 4;
omnetpp::simtime_t timestamp;
}
Define parameters to control the application logic. Indeed, if later we want to set up a parameter study, we wish to be able to conveniently define TTL values from the omnetpp.ini file. Similarly, we wish to toggle on/off the ability of avoiding duplicates. So introduce 2 parameters in the TraCIDemo11p.ned file to be able to customize the Application behavior.
simple TraCIDemo11p extends DemoBaseApplLayer
{
parameters:
@class(veins::TraCIDemo11p);
@display("i=block/app2");
bool avoidDuplicates = default(true);
int TTL = default(2);
}
Implementation time:
To implement the “duplicates avoidance strategy” we need to keep in memory the set of already seen messages. We also need to know, inside the application code, what is the TTL number configured by the user in the omnetpp.ini. Introduce the set of received messages, a TTL variable and “avoidDuplicates” flag in TraCIDemo11p.h
TraCIDemo11p.h
#include <set>
#include <tuple>
namespace veins {
...
protected:
simtime_t lastDroveAt;
int currentSubscribedServiceId;
int TTL;
bool avoidDuplicates;
std::set<std::tuple<LAddress::L2Type, omnetpp::simtime_t>> rcvd_messages;
and initialize them within the usual initialize()
function
TraCIDemo11p.c
void TraCIDemo11p::initialize(int stage)
{
...
if (stage == 0) {
...
avoidDuplicates = par("avoidDuplicates").boolValue();
TTL = par("TTL").intValue();
}
}
Now try to complete the implementation! Essentially you must add some logic to the onWSM() function used to handle the reception of Wave Short Messages and, in particular:
- if avoidDuplicates is
true
then you must check if the receivedwsm
is already included inrcvd_messages
- if so, ignore it;
- otherwise, add it to the set of received messages and then check the TTL.
- if the TTL is not expired, decrease the TTL and propagate the packet.
- When the car that suffers an accident generates a packet, don’t forget to correctly set the
TTL
,senderAddress
and thetimestamp
fields.
void TraCIDemo11p::handlePositionUpdate(cObject* obj)
{
...
wsm->setTimestamp(simTime());
wsm->setSenderAddress(myId);
wsm->setTTL(TTL);
...
}
In the proposed solution the code of TraCIDemo11p.c
has been polished, removing the code related to beacons
and channelSwitching
modes.
5. Set up a Parameter Study
Let’s define 2 configurations:
- One that we call “default” where we avoid duplicates
- A second where instead duplicates are allowed (called “allowDuplicates”)
Beyond this, let’s have an iteration variable called “ttl” that loops over the values {1,2,..5}
omnetpp.ini
repeat = 1
[Config Default]
*.node[*].appl.TTL = ${ttl = 1..5 step 1}
*.node[*].appl.avoidDuplicates = true
output-vector-file = ${resultdir}/${configname}_${ttl}_${repetition}.vec
output-scalar-file = ${resultdir}/${configname}_${ttl}_${repetition}.sca
[Config allowDuplicates]
extends = Default
*.node[*].appl.avoidDuplicates = false
output-vector-file = ${resultdir}/${configname}_${ttl}_${repetition}.vec
output-scalar-file = ${resultdir}/${configname}_${ttl}_${repetition}.sca
[Config DefaultNoGui]
extends = Default
*.manager.ignoreGuiCommands = true
[Config allowDuplicatesNoGui]
extends = allowDuplicates
*.manager.ignoreGuiCommands = true
- it is a good practice to give a conventional name to
*.vec
and*.sca
generated by a simulation. In our example we prefix all filenames with the configuration name and then, separated by_
, we concatenate all the iteration variable values we want to track (${resultdir}/${configname}_${ttl}_${repetition}
). - we defined also
NoGui
configurations where we disable the support for GUIs, these are configurations suitable for batch executions in parallel.
Now take a look to the veins scripts, which are a collection of tools that help a researcher in defining simulation batches to be run in parallel thanks to runmaker. We take advantage of these tools to:
- Define a list of all the simulations we want to run;
- Run this list of simulations in parallel.
To accomplish our first goal we rely on generateRunsFile.pl
~/src/veins/examples/veins$ generateRunsFile.pl | grep NoGui
. ./run -u Cmdenv -c DefaultNoGui -r 4
. ./run -u Cmdenv -c allowDuplicatesNoGui -r 3
. ./run -u Cmdenv -c DefaultNoGui -r 1
. ./run -u Cmdenv -c allowDuplicatesNoGui -r 2
. ./run -u Cmdenv -c allowDuplicatesNoGui -r 1
. ./run -u Cmdenv -c DefaultNoGui -r 2
. ./run -u Cmdenv -c allowDuplicatesNoGui -r 4
. ./run -u Cmdenv -c DefaultNoGui -r 3
. ./run -u Cmdenv -c allowDuplicatesNoGui -r 0
. ./run -u Cmdenv -c DefaultNoGui -r 0
The script is able to list all possible runs for all configurations listed in the omnetpp.ini
file, and with grep
we filter and keep only those that contain the NoGui
keyword. The output of generateRunsFile.pl
is formatted to be compliant with runmaker.py, so let’s save the output of generateRunsFile.pl
in text file that we call runs.txt
generateRunsFile.pl | grep NoGui > runs.txt
We now just need to let runmaker do the job it is designed for, i.e., spawn in parallel a process for each simulation listed in some input file (runs.txt
in the present case). Before doing so, don’t forget to run the veins_launchd daemon.
veins_launchd -vv -c sumo -d
runmaker4.py -j $(nproc) runs.txt
Once the simulations will be completed, check if there are new files in your results folder:
~/src/veins/examples/veins$ ls results/
allowDuplicatesNoGui_1_0.sca allowDuplicatesNoGui_3_0.vec DefaultNoGui_1_0.vci DefaultNoGui_4_0.sca
allowDuplicatesNoGui_1_0.vci allowDuplicatesNoGui_4_0.sca DefaultNoGui_1_0.vec DefaultNoGui_4_0.vci
allowDuplicatesNoGui_1_0.vec allowDuplicatesNoGui_4_0.vci DefaultNoGui_2_0.sca DefaultNoGui_4_0.vec
allowDuplicatesNoGui_2_0.sca allowDuplicatesNoGui_4_0.vec DefaultNoGui_2_0.vci DefaultNoGui_5_0.sca
allowDuplicatesNoGui_2_0.vci allowDuplicatesNoGui_5_0.sca DefaultNoGui_2_0.vec DefaultNoGui_5_0.vci
allowDuplicatesNoGui_2_0.vec allowDuplicatesNoGui_5_0.vci DefaultNoGui_3_0.sca DefaultNoGui_5_0.vec
allowDuplicatesNoGui_3_0.sca allowDuplicatesNoGui_5_0.vec DefaultNoGui_3_0.vci
allowDuplicatesNoGui_3_0.vci DefaultNoGui_1_0.sca DefaultNoGui_3_0.vec
6. Set up a custom Analysis
Our goal is now to analyse our result files, and draw some plots to show how much traffic has been generated during the various simulations. We expect this analysis to highlight that, increasing the TTL, more messages are propagated/sent by cars moreover, avoiding duplicates, we should instead observe less traffic.
The first thing to do is actually to figure out what our *.sca
and *.vec
contain. You can explore them inside the Omnet editor or just give a quick look to some of these files:
cat results/allowDuplicatesNoGui_1_0.sca | grep generated
scalar demoVNCDScenario.node[0].appl generatedWSMs 40
scalar demoVNCDScenario.node[0].appl generatedBSMs 0
scalar demoVNCDScenario.node[0].appl generatedWSAs 0
scalar demoVNCDScenario.node[1].appl generatedWSMs 40
scalar demoVNCDScenario.node[1].appl generatedBSMs 0
scalar demoVNCDScenario.node[1].appl generatedWSAs 0
scalar demoVNCDScenario.node[2].appl generatedWSMs 8
scalar demoVNCDScenario.node[2].appl generatedBSMs 0
scalar demoVNCDScenario.node[2].appl generatedWSAs 0
scalar demoVNCDScenario.node[3].appl generatedWSMs 1
scalar demoVNCDScenario.node[3].appl generatedBSMs 0
scalar demoVNCDScenario.node[3].appl generatedWSAs 0
scalar demoVNCDScenario.node[4].appl generatedWSMs 1
scalar demoVNCDScenario.node[4].appl generatedBSMs 0
scalar demoVNCDScenario.node[4].appl generatedWSAs 0
Seems like the *.sca
files contain the count of how many WSM, BSM and WSA messages have been generated by each car, bingo! If you are interested in understanding how this stats have been recorded, then check DemoBaseApplLayer, which is the parent class of the application run by our cars (TraCIDemo11p
).
Unfortunately, the format of scalar and vector files is a bit cumbersome, and cannot be read directly by Matlab, Rstudio, Python or whatever tool one may want to use to draw some statistical plot. Once again, the veins_scripts
come to our rescue. In particular, there are some evaluation scripts that can convert scalar and vector file to csv format, and some others that can even better extract a subset of stats from the Omnet result files and print a summary of them over further csv files. Check out all of the evaluation scripts even if for the present exercise we limit ourselves to just use the opp_sca2csv.pl script to extract the counter of generatedWSAs from each *.sca
file we find.
opp_sca2csv.pl -F generatedWSMs results/allowDuplicatesNoGui_1_0.sca
node generatedWSMs
demoVNCDScenario.node[0].appl 40
demoVNCDScenario.node[1].appl 40
demoVNCDScenario.node[2].appl 8
demoVNCDScenario.node[3].appl 1
demoVNCDScenario.node[4].appl 1
Let’s use a bash script to apply the opp_sca2csv.pl
to each *.sca
file and save the result in a related .csv
file. Create a new directory called analysis
and, inside it, create a bash script entitled extract_data.sh
made like this:
analysis/extract_data.sh
#!/bin/bash
RESULT_FOLDER="../results"
for sca in $(ls $RESULT_FOLDER/*sca)
do
out=${sca/sca/csv}
opp_sca2csv.pl -F generatedWSMs $sca > $out
done
Run the script and you should see a bunch of new .csv
files available and ready to be easily processed and plotted with… Python!
~/src/veins/examples/veins$ (cd analysis && bash extract_data.sh)
~/src/veins/examples/veins$ ls results/*.csv
results/allowDuplicatesNoGui_1_0.csv results/allowDuplicatesNoGui_5_0.csv results/DefaultNoGui_4_0.csv
results/allowDuplicatesNoGui_2_0.csv results/DefaultNoGui_1_0.csv results/DefaultNoGui_5_0.csv
results/allowDuplicatesNoGui_3_0.csv results/DefaultNoGui_2_0.csv
results/allowDuplicatesNoGui_4_0.csv results/DefaultNoGui_3_0.csv
Analysis with Python
Now you are free to process/plot the csv files the way you want. On the Omnet website you can find an advanced tutorial about Result Analysis with Python
For the present case, just add this python script to your analysis
folder and run it:
analysis/compareNumMessages.py
import re
import pandas as pd
from glob import glob
import matplotlib.pyplot as plt
import seaborn as sns
regex = re.compile("demoVNCDScenario.node\[([\d+])\].appl")
def renameModule(s):
nc = regex.search(s).groups()[0]
return int(nc)
csvs = glob("../results/*NoGui_*.csv")
dfs = []
for csv in csvs:
fname = csv.lstrip("../results/")
df = pd.read_csv(csv, sep='\t')
ttl = int(re.search("NoGui_([\d+])", fname).groups()[0])
avoidDup = True if fname.startswith("Default") else False
df['node'] = df['node'].apply(lambda x: renameModule(x))
df['ttl'] = ttl
df['dup'] = not avoidDup
dfs.append(df)
df = pd.concat(dfs)
sns.set_style("whitegrid")
sns.catplot(x='node', hue='ttl', y='generatedWSMs',
data=df, kind='bar', col='dup')
plt.savefig("comparisonNumMSGS.pdf", format='pdf')
~/src/veins/examples/veins$ cd analysis
~/src/veins/examples/veins/analysis$ python3 compareNumMessages.py
~/src/veins/examples/veins/analysis$ evince comparisonNumMSGS.pdf &
The plot seems to confirm our expectation:
- when we suppress duplicates (left plot), in general cars generate less messages;
- when duplicates are not suppressed (right plot), then a larger TTL implies that car keep re-propagating messages more times, so bars become higher when growing from TTL=1 up to TTL=5
7. Conclusion
Congratulations! You have just finished to define and run a VEINS-supported V2V simulation study, completing also a basic analysis of the Vehicular Communication traffic!
Tutorial written by Lorenzo Ghiro