Search code examples
groovyunetstack

Exception in simulator agent when implementing localization algorithm


I have implemented a localization algorithm with 4 nodes topology and it is working fine but in the log file I am getting this error and I am not able to understand where the problem is. The algorithm gets stuck for sometime and this error appears and after that it again resumes the normal flow.how to remove this error ?

Exception in agent: simulator
java.lang.NullPointerException: Cannot get property 'location' on null object
Stack trace: ...
org.arl.unet.sim.SimulationAgent$_doMotion_closure1.doCall(initrc.groovy:236)
jdk.internal.reflect.GeneratedMethodAccessor103.invoke(Unknown Source)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ...
org.arl.unet.sim.SimulationAgent.doMotion(initrc.groovy:235)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ...
org.arl.unet.sim.SimulationAgent.this$dist$invoke$2(initrc.groovy)
org.arl.unet.sim.SimulationAgent$3.methodMissing(initrc.groovy)
jdk.internal.reflect.GeneratedMethodAccessor201.invoke(Unknown Source)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ...
org.arl.unet.sim.SimulationAgent$3.onExpiry(initrc.groovy:227)
org.arl.fjage.BackoffBehavior.action(BackoffBehavior.java:90)
org.arl.fjage.Agent.executeBehavior(Agent.java:774)
org.arl.fjage.Agent.run(Agent.java:811) ...

Following are my scripts :

simulation.groovy


import org.arl.fjage.*

println '''
3-node network
--------------
'''
platform = RealTimePlatform

simulate {
  
  def m=node 'B', address:107,  location: [-600.m,  -22.m, -15.m], web:8081,api: 1101, shell: 
  true, mobility:true,stack: "$home/etc/setup2"
  m.motionModel=[ [duration:1.minutes, speed:0.mps, heading:100.deg], 
                  [time:1.minutes, duration:3.minutes, speed:1.mps, heading:100.deg] 
                ]
  
  node 'n1', address: 101, location: [900.m, 900.m, -15.m], web:8082,api: 1102, shell: 5101, 
  stack: "$home/etc/setup3"
 
  node 'n2', address: 102, location: [-900.m, 900.m, -15.m], web:8083,api: 1103, shell: 5102, 
  stack: "$home/etc/setup3"

  node 'n3', address: 103, location: [ -900.m, -800.m, -15.m], web:8084,api: 1104, shell: 5103, 
  stack: "$home/etc/setup3"


}

anchor_agent.groovy


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;
    def nme;
    def phy;
    def nodeInfo;
    
  private final static PDU format = PDU.withFormat
    {
        uint16('source');
    }
  
  void startup() 
  {
    def phy = agentForService Services.PHYSICAL;
    subscribe topic(phy);
    
    
    nodeInfo = agentForService Services.NODE_INFO;
    
  }
  
  void processMessage(Message msg) 
  {
      
    phy = agentForService Services.PHYSICAL;
    subscribe topic(phy);
      
    addr = nodeInfo.address;
    nme = nodeInfo.nodeName;
      
    def datapacket = format.encode(source: addr);
    
    if(msg instanceof DatagramNtf && msg.protocol==Protocol.MAC)
    { 
       def n=rndint(4);
       def k=rndint(5);
      // def l=rndint(4);
       
       def delay=(n+1)*(k+1);
       
       println "Broadcast packet received at node "+nme;
       println "Node "+nme+" will respond after "+ delay +" seconds"
       println ' '
       
       
       
       add new WakerBehavior(delay*1000,{
       phy << new TxFrameReq(to: msg.from, 
                             protocol: Protocol.MAC, 
                             data: datapacket)  
       })
           
     
    }
  }
}

node_agent.groovy


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
import groovy.time.TimeCategory 
import groovy.time.TimeDuration

class node_agent extends UnetAgent {
    float neighbor_addr;
    float distance;
    def ranging;
    def nodeInfo;
    
    def start;
    def stop;

    def nos=0;

    def xlist = [];
    def ylist = [];
    def zlist = [];

    def dlist = [];
    def adlist = [];
    
  private final static PDU format = PDU.withFormat
  {
    uint16('source')
  }

  void startup() 
  {
    def phy = agentForService Services.PHYSICAL;
    subscribe topic(phy);
    
    ranging = agentForService Services.RANGING;
    subscribe topic(ranging);
    
    nodeInfo = agentForService Services.NODE_INFO;
   

    println 'Starting discovery...'
    
    start = currentTimeMillis() ;

    
    xlist.clear();
    ylist.clear();
    zlist.clear();

    dlist.clear();
    adlist.clear();
    
    
    phy << new DatagramReq(to: 0, protocol:Protocol.MAC);
    
        add new TickerBehavior(120000, {
           
            nos=0;
            
                xlist.clear();
                ylist.clear();
                zlist.clear();
            
                dlist.clear();
                adlist.clear();
            
            
          println 'Starting discovery...'
          
           start = currentTimeMillis() ;
           
          phy << new DatagramReq(to: 0, protocol:Protocol.MAC);
    
        })
  
}
    
  void processMessage(Message msg) 
  {
      
      
    if(msg instanceof RxFrameNtf && msg.protocol==Protocol.MAC && nos<3)
    {
        nos++;
        
       def rx = format.decode(msg.data);
       neighbor_addr=rx.source;
       
       println "Found one anchor with address "+neighbor_addr;
       
       adlist.add(neighbor_addr)
       println adlist;
       if(adlist.size()==3)     //As soon as I get the address of 3 neighbour
       {                        //nodes query the range and coordinates information.
           
           println "Found 3 anchor nodes, getting locations....";
           println ' '
           
           def i=0;
           float n1=adlist[0];
           float n2=adlist[1];
           float n3=adlist[2];
           
            
              //waker behavior to avoid collision in RangeNtf
              
                  
                  add new WakerBehavior(0,{
                  ranging<< new RangeReq(to: n1,requestLocation: true)})
                  
                  add new WakerBehavior(7000,{
                  ranging<< new RangeReq(to: n2,requestLocation: true)})
                  
                  add new WakerBehavior(14000,{
                  ranging<< new RangeReq(to: n3,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;
       
       if(dlist.size()==3)
       {
            println ' '
            println 'x-coordinates'+ xlist;
            println 'y-coordinates'+ ylist;
            println 'z-coordinates'+ zlist;
            println 'ranges'+ dlist;
          
         
    BigDecimal X, Y, Va, Vb, Xa, Xb, Xc, Ya, Yb, Yc, Da, Db, Dc, tmp1, tmp2, tmp3, tmp4, tmp5;

    Xa = xlist[0];          // Initializing the parameters
    Ya = ylist[0];
    Xb = xlist[1]; 
    Yb = ylist[1];
    Xc = xlist[2];  
    Yc = ylist[2];
    Da = dlist[0];   
    Db = dlist[1];
    Dc = dlist[2];
    
    tmp1=(Math.pow(Xb, 2) - Math.pow(Xa, 2))    
    tmp2=(Math.pow(Yb, 2) - Math.pow(Ya, 2))
    tmp3=(Math.pow(Db, 2) - Math.pow(Da, 2))    // tmp1, tmp2, tmp3 are intermediate variables to avoid overflow

    Va = ( tmp1 + tmp2 - tmp3) / 2;
    
    tmp1=(Math.pow(Xb, 2) - Math.pow(Xc, 2))
    tmp2=(Math.pow(Yb, 2) - Math.pow(Yc, 2))
    tmp3=(Math.pow(Db, 2) - Math.pow(Dc, 2))    // tmp1, tmp2, tmp3 are intermediate variables to avoid overflow
    
    Vb = (tmp1 + tmp2 - tmp3) / 2;
    
    tmp4=(Va * (Yb - Yc) - Vb * (Yb - Ya))
    tmp5=((Yb - Yc) * (Xb - Xa) - (Yb - Ya) * (Xb - Xc))    // tmp4, tmp5 are intermediate variables to avoid overflow

    X = tmp4 / tmp5;
    
    tmp4=(Va * (Xb - Xc) - Vb * (Xb - Xa))
    tmp5=((Yb - Ya) * (Xb - Xc) - (Xb - Xa) * (Yb - Yc))    // tmp4, tmp5 are intermediate variables to avoid overflow
    
    Y = tmp4 / tmp5

    X = Math.round(X * 100000) / 100000         
    Y = Math.round(Y * 100000) / 100000     // precision of upto 5 digits after decimal
    
    println ' '
    println 'THE ACTUAL COORDINATES AT THIS INSTANCE ARE '+ nodeInfo.location;

    
    stop = currentTimeMillis() ;

    int td =  stop - start;
    td=td/1000;
    
    def e1=td*0.98480775301;
    def e2=td*(-0.17364817766);
    
    ///////////////////////////////////////////////////////////
    
    def p=Math.pow((X-(nodeInfo.location[0])),2);
    def q=Math.pow((Y-(nodeInfo.location[1])),2);
    def r=Math.sqrt(p+q);
    
    r=Math.round(r * 100000) / 100000;
    
    //////////////////////////////////////////////////////////
    
    BigDecimal X1,Y1;
    Y1=Y+e2;
    X1=X+e1;
        
    def j=Math.pow((X1-(nodeInfo.location[0])),2);
    def k=Math.pow((Y1-(nodeInfo.location[1])),2);
    def l=Math.sqrt(p+q);
        
    l=Math.round(l * 100000) / 100000;
    
    //////////////////////////////////////////////////////////
    
    if(r < l)
    {   
        println "The coordinates of the Blind node are "
        println "("+ X + " , " +  Y + " , " + zlist[0] + ")" ;
        
        println "Distance between actual coordinates and estimated coordinates: r ";
        println ' '+r;
    }
    else{
            println "The coordinates of the Blind node are "
            println "("+ X1 + " , " +  Y1 + " , " + zlist[0] + ")" ;
            
            println "Distance between actual coordinates and estimated coordinates: l ";
            println ' '+l;
    }
    
    println "Total time taken "+td+' seconds';
    
    xlist.clear();
    ylist.clear();
    zlist.clear();

    dlist.clear();
    adlist.clear();
    

       }
    }
  }
}

setup2.groovy

import org.arl.fjage.Agent

boolean loadAgentByClass(String name, String clazz) {
  try {
    container.add name, Class.forName(clazz).newInstance()
    return true
  } catch (Exception ex) {
    return false
  }
}

boolean loadAgentByClass(String name, String... clazzes) {
  for (String clazz: clazzes) {
    if (loadAgentByClass(name, clazz)) return true
  }
  return false
}

loadAgentByClass 'arp',          'org.arl.unet.addr.AddressResolution'
loadAgentByClass 'ranging',      'org.arl.unet.localization.Ranging'
loadAgentByClass 'mac',          'org.arl.unet.mac.CSMA'
loadAgentByClass 'uwlink',       'org.arl.unet.link.ECLink', 'org.arl.unet.link.ReliableLink'
loadAgentByClass 'transport',    'org.arl.unet.transport.SWTransport'
loadAgentByClass 'router',       'org.arl.unet.net.Router'
loadAgentByClass 'rdp',          'org.arl.unet.net.RouteDiscoveryProtocol'
loadAgentByClass 'statemanager', 'org.arl.unet.state.StateManager'
//loadAgentByClass 'pri',          'org.arl.unet.UnetAgent.node_agent'

container.add 'remote', new org.arl.unet.remote.RemoteControl(cwd: new File(home, 'scripts'), enable: false)
container.add 'bbmon',  new org.arl.unet.bb.BasebandSignalMonitor(new File(home, 'logs/signals-0.txt').path, 64)
container.add 'myagent' , new node_agent() 

setup3.groovy

import org.arl.fjage.Agent

boolean loadAgentByClass(String name, String clazz) {
  try {
    container.add name, Class.forName(clazz).newInstance()
    return true
  } catch (Exception ex) {
    return false
  }
}

boolean loadAgentByClass(String name, String... clazzes) {
  for (String clazz: clazzes) {
    if (loadAgentByClass(name, clazz)) return true
  }
  return false
}

loadAgentByClass 'arp',          'org.arl.unet.addr.AddressResolution'
loadAgentByClass 'ranging',      'org.arl.unet.localization.Ranging'
loadAgentByClass 'mac',          'org.arl.unet.mac.CSMA'
loadAgentByClass 'uwlink',       'org.arl.unet.link.ECLink', 'org.arl.unet.link.ReliableLink'
loadAgentByClass 'transport',    'org.arl.unet.transport.SWTransport'
loadAgentByClass 'router',       'org.arl.unet.net.Router'
loadAgentByClass 'rdp',          'org.arl.unet.net.RouteDiscoveryProtocol'
loadAgentByClass 'statemanager', 'org.arl.unet.state.StateManager'

container.add 'remote', new org.arl.unet.remote.RemoteControl(cwd: new File(home, 'scripts'), enable: false)
container.add 'bbmon',  new org.arl.unet.bb.BasebandSignalMonitor(new File(home, 'logs/signals-0.txt').path, 64)
container.add 'resp_agent' , new anchor_agent() 

Solution

  • I ran your simulation using the code you provided and managed to reproduce the error. Tracing through the logs, I found that the error occurred on the third motion update for node B, whereas your simulation script only seemed to have 2 legs in the motion model. That gave me a hint as to what the problem was.

    Your motion model states:

    [
      [duration:1.minutes, speed:0.mps, heading:100.deg], 
      [time:1.minutes, duration:3.minutes, speed:1.mps, heading:100.deg]
    ]
    

    This has 2 problems:

    1. The first leg has a duration of 1 minute. The second leg starts at 1 minute. While this is consistent, it is duplicate information and has a potential to create inconsistency. Better to specify one or the other, but not both. In your case, this isn't the reason for the exception.
    2. The second leg has a duration of 3 minutes. What happens after that is unspecified. So the simulator tries to do a third motion update, doesn't find a specification for it, and throws an exception.

    I updated the motion model to:

    [
      [duration:1.minutes, speed:0.mps, heading:100.deg], 
      [speed:1.mps, heading:100.deg]
    ]
    

    and the exception goes away. The second leg continues forever. Alternatively, if you wanted the node to stop moving after 3 minutes, you could do:

    [
      [duration:1.minutes, speed:0.mps, heading:100.deg], 
      [duration:3.minutes, speed:1.mps, heading:100.deg],
      [speed:0.mps]
    ]
    

    and that should work too.

    P.S. The exception thrown by UnetSim for this case was not very informative and so I'll file an issue to improve error checking/messages for motion models.