Search code examples
unetstack

How to handle collision for response of a broadcast request?


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"

}



 

Solution

  • 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.