or how to stream turtles
Simtools Workshop
Paris, June 5th 2013
Random Waypoint is simple mobility model
Applications to MANETs
Open netlogo/RWP_Initial.nlogo
and take a look at it.
How does the connectivity of this graph evolve?
Create a sub-folder gs
in extensions
folder of your NetLogo installation and put inside netlogo/gs.jar
and gs-core.jar
To load the extension, put this at the beginning of your NetLogo code
extensions [gs]
to setup-sender
;; remove all existing senders
gs:clear-senders
;; create a sender connected locally to port 2000
gs:add-sender "snd" "localhost" 2000
;; send graph cleared event
gs:clear "snd"
;; send graph attribute added events
gs:add-attribute "snd" "ui.antialias" true
gs:add-attribute "snd" "ui.stylesheet"
"node{fill-color:blue;} node.giant{fill-color:red;} edge{fill-color:grey;}"
end
to setup
setup-sender
ca
...
end
Turtles inform the external application that they are created
to setup
...
crt population [
;; each turtle sends node added event when created
gs:add "snd"
setxy random-xcor random-ycor
;; and its initial coordinates
gs:add-attribute "snd" "xy" list xcor ycor
...
]
...
end
... and when they change their coordinates
to move
if patch-here = destination [
choose-destination
]
fd 0.1
gs:add-attribute "snd" "xy" list xcor ycor
end
Links inform the external application when created and removed
to update-links
ask my-links with [link-length > radius][
;; send edge removed event
gs:remove "snd"
die
]
create-links-with other turtles in-radius radius [
;; send edge added event
gs:add "snd"
]
end
This class is in org.graphstream.demo.tutorial3
package in your project.
public class RWPViewer { public static void main(String[] args) { Graph g = new SingleGraph("RWP"); NetStreamReceiver rcv = new NetStreamReceiver("localhost", 2000); ThreadProxyPipe pipe = rcv.getDefaultStream(); pipe.addSink(g); g.display(false); while (true) { pipe.blockingPump(); } } }
public class RWPAnalyzer { private Graph graph; private ConnectedComponents cc; public RWPAnalyzer(Graph graph) { this.graph = graph; cc = new ConnectedComponents(); cc.init(graph); } private void reportResults() { for (Node node : graph) node.removeAttribute("ui.class"); List<Node> giant = cc.getGiantComponent(); for (Node node : giant) node.addAttribute("ui.class", "giant"); System.out.println("Size of giant component " + giant.size()); } }
But how often to report the results ? → at each NetLogo simulation step
Our NetLogo simulation sends step event at each "tick"
to go
ask turtles [
move
update-links
]
gs:step "snd" ticks
tick
end
... and our analyzer processes these events
public class RWPAnalyzer extends SinkAdapter { ... public RWPAnalyzer(Graph graph) { ... graph.addElementSink(this); } @Override public void stepBegins(String sourceId, long timeId, double step) { reportResults(); } }
Don't forget to instantiate an analyzer:
public class RWPViewer {
public static void main(String[] args) {
Graph g = new SingleGraph("RWP");
NetStreamReceiver rcv = new NetStreamReceiver("localhost", 2000);
ThreadProxyPipe pipe = rcv.getDefaultStream();
pipe.addSink(g);
new RWPAnalyzer(g);
g.display(false);
while (true) {
pipe.pump();
Thread.sleep(1);
}
}
}
This is cool, but let's bring the results back to NetLogo!
At GraphStream side we create a sender
public class RWPAnalyzer extends SinkAdapter { ... private NetStreamSender sender; private String mySourceId; private long myTimeId; public RWPAnalyzer(Graph graph) { ... sender = new NetStreamSender("localhost", 3000); mySourceId = toString(); myTimeId = 0; } ... }
... and slightly change the method reporting results
private void reportResults() { for (Node node : graph) node.removeAttribute("ui.class"); List<Node> giant = cc.getGiantComponent(); for (Node node : giant) { node.addAttribute("ui.class", "giant"); sender.nodeAttributeAdded(mySourceId, myTimeId++, node.getId(), "giant", true); } sender.graphAttributeAdded(mySourceId, myTimeId++, "gc-size", giant.size()); }
At NetLogo side we create a receiver
to setup-receiver
gs:clear-receivers
gs:add-receiver "rcv" "localhost" 3000
end
but this time we associate this procedure to a separate button instead of calling it from setup.
In setup we just flush the receiver and initialize a global variable that keeps the size of the giant component
globals [ gc-size ]
...
to setup
...
set gc-size []
gs:flush "rcv"
reset-ticks
end
Agents retrieve their attributes in the following procedure
to get-attributes
set gc-size gs:get-attribute "rcv" "gc-size"
ask turtles [
let tmp gs:get-attribute "rcv" "giant"
ifelse empty? tmp [
set color blue
][
set color red
]
]
end
... which is called at each step
to go
ask turtles [
move
update-links
]
gs:step "snd" ticks
get-attributes
tick
end
We will visualize the giant component size in a plot with the following pen update command:
foreach gc-size [plot ?]
When a sender is created there must be a receiver running on the other side. We have senders and receivers on both sides. So run in the following order:
After sending the attributes, the external application sends a step event
public void stepBegins(String sourceId, long timeId, double step) {
reportResults();
sender.stepBegins(mySourceId, myTimeId++, step);
}
NetLogo simulation waits for this event
to go
...
gs:step "snd" ticks
while [gs:wait-step "rcv" != ticks][]
get-attributes
tick
end
Unlike gs:get-attribute
, gs:wait-step
is blocking
Make another plot for the the connected components count.