I am trying to get coordinates from the neighbouring nodes using a broadcast request and store them into lists. I am able to get only one of the responses and I think this happened because of collision between the responses as the nodes have responded at the same time. I have tried to use the Backoff Behavior with random backoff on the agent of neighbouring node but it didn't solved the problem. How can I sync the mechanism to avoid the collision ? I have written an agent for each blind node (which will make the broadcast request) and the neighbouring nodes.
Following are the code snippets.
Agent on blind node:
import org.arl.fjage.*
import org.arl.fjage.Message
import org.arl.fjage.RealTimePlatform
import org.arl.unet.phy.*
import org.arl.unet.mac.*
import org.arl.unet.*
import org.arl.unet.PDU
import org.arl.unet.net.Router
import org.arl.unet.nodeinfo.NodeInfo
import org.arl.unet.localization.*
import org.arl.unet.localization.RangeNtf.*
import org.arl.unet.localization.Ranging.*
import org.arl.unet.localization.RangeReq
import org.arl.unet.net.RouteDiscoveryReq
class node_agent extends UnetAgent {
float neighbor_addr;
float distance;
def xlist = [];
def ylist = [];
def zlist = [];
def dlist = [];
private final static PDU format = PDU.withFormat
{
uint16('source')
}
void startup()
{
def phy = agentForService Services.PHYSICAL;
subscribe topic(phy);
println 'Starting discovery...'
phy << new DatagramReq(to: 0, protocol:Protocol.MAC);
}
void processMessage(Message msg)
{
def ranging = agentForService Services.RANGING;
subscribe topic(ranging);
if(msg instanceof RxFrameNtf && msg.protocol==Protocol.MAC)
{
def rx = format.decode(msg.data);
neighbor_addr=rx.source;
println "Found one neighbour with address "+neighbor_addr;
ranging<< new RangeReq(to: neighbor_addr,requestLocation: true);
}
else if (msg instanceof RangeNtf )
{
distance = msg.getRange();
def locat=new double[3];
locat = msg.getPeerLocation();
double x,y,z;
x=locat[0];
y=locat[1];
z=locat[2];
xlist.add(x);
ylist.add(y);
zlist.add(z);
dlist.add(distance);
println " The coordinates of "+msg.to + " are " +locat+ " and the distance is "+distance;
println xlist;
println ylist;
println zlist;
println dlist;
}
}
}
Agent on neighbouring nodes:
import org.arl.fjage.*
import org.arl.fjage.Message
import org.arl.fjage.RealTimePlatform
import org.arl.unet.phy.*
import org.arl.unet.mac.*
import org.arl.unet.*
import org.arl.unet.PDU
import org.arl.unet.net.Router
import org.arl.unet.nodeinfo.NodeInfo
import org.arl.unet.localization.*
import org.arl.unet.localization.RangeNtf.*
import org.arl.unet.localization.Ranging.*
import org.arl.unet.localization.RangeReq
import org.arl.unet.net.RouteDiscoveryReq
class anchor_agent extends UnetAgent {
def addr;
private final static PDU format = PDU.withFormat
{
uint16('source')
}
void startup()
{
def phy = agentForService Services.PHYSICAL;
subscribe topic(phy);
}
void processMessage(Message msg)
{
def phy = agentForService Services.PHYSICAL;
subscribe topic(phy);
def nodeInfo = agentForService Services.NODE_INFO;
addr = nodeInfo.address;
def datapacket = format.encode(source: addr);
if(msg instanceof DatagramNtf && msg.protocol==Protocol.MAC)
{
def n=rndint(3);
add new BackoffBehavior(n*1000,{
phy << new TxFrameReq(to: msg.from,
protocol: Protocol.MAC,
data: datapacket)})
backoff(n*1000)})
}
}
}
Simulation Script :
import org.arl.fjage.*
println '''
3-node network
--------------
'''
platform = RealTimePlatform
simulate {
node 'B', address: 101, location: [ 0.km, 0.km, -15.m], web:8081,api: 1101, shell: true, stack: "$home/etc/setup2"
node '1', address: 102, location: [ 0.km, 1.km, -15.m], web:8082,api: 1102, shell: 5101, stack: "$home/etc/setup3"
node '2', address: 103, location: [-900.m, 0.km, -15.m], web:8083,api: 1103, shell: 5102, stack: "$home/etc/setup3"
node '3', address: 104, location: [ 600.m, 0.km, -15.m], web:8084,api: 1104, shell: 5103, stack: "$home/etc/setup3"
}
I've simplified your agents and made a working example. The anchor discovery is broken into 2 phases: (1) to find what anchors are around, and (2) get their locations. The reason to break it up is that you want the initial discovery to require minimum channel access to reduce chance of collisions. The later location request can be better controlled once you know what nodes are around.
I have combined the agents into a single simulation script, so you can simply copy the code in a file and run it:
import org.arl.fjage.*
import org.arl.unet.*
import org.arl.unet.localization.*
platform = RealTimePlatform
class NodeAgent extends UnetAgent {
AgentID phy
AgentID ranging
List<Integer> anchors = []
def anchorLocations = [:]
void startup() {
phy = agentForService Services.PHYSICAL
ranging = agentForService Services.RANGING
subscribe topic(phy)
log.info 'Starting anchor discovery...'
phy << new DatagramReq(to: 0, protocol: Protocol.USER)
// wait for 15 seconds for all nodes to respond before trying to get their locations
add new WakerBehavior(15000, this.&getLocations)
}
void getLocations() {
log.info "Discovered ${anchors.size()} anchor(s), getting locations..."
anchors.eachWithIndex { addr, i ->
// request location for each node 5 seconds apart
add new WakerBehavior(5000*i, {
log.info "Asking node ${addr} for its location..."
ranging << new RangeReq(to: addr, requestLocation: true)
})
}
}
void processMessage(Message msg) {
if (msg instanceof DatagramNtf && msg.protocol == Protocol.USER) {
log.info "Discovered node ${msg.from}"
anchors.add(msg.from)
}
if (msg instanceof RangeNtf) {
log.info "Got location of anchor ${msg.to}: ${msg.peerLocation}"
anchorLocations[msg.to] = msg.peerLocation
}
}
}
class AnchorAgent extends UnetAgent {
AgentID phy
AgentLocalRandom rnd
void startup() {
rnd = AgentLocalRandom.current()
phy = agentForService Services.PHYSICAL
subscribe topic(phy)
}
void processMessage(Message msg) {
if (msg instanceof DatagramNtf && msg.protocol == Protocol.USER) {
// respond back to a discovery request with a random backoff of up to 13 seconds
long backoff = rnd.nextDouble(0, 13000)
log.info "Discovery request from ${msg.from}, will respond after ${backoff} ms"
add new WakerBehavior(backoff, {
log.info 'Responding...'
phy << new DatagramReq(to: msg.from, protocol: Protocol.USER)
})
}
}
}
setup2 = { c ->
c.add 'ranging', new Ranging()
c.add 'agent2', new NodeAgent()
}
setup3 = { c ->
c.add 'ranging', new Ranging()
c.add 'agent3', new AnchorAgent()
}
simulate {
node 'B', address: 101, location: [ 0.km, 0.km, -15.m], web:8081,api: 1101, shell: true, stack: setup2
node '1', address: 102, location: [ 0.km, 1.km, -15.m], web:8082,api: 1102, shell: 5101, stack: setup3
node '2', address: 103, location: [-900.m, 0.km, -15.m], web:8083,api: 1103, shell: 5102, stack: setup3
node '3', address: 104, location: [ 600.m, 0.km, -15.m], web:8084,api: 1104, shell: 5103, stack: setup3
}
Here are the logs from a sample run:
1613226179777|INFO|NodeAgent/B@83:call|Starting anchor discovery...
1613226180943|INFO|AnchorAgent/3@112:doInvoke|Discovery request from 101, will respond after 6704 ms
1613226181134|INFO|AnchorAgent/2@103:doInvoke|Discovery request from 101, will respond after 7628 ms
1613226181193|INFO|AnchorAgent/1@94:doInvoke|Discovery request from 101, will respond after 3747 ms
1613226184945|INFO|AnchorAgent/1@94:call|Responding...
1613226186356|INFO|NodeAgent/B@83:doInvoke|Discovered node 102
1613226187654|INFO|AnchorAgent/3@112:call|Responding...
1613226188764|INFO|AnchorAgent/2@103:call|Responding...
1613226188805|INFO|NodeAgent/B@83:doInvoke|Discovered node 104
1613226190731|INFO|NodeAgent/B@83:doInvoke|Discovered node 103
1613226194960|INFO|NodeAgent/B@83:doInvoke|Discovered 3 anchor(s), getting locations...
1613226194963|INFO|NodeAgent/B@83:doInvoke|Asking node 102 for its location...
1613226199126|INFO|NodeAgent/B@83:doInvoke|Got location of anchor 102: [0.0, 1000.0, -15.0]
1613226199965|INFO|NodeAgent/B@83:doInvoke|Asking node 104 for its location...
1613226203782|INFO|NodeAgent/B@83:doInvoke|Got location of anchor 104: [600.0, 0.0, -15.0]
1613226204969|INFO|NodeAgent/B@83:doInvoke|Asking node 103 for its location...
1613226209069|INFO|NodeAgent/B@83:doInvoke|Got location of anchor 103: [-900.0, 0.0, -15.0]
showing that it works!
There is still a probability for collision, as always in a random access scenario, but it'll be small the longer you make the initial discovery period (currently 15 seconds). You could further make the system more robust by adding retries if you receive a BadFrameNtf
or a CollisionNtf
, indicating that some node tried to communicate but failed.