Real Live AI traffic: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
Line 10: Line 10:


== Scenario Loading on Demand ==
== Scenario Loading on Demand ==
To get the ai models on demand I'm using a scenario template then editing it and then loading the model. CALLSIGN and PLANEFILE are replaced by the the property name and the model file appropriate. The amended file is saved in a subfolder:  /AI/rttemp. "load-scenario" command needs a unique file name for each plane and only loads from within AI folder. This means that files will pile up in rttemp and should be deleted at the end of the session.
To get the ai models on demand I'm using a scenario template then editing it and then loading the model. CALLSIGN and PLANEFILE are replaced by the the property name and the model-file appropriate. The amended file is saved in a subfolder:  /AI/''rttemp''. "load-scenario" command needs a unique file name for each plane and only loads from within AI folder. ''rttemp'' is a simlink to '''fg-home'''/cache/rttemp.
This process requires a write access addition to /Nasal/IORules
I do this because fg 2018 allows read write access only to fg-home / cache.
 
 
This means that files will pile up in rttemp and should be deleted at the end of the session.
 


<syntaxhighlight lang="nasal">
<syntaxhighlight lang="nasal">
Line 52: Line 56:
I am using Perl but of course any suitable language would do.
I am using Perl but of course any suitable language would do.
Input is the latitude and longitude range  and a list of position altitude and speed etc is returned for each plane (looks similar to JSON format, i.e. Nasal hashes).
Input is the latitude and longitude range  and a list of position altitude and speed etc is returned for each plane (looks similar to JSON format, i.e. Nasal hashes).
 
The capture routine also collects live METAR weather. This can be easily amended for changes in the server.


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
#!/usr/bin/perl -w
#!/usr/bin/perl -w
use HTTP::Tiny;
use HTTP::Tiny;
 
my $infile = "/root/.fgfs/cache/pipein.txt";
my $infile = "/mnt/cache1/pipein.txt";
my $outfile = "/root/.fgfs/cache/testpipe.txt";
my $outfile = "/mnt/cache1/testpipe.txt";
my $types = "/root/.fgfs/cache/types.txt";
my $types = "/mnt/cache1/types.txt";
unlink $outfile;
unlink $outfile;
unlink $infile;
unlink $infile;
Line 75: Line 78:
my @prevInterp;
my @prevInterp;
my $count = 0;
my $count = 0;
 
my $metar ="";
while(!eof(INFILE)){
while(!eof(INFILE)){
    $count = $count + 1;
  $count = $count + 1;
   my $row = INFILE->getline();
   my $row = INFILE->getline();
   #print $row;
   #print $row;
   chomp $row;
   chomp $row;
    
    
  @words = split(/ /,  $row);
  @words = split(/ /,  $row);
  print "list $words[2]\n";
 
  my $requesta = 'http://data-live.flightradar24.com/zones/fcgi/feed.js?bounds='.$words[1].'&faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&estimated=0&maxage=900&gliders=1&stats=1';
   my $response = HTTP::Tiny->new(timeout => 2)->get('http://data-live.flightradar24.com/zones/fcgi/feed.js?bounds='.$words[1].'&faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&estimated=0&maxage=900&gliders=1&stats=1');
   my $response = HTTP::Tiny->new(timeout => 2)->get('http://data-live.flightradar24.com/zones/fcgi/feed.js?bounds='.$words[1].'&faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&estimated=0&maxage=900&gliders=1&stats=1');
 
  print TYPES "$requesta\n";
    
   if ($words[3] =~ ".TXT"){
    
    my $metarresponse =  HTTP::Tiny->new(timeout => 2)->get('https://tgftp.nws.noaa.gov/data/observations/metar/stations/'.$words[3]);
    if ($metarresponse->{status} == 200){
$metar = $metarresponse->{content};
$metar = (split(/\n/,$metar))[1];
    }
   }
   
   
  if ($response->{status} != 200){
  if ($response->{status} != 200){
Line 118: Line 126:
    foreach $a (@matches){
    foreach $a (@matches){
       
       
      print "xxxxxxxxxxxxxxxxxx\n";
       
      print $a;
      @datum = split(/,/,$a);      
      @datum = split(/,/,$a);      
       
       
Line 127: Line 134:
    $planesLast{$datum[9]} = $a;
    $planesLast{$datum[9]} = $a;
}
}
     
print "xxxxx: $words[2] :xxxxx: $datum[9] :\n";
my $k = index($words[2], $datum[9]);
print "k:$k\n";
        if (($words[2] =~ "all")||($k > -1)){
        print "xxxxxxxxxxxxxxxxxx\n";
        print $a;
@datumLast = split(/,/,$planesLast{$datum[9]}); # get previous for this plane
@datumLast = split(/,/,$planesLast{$datum[9]}); # get previous for this plane
# create interpolation
# create interpolation
Line 154: Line 168:
# TYPES->flush;
# TYPES->flush;
$planesLast{$datum[9]} = $a;
$planesLast{$datum[9]} = $a;
}


  }   
  }   
  print OUTFILE "end\n";
  print OUTFILE "end ".$metar. "\n";
  OUTFILE->flush;  
  OUTFILE->flush;  
  print TYPES "end\n";
  print TYPES "end ".$metar. "\n";
  TYPES->flush;   
  TYPES->flush;   
}
}
Line 164: Line 180:
</syntaxhighlight>
</syntaxhighlight>


Line 239: Line 256:
var planes = {};
var planes = {};
var plane = {lat:0,lon:0,heading:0,alt:0,callsign:0,type:0};
var plane = {lat:0,lon:0,heading:0,alt:0,callsign:0,type:0};
var path = "/mnt/cache1/testpipe.txt"; # output from data server
var path = "/root/.fgfs/cache/testpipe.txt"; # output from data server
var pathin = "/mnt/cache1/pipein.txt"; # path input to data server
var path2 = "/root/.fgfs/cache/testpipetest.txt"; # output from data server
var pathin = "/root/.fgfs/cache/pipein.txt"; # path input to data server
var pathin2 = "/root/.fgfs/cache/pipeintest.txt"; # path input to data server
var rtpipe = nil;
var rtpipe = nil;
var rtpipein = nil;
var rtpipein = nil;
var RealPlanes = props.globals.getNode("/RealPlanes",1);  # real planes data
var RealPlanes = props.globals.getNode("/RealPlanes",1);  # real planes data
var RealPlanesAirport =  props.globals.getNode("/RealPlanesAirport",1);  # real planes data
var rttimer = nil;
var rttimer = nil;
var planefiles ={};
var planefiles ={};
Line 259: Line 279:
var updateOK=1;
var updateOK=1;
var lastTime=0;
var lastTime=0;
var activeList = "";
var metarinf="";
var airportname ="EGPF";
var lastairportname ="";
var icaoparam="NO";
#
#
# create hash table from plane hash file
# create hash table from plane hash file
Line 335: Line 360:
       p = RealPlanes.getChild(pname,0,0);
       p = RealPlanes.getChild(pname,0,0);
       if(p!= nil){
       if(p!= nil){
if (p.getNode("pitchcounter").getValue() < 20){
if ((p.getNode("pitchcounter").getValue() < 20) and (p.getNode("pitchcounter").getValue() > 0)){
  p.getNode("pitchcounter").setIntValue(p.getNode("pitchcounter").getValue()+1);
  p.getNode("pitchcounter").setIntValue(p.getNode("pitchcounter").getValue() + p.getNode("pitchcounterInc").getValue());
}
}
p.getNode("pitch").setDoubleValue(p.getNode("pitchcounter").getValue()/2);   
p.getNode("pitch").setDoubleValue(p.getNode("pitchcounter").getValue()/2);   
Line 557: Line 582:
## flare effect
## flare effect
##
##
  if(((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 200 )
  if(((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 100 )
  and (plane.getNode("speed",1).getValue() < 200)  
#   and (plane.getNode("speed",1).getValue() < 200)  
  and (plane.getNode("flare").getValue()==1)
#   and (plane.getNode("flare").getValue()==1)
  and (plane.getNode("speed",1).getValue() > 100)){
  and (plane.getNode("speed",1).getValue() > 70)){
   
  #   and (plane.getNode("speed",1).getValue() < 200)  
#   and (plane.getNode("flare").getValue()==1)
#   and (plane.getNode("speed",1).getValue() > 100)){
   
   
   
   
  plane.getNode("flare").setBoolValue(0);
  plane.getNode("flare").setBoolValue(0);
     # print("zz"~ plane.getNode("current-speed",1).getValue());
     # print("zz"~ plane.getNode("current-speed",1).getValue());
    if(approach.contains(plane.getName()) == 0){
    if(approach.contains(plane.getName()) == 0){
        plane.getNode("pitchcounterInc",1).setIntValue(1);
approach.append(plane.getName());
approach.append(plane.getName());
    }
    }
Line 602: Line 636:
  }
  }
  if((approach.contains(plane.getName()) == 1)
  if((approach.contains(plane.getName()) == 1)
  and (plane.getNode("speed").getValue() <= 90)){
  and ((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 10)
  and (plane.getNode("speed").getValue() <= 70)){
    plane.getNode("flare").setBoolValue(0);
    plane.getNode("flare").setBoolValue(0);
    approach.remove(plane.getName());
    approach.remove(plane.getName());
Line 608: Line 643:
altitudeup.remove(plane.getName());
altitudeup.remove(plane.getName());
    }
    }
    plane.getNode("pitchcounter").setIntValue(0);
    plane.getNode("pitchcounterInc").setIntValue(-1);
    plane.getNode("pitch").setDoubleValue(0);
  # plane.getNode("pitchcounter").setIntValue(0);
  # plane.getNode("pitch").setDoubleValue(0);
    plane.getNode("targetpitch").setDoubleValue(0);
    plane.getNode("targetpitch").setDoubleValue(0);
  }   
  }   
Line 768: Line 804:
    
    
    
    
     print(targetnode.getPath());
     print("ZZZZtargetnode" ~ targetnode.getPath());
     loadparm = targetnode;
     loadparm = targetnode;
     clsgn = loadparm.getParent().getNode("clsgn").getValue();
     clsgn = loadparm.getParent().getNode("clsgn").getValue();
Line 793: Line 829:
#   
#   
   
   
     var rtscen = io.readfile(getprop("sim/fg-root") ~"/AI/rttemplate.xml");
     var rtscen = io.readfile(getprop("sim/fg-home") ~"/cache/rttemplate.xml");
     rtscen = string.replace(rtscen,"CALLSIGN",clsgn);
     rtscen = string.replace(rtscen,"CALLSIGN",clsgn);
     rtscen = string.replace(rtscen,"PLANEFILE",planefile);
     rtscen = string.replace(rtscen,"PLANEFILE",planefile);
     scenfile = io.open(getprop("sim/fg-root") ~"/AI/rttemp/scen"~clsgn~".xml", "w");
     scenfile = io.open(getprop("sim/fg-home") ~"/cache/rttemp/scen"~clsgn~".xml", "w");
      
      
      
      
Line 814: Line 850:
       var args = { 'name': xmlname,'load-property': loadparm.getPath()};
       var args = { 'name': xmlname,'load-property': loadparm.getPath()};
       var currentCount = models.getNode("count").getValue();
       var currentCount = models.getNode("count").getValue();
      print("at fgcommand");
       fgcommand("load-scenario", args);   
       fgcommand("load-scenario", args);   
      print(xmlname~" "~loadparm.getPath());
   }   
   }   
       print("pre-binding A"~clsgn);  
       print("pre-binding A"~clsgn);  
     
   
     
     
     
     
   if (loadparm.getValue() == 1){   
   if (loadparm.getValue() == 1){   
     #   
     #   
Line 906: Line 950:
# open the pipes
# open the pipes
#
#
  if (call(io.readfile, [path2], nil, nil, var err=[]) != nil){
    print("test path exists");
    path = path2;
    pathin = pathin2;
  }
 
 
 
   if (getprop("/RealPlanesActive") == 1){
   if (getprop("/RealPlanesActive") == 1){
     if(rtpipe == nil){
     while(rtpipe == nil){
rtpipe = io.open(path,"r");
     
rtpipein = io.open(pathin,"w");
     
      var stat = io.stat(path);
      if (stat != nil){
printf("File time: %s ", stat[9]); # prints file time
     
#if ((systime() - stat[9] ) < 10){
  printf("opening rtpipe"); # prints file size
  rtpipe = io.open(path,"r");
  rtpipein = io.open(pathin,"w");
  # start the cycle
  rttimer.start();
      }
     }
     }
    # start the cycle
 
rttimer.start();
   }
   }
   else{
   else{
       #rtpipe.close();
       if (path == path2){
# rtpipe.close();
      }
       rttimer.stop();
       rttimer.stop();
      
      
Line 962: Line 1,029:
   #
   #
    
    
    
   printf("rtpipein set position"); # prints file size
   io.write(rtpipein,"read "~ pt.lat~","~ pb.lat~","~ pl.lon~","~pr.lon~"\n");
  if (getprop("/RealPlanesActiveList") == 1){
    if (!(string.match(activeList, "*all,*"))){
activeList = activeList ~ "all,";
    }
  }
  else{
    activeList = string.replace(activeList,"all,","");
  }
  if (activeList == ""){
    activeList = "all,";
  }
  printf("AL>"~activeList~"\n");
  props.globals.getNode("/RealPlanesUpdated",1).setIntValue(0);
  info = airportinfo(getprop("position/latitude-deg"),getprop("position/longitude-deg"));
  if (getprop("/RealPlanesAirport") == nil){
      airportname = info.id;
  }
  else{
      airportname = getprop("/RealPlanesAirport");
  }
  if (airportname != lastairportname){
    icaoparam = airportname~".TXT";
    lastairportname =  airportname;
  }
  else{
    icaoparam = "NO";
  }
   io.write(rtpipein,"read "~ pt.lat~","~ pb.lat~","~ pl.lon~","~pr.lon~" "~activeList~" "~icaoparam~"\n");
   io.flush(rtpipein);
   io.flush(rtpipein);
  #activeList ="";
   #print("start loop");
   #print("start loop");
 
#
  var stat = io.stat(path);
  printf("stat[9] %s systime %s",stat[9],lastTime);
  if (path != path2){
  if (stat[9] == lastTime){
    return;
  }
  }
  lastTime = stat[9];
  setprop("/environment/metar[0]/data",metarinf);
   
   
   
   
   
 
 
   while(1){
   while(1){
     #
     #
     # read a batch of data from flightradar24
     # read a batch of data from flightradar24
     #
     #
  line = io.readln(rtpipe);
    
    
  lastTime = systime();  
    line = io.readln(rtpipe);
     if(line == "end"){
    print(line);
     if(substr(line,0,4) == "http"){
      print(line);
      continue;
    }
   
    if(substr(line,0,3) == "end"){
       updateOK = 1; #1
       updateOK = 1; #1
       print("END");
       print("END");
      var metarinfs = (split("end ",line));
 
metarinf = metarinfs[1];
metarinf = string.replace(metarinf,"/"," ");
print(metarinf);
 
     break;
     break;
     }
     }
Line 984: Line 1,107:
     break;
     break;
     }
     }
 
    if(line == "restart"){
      io.close(rtpipe);
      rtpipe = io.open(path,"r");
      print("RESTART");
   
    break;
    }
#var RealPlanes = props.globals.getNode("/RealPlanes",1);   
#var RealPlanes = props.globals.getNode("/RealPlanes",1);   
Line 991: Line 1,120:
var elevinit = 0;
var elevinit = 0;
var clsgn = bits[8];
var clsgnraw = bits[8];
clsgn = split('"',clsgn);
clsgn = split('"',clsgnraw);
clsgn = split('-',clsgn[1]);
clsgn = split('-',clsgn[1]);
clsgn = string.join("_",clsgn);
clsgn = string.join("_",clsgn);
Line 1,000: Line 1,129:
var elevinit=0;
var elevinit=0;
print("CLSGN"~clsgn);
#print("CLSGN"~clsgn);
if (clsgn != ""){    
if (clsgn != ""){    
 
 
Line 1,010: Line 1,139:
  else{
  else{
      elevinit = 1;
      elevinit = 1;
      elev = (elev *3.28);
      elev = (elev * 3.28);
  }
  }
  plane = RealPlanes.getNode("_"~clsgn,1);
  plane = RealPlanes.getNode("_"~clsgn,1);
Line 1,018: Line 1,147:
  if(getprop("/RealPlanes/_"~clsgn~"/model") == nil){
  if(getprop("/RealPlanes/_"~clsgn~"/model") == nil){
  # initialise new real plane
  # initialise new real plane
    gearheight = 4;
    gearheight = 9;
    type = split('"',bits[7]);
    type = split('"',bits[7]);
    type = type[1];
    type = type[1];
Line 1,057: Line 1,186:
    plane.getNode("icao",1).setValue(icao);
    plane.getNode("icao",1).setValue(icao);
    plane.getNode("pitch",1).setDoubleValue(0);
    plane.getNode("pitch",1).setDoubleValue(0);
    plane.getNode("pitchcounter",1).setIntValue(0);
    plane.getNode("pitchcounter",1).setIntValue(1);
    plane.getNode("pitchcounterInc",1).setIntValue(0);
    plane.getNode("targetpitch",1).setDoubleValue(0);
    plane.getNode("targetpitch",1).setDoubleValue(0);
    plane.getNode("roll",1).setDoubleValue(0);
    plane.getNode("roll",1).setDoubleValue(0);
Line 1,084: Line 1,214:
    plane.getNode("takeoff-landing",1).setIntValue(0);
    plane.getNode("takeoff-landing",1).setIntValue(0);
    plane.getNode("log",1);
    plane.getNode("log",1);
    plane.getNode("airport",1).setValue((airportinfo( plane.getNode("lat").getValue(),plane.getNode("lon").getValue())).id);
   
     
     
  }
  }
Line 1,133: Line 1,265:
    #
    #
    # AI model is active
    # AI model is active
    #
if (!(string.match(activeList, "*"~clsgnraw~"*"))){
print(line);
  activeList = activeList ~ clsgnraw ~ ',';
  print("AAA updated"~activeList);
}
else{
  print("AAA Matched" ~ clsgnraw ~ " in "~activeList);
}


  if((plane.getNode("latold").getValue() == inLat)and(plane.getNode("lonold").getValue() == inLon)){
  if((plane.getNode("latold").getValue() == inLat)and(plane.getNode("lonold").getValue() == inLon)){
Line 1,219: Line 1,356:
  # assume landing if speed < 200 and lower than 2000        
  # assume landing if speed < 200 and lower than 2000        


  if(plane.getNode("runway-end-lat").getValue() == 9999){
if(plane.getNode("runway-end-lat").getValue() == 9999){
      var endbrng =9999;
    var endbrng =9999;
      plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
    plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
      info =  airportinfo(position.lat,position.lon);
    info =  airportinfo(position.lat,position.lon);
      foreach(var rwy; keys(info.runways)){
 
    var endr = {};
    foreach(var rwy; keys(info.runways)){
    endr.lat = info.runways[rwy].lat;
  var endr = {};
    endr.lon = info.runways[rwy].lon;
  endr.lat = info.runways[rwy].lat;
    var startbearing =  getBearing(position,endr);
  endr.lon = info.runways[rwy].lon;
    var heading = plane.getNode("input-heading").getValue();      
  var startbearing =  getBearing(position,endr);
    var startdev = math.abs(startbearing - heading);
  var heading = plane.getNode("input-heading").getValue();      
    if (startdev > 180){        
  var startdev = math.abs(startbearing - heading);
  startdev = math.abs(360 - startdev);
  if (startdev > 180){        
    }
startdev = math.abs(360 - startdev);
    if (startdev > 90){        
  }
  startdev = math.abs(180 - startdev);
  if (startdev > 90){        
    }
startdev = math.abs(180 - startdev);
    if (startdev < 10){      
  }
var dist = getDist(position,endr);
  if (startdev < 10){      
var length =  info.runways[rwy].length ;        
      var dist = getDist(position,endr);
if (dist < leastDist){
      var length =  info.runways[rwy].length ;        
  leastDist = dist;
      if (dist < leastDist){
  rwylength = length;      
leastDist = dist;
  endrwy = getPointAtDistance(endr,info.runways[rwy].heading,length/1852);
rwylength = length;      
  startlat = endr.lat ;
endrwy = getPointAtDistance(endr,info.runways[rwy].heading,length/1852);
  startlon = endr.lon ;
startlat = endr.lat ;
  endbrng = getBearing(endr,endrwy);
startlon = endr.lon ;
}
endbrng = getBearing(endr,endrwy);
    }
      }
}
  }
if (endbrng < 9999){
      }
    plane.getNode("runway-elevation").setDoubleValue(info.elevation * 3.28);
      if (endbrng < 9999){
    plane.getNode("runway-end-lat").setDoubleValue(endrwy.lat);
  plane.getNode("runway-elevation").setDoubleValue(info.elevation * 3.28);
    plane.getNode("runway-end-lon").setDoubleValue(endrwy.lon);
  plane.getNode("runway-end-lat").setDoubleValue(endrwy.lat);
    plane.getNode("runway-start-lat").setDoubleValue(startlat);
  plane.getNode("runway-end-lon").setDoubleValue(endrwy.lon);
    plane.getNode("runway-start-lon").setDoubleValue(startlon);        
  plane.getNode("runway-start-lat").setDoubleValue(startlat);
    plane.getNode("runway-distance",1).setDoubleValue(leastDist);
  plane.getNode("runway-start-lon").setDoubleValue(startlon);        
    plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
  plane.getNode("runway-distance",1).setDoubleValue(leastDist);
    plane.getNode("runway-reference-bearing").setDoubleValue(endbrng);
  plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
}
  plane.getNode("runway-reference-bearing").setDoubleValue(endbrng);
    }
      }
   
  }
    }
    }
     
     
Line 1,274: Line 1,410:
targetHeading = targetHeading - 360;
targetHeading = targetHeading - 360;
      }  
      }  
   
     
      plane.getNode("roll").setDoubleValue((targetHeading - heading));
      plane.getNode("roll").setDoubleValue((targetHeading - heading));
   
   
Line 1,297: Line 1,431:
    if(plane.getNode("takeoff-landing").getValue() == 1){
    if(plane.getNode("takeoff-landing").getValue() == 1){
      if(plane.getNode("alt").getValue() < (70 + plane.getNode("elev").getValue())  ){
      if(plane.getNode("alt").getValue() < (70 + plane.getNode("elev").getValue())  ){
  if( plane.getNode("input-speed").getValue() > 30){
  if( plane.getNode("input-speed").getValue() > 60){
      # check heading valid
      # check heading valid
      if(math.abs(plane.getNode("input-heading").getValue() - plane.getNode("runway-reference-bearing").getValue()) < 10){
      if(math.abs(plane.getNode("input-heading").getValue() - plane.getNode("runway-reference-bearing").getValue()) < 10){
Line 1,340: Line 1,474:
   
   
}
}
       }
        
      
      
     if (getprop("/RealPlanesAutoModel")){
     if (getprop("/RealPlanesAutoModel")){
Line 1,347: Line 1,481:
       }
       }
     }
     }
  }
# props.globals.getNode("/RealPlanesActiveList",1).setIntValue(0);
 
  props.globals.getNode("/RealPlanesUpdated",1).setIntValue(1);
}
}
var manualstart = func{
var manualstart = func{
Line 1,352: Line 1,490:
   altitudeup = std.Vector.new();  
   altitudeup = std.Vector.new();  
   rttimer = maketimer(5,realtraffic);
   rttimer = maketimer(5,realtraffic);
  #datatimer = maketimer(5
   flaretimer = maketimer(1,fixpitch);
   flaretimer = maketimer(1,fixpitch);
   flaretimer.start();
   flaretimer.start();
Line 1,393: Line 1,532:
    
    
}   
}   
</syntaxhighlight>
== Named Pipes ==
The perl server should be started before activating the real plane interface.
== Canvas Control Panel ==
I've made a Canvas panel  to control the process.
The file goes in /Nasal/canvas/gui/dialogs.
This my /Nasal/canvas/gui/dialogs/RealPlanes.nas
<syntaxhighlight lang="nasal>
var RealPlanes = {
  new: func
  {
    var m = {
      parents: [RealPlanes],
      _dlg: canvas.Window.new([600,500], "dialog")
                        .set("title", "Real Planes")
.set("always-on-top",0)
                        .set("resize", 1),
                       
                       
      _active_button: nil,
      _show_more: nil
   
    };
    m._dlg.del = func()
{
  print("Cleaning up...\n");
  m._rttracker.stop();
  call(canvas.Window.del, [],m._dlg);
};               
    m._dlg.getCanvas(1)
          .set("background", canvas.style.getColor("bg_color"));
    m._root = m._dlg.getCanvas().createGroup();
    m._rttracker = nil;
 
    m._rows = std.Vector.new();
    m._RealPlanes = props.globals.getNode("/RealPlanes",1); 
    m._planes = m._RealPlanes.getChildren();
    m._pcount = size(m._planes);
    m._planeChecks = std.Vector.new();
  return m;  },
 
 
 
 
  showlist: func (){
 
    var vbox = VBoxLayout.new();
   
    me._dlg.setLayout(vbox);   
    me._tab_bar = HBoxLayout.new();
    #vbox.addItem(m._tab_bar);
 
   
   
   
   
 
  var headingrow = HBoxLayout.new();
  var headingrow0 = HBoxLayout.new();
  var vorentry = {};
  vorentry.csgn = "CallSign";
  vorentry.lat = "lat";
  vorentry.lon = "lon";
  vorentry.hdg = "Hdg"; 
  vorentry.speed = "Speed";
  vorentry.alt = "Alt";
  vorentry.type = "Type";
  vorentry.model = "AI Model";
  var test = gui.widgets.Label.new(me._root, style, {});   
  var text =
  me._root.createChild("text")
      .setText( vorentry.csgn );
   
 
 
 
 
  # headingrow.addItem(test.setText(text));
 
    headingrow.addItem(test.setText(vorentry.csgn).setFixedSize(60, 25));
    headingrow.addSpacing(5);
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
    .setText(vorentry.lat));                 
    headingrow.addSpacing(5);
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
    .setText(vorentry.lon)); 
    headingrow.addSpacing(5);
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
      .setText(vorentry.hdg)); 
    headingrow.addSpacing(5);
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
      .setText(vorentry.speed)); 
    headingrow.addSpacing(5);
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
      .setText(vorentry.alt)); 
    headingrow.addSpacing(5);
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
      .setText(vorentry.type)); 
    headingrow.addItem(gui.widgets.Label.new(me._root, style, {}).setFixedSize(60, 25)
      .setText(vorentry.model));     
   
#     var buttonActivate = gui.widgets.Button.new(me._root, style, {})
#        .setFixedSize(95, 25)
# .setText(sprintf("%d",10));
var chb1 = gui.widgets.CheckBox.new(me._root, style, {})
.setText("Active")
.setCheck(props.globals.getNode("/RealPlanesActive",1).getValue())
.listen("toggled", func(e) {  
      if (chb1.checked()){
props.globals.getNode("/RealPlanesActive",1).setBoolValue(1);
      }
      else{
me.setModelsInactive();
props.globals.getNode("/RealPlanesActive",1).setBoolValue(0);
      }
}
      );
var chb2 = gui.widgets.CheckBox.new(me._root, style, {})
.setText("Auto Load Models")
.setCheck(props.globals.getNode("/RealPlanesAutoModel",1).getValue())
.listen("toggled", func(e) {  
      if (chb2.checked()){
props.globals.getNode("/RealPlanesAutoModel",1).setBoolValue(1);
      }
      else{
props.globals.getNode("/RealPlanesAutoModel",1).setBoolValue(0);
      }
}
      );
      headingrow0.addItem(chb1);
      headingrow0.addItem(chb2);   
      var rangel =  gui.widgets.Label.new(me._root, style, {});
      headingrow0.addItem(rangel.setText("Range(nm)"));
    var range = gui.widgets.LineEdit.new(me._root, style, {size: [60, 50]});   
    range.setFixedSize(60, 25); 
    headingrow0.addItem(range.setText(sprintf("%d",getprop("/RealPlanesRange")))); 
   
    var buttonActivate = gui.widgets.Button.new(me._root, style, {})
.setText("refresh")
.setFixedSize(95, 25);
    headingrow0.addItem(buttonActivate); 
    buttonActivate.listen("clicked",func{
      if(num(range.text())){
props.globals.getNode("/RealPlanesRange",1).setIntValue(range.text());
      }
      fillList();
      });
   
   
    vbox.addItem(headingrow0);
   
    vbox.addItem(headingrow);
   
  var scroll = gui.widgets.ScrollArea.new(me._root, style, {size: [96, 128]})
    .move(20, 100);
  vbox.addItem(scroll, 1);
  me._scroll_content =
    scroll.getContent()
  .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
  .set("character-size", 16)
  .set("alignment", "left-center");
  me._list = VBoxLayout.new();
  scroll.setLayout(me._list);
  me._info_label = gui.widgets.Label.new(me._root, style, {});
  vbox.addItem(me._info_label);
 
 
 
 
 
 
var fillList = func(){
  me._RealPlanes = props.globals.getNode("/RealPlanes",1); 
  me._planes = me._RealPlanes.getChildren();
  me._pcount = size(me._planes);
  me._planeChecks = std.Vector.new();
# print(size( me._list.getChildren());
  me._list.clear();
  #scroll.setVisible(0);
  # scroll = gui.widgets.ScrollArea.new(me._root, style, {size: [96, 128]})
  #     .move(20, 100);
  me._scroll_content =
    scroll.getContent()
  .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
  .set("character-size", 16)
  .set("alignment", "left-center");
  me._list = VBoxLayout.new();
  scroll.setLayout(me._list);  
  for(i=0;i<size(me._rows.vector);i+=1){
   
      me._rows.vector[i].hide();
   
     
  }
  me._rows = std.Vector.new();
 
  for(i=0;i<me._pcount;i += 1){ 
   
      var row = HBoxLayout.new();      
      me._rows.append(row);
      me._list.addItem(row);   
   
      var clsgnlabel = gui.widgets.Label.new(me._scroll_content, style, {})
  .setBackground("#ffffff")
  .setFixedSize(60, 25)
.setText(me._planes[i].getNode("clsgn").getValue());
     
      row.addItem(clsgnlabel);
 
      row.addSpacing(5);
      row.addItem(gui.widgets.Label.new(me._scroll_content, style, {})
      .setBackground("#ffffff")
      .setFixedSize(60, 25)
      .setText(sprintf("%9.4f",me._planes[i].getNode("lat").getValue())));                 
#      sprintf("%d",package.lat))
      row.addSpacing(5);
     
      row.addItem(gui.widgets.Label.new(me._scroll_content, style, {})
      .setBackground("#ffffff")
      .setFixedSize(60, 25)
      .setText(sprintf("%9.4f",me._planes[i].getNode("lon").getValue()))); 
      row.addSpacing(5);
     
      row.addItem(gui.widgets.Label.new(me._scroll_content, style, {})
      .setBackground("#ffffff")
      .setFixedSize(60, 25)
      .setText(sprintf("%4.0f",me._planes[i].getNode("heading").getValue()))); 
      row.addSpacing(5);
     
   
row.addItem(gui.widgets.Label.new(me._scroll_content, style, {})
      .setBackground("#ffffff")
      .setFixedSize(60, 25)
      .setText(sprintf("%3.0f",me._planes[i].getNode("speed").getValue()))); 
row.addSpacing(5);
row.addItem(gui.widgets.Label.new(me._scroll_content, style, {})
      .setBackground("#ffffff")
      .setFixedSize(60, 25)
      .setText(sprintf("%5.0f",me._planes[i].getNode("alt").getValue()))); 
      row.addSpacing(5);
      row.addItem(gui.widgets.Label.new(me._scroll_content, style, {})
      .setBackground("#ffffff")
      .setFixedSize(60, 25)
      .setText(sprintf("%s",me._planes[i].getNode("type").getValue()))); 
      row.addSpacing(5);
     
      print("pi i="~i~"model "~me._planes[i].getNode("model").getValue());
      me._planeChecks.append(gui.widgets.CheckBox.new(me._scroll_content, style, {})
.setText("model")
.setCheck(me._planes[i].getNode("model").getValue())
.listen("clicked", func(e) {
    me.setModel();
})
      );
      row.addItem(me._planeChecks.vector[i]);
      var x = me._planeChecks.vector[i];
  }
}
fillList();
me._rttracker = maketimer(10,fillList);
me._rttracker.start();
  },
  setModel: func{
    print("setmodel");   
    for(i=0;i<size(me._planeChecks.vector);i+=1){
      print(me._planes[i].getNode("clsgn").getValue());
      var thisplane = me._RealPlanes.getNode(me._planes[i].getNode("clsgn").getValue());
   
      if(thisplane != nil){   
  var x = me._planeChecks.vector[i].checked();
  thisplane.getNode("model").setBoolValue(x);
      }
      else{
me.showlist();
      }
    } 
  },
  setModelsInactive: func{
    print("setModelsInactive");   
    for(i=0;i<size(me._planes);i+=1){
      print(me._planes[i].getNode("clsgn").getValue());
      var thisplane = me._RealPlanes.getNode(me._planes[i].getNode("clsgn").getValue());
   
      if(thisplane != nil){   
  thisplane.getNode("model").setBoolValue(0);
      }
   
    } 
  },
} ;
var ac = RealPlanes.new();
ac.showlist();
</syntaxhighlight>
''setCheck'' changes the click status '''''before''''' calling the function so the function can know what's wanted.
''checked'' just returns whether the box is checked or not.
In '''/gui/menubar.xml''' insert the RealPlanes entry in the AI menu as below:
<syntaxhighlight lang="xml">
<menu>
<name>ai</name>
<item>
<name>scenario</name>
<binding>
<command>dialog-show</command>
<dialog-name>scenario</dialog-name>
</binding>
</item>
<item>
<name>RealPlanes</name>
<binding>
<command>nasal</command>
<script>
  canvas.loadDialog("RealPlanes");
</script>
</binding>
</item>
...
</menu>
</syntaxhighlight>
== Extra Views ==
So that we can watch the action I've added extra views to my aircraft -set.xml in the <sim> element. The views also work for multiplayer mode.
<syntaxhighlight lang="xml">
<view n="108">
<name>MPlayers</name>
<type>lookfrom</type>
<config>
<internal type="bool">false</internal>
<from-model type="bool">false</from-model>
<!--<from-model-idx type="int">0</from-model-idx>-->
<eye-lat-deg-path>/sim/somepath/ground-observer-lat-deg</eye-lat-deg-path>
<eye-lon-deg-path>/sim/somepath/ground-observer-lon-deg</eye-lon-deg-path>
<eye-alt-ft-path>/sim/somepath/ground-observer-alt-ft</eye-alt-ft-path>
<eye-roll-deg-path>/sim/eyeroll</eye-roll-deg-path>
<eye-pitch-deg-path>/sim/eyepitch</eye-pitch-deg-path>
<eye-heading-deg-path>/sim/eyeheading</eye-heading-deg-path>
<at-model type="bool">false</at-model>
<at-model-idx type="int">0</at-model-idx>
<ground-level-nearplane-m type="double">0.1f</ground-level-nearplane-m>
<target-lat-deg-path>/sim/somepath/target-lat-deg</target-lat-deg-path>
<target-lon-deg-path>/sim/somepath/target-lon-deg</target-lon-deg-path>
<target-alt-ft-path>/sim/somepath/target-alt-ft</target-alt-ft-path>
<target-x-offset-m>/sim/somepath/targetxoff</target-x-offset-m>
<target-y-offset-m>/sim/somepath/targetyoff</target-y-offset-m>
<target-z-offset-m>/sim/somepath/targetzoff</target-z-offset-m>
   
<!-- back -->
<!--<heading-offset-deg>180</heading-offset-deg>-->
<x-offset-m>30</x-offset-m>
<!-- right -->
<y-offset-m>0.0</y-offset-m>
<!-- up -->
<z-offset-m>0.0</z-offset-m>
<!-- back -->
<!--<pitch-offset-deg>0</pitch-offset-deg>-->
<default-field-of-view-deg>60</default-field-of-view-deg>
</config>
</view>
<view n="109">
<name>Real Traffic</name>
<type>lookfrom</type>
<config>
<internal type="bool">false</internal>
<from-model type="bool">false</from-model>
<!--<from-model-idx type="int">0</from-model-idx>-->
<eye-lat-deg-path>/sim/somepath/ground-observer-lat-deg</eye-lat-deg-path>
<eye-lon-deg-path>/sim/somepath/ground-observer-lon-deg</eye-lon-deg-path>
<eye-alt-ft-path>/sim/somepath/ground-observer-alt-ft</eye-alt-ft-path>
<eye-roll-deg-path>/sim/eyeroll</eye-roll-deg-path>
<eye-pitch-deg-path>/sim/eyepitch</eye-pitch-deg-path>
<eye-heading-deg-path>/sim/eyeheading</eye-heading-deg-path>
<at-model type="bool">false</at-model>
<at-model-idx type="int">0</at-model-idx>
<ground-level-nearplane-m type="double">0.1f</ground-level-nearplane-m>
<target-lat-deg-path>/sim/somepath/target-lat-deg</target-lat-deg-path>
<target-lon-deg-path>/sim/somepath/target-lon-deg</target-lon-deg-path>
<target-alt-ft-path>/sim/somepath/target-alt-ft</target-alt-ft-path>
<target-x-offset-m>/sim/somepath/targetxoff</target-x-offset-m>
<target-y-offset-m>/sim/somepath/targetyoff</target-y-offset-m>
<target-z-offset-m>/sim/somepath/targetzoff</target-z-offset-m>
   
<!-- back -->
<!--<heading-offset-deg>180</heading-offset-deg>-->
<x-offset-m>30</x-offset-m>
<!-- right -->
<y-offset-m>0.0</y-offset-m>
<!-- up -->
<z-offset-m>0.0</z-offset-m>
<!-- back -->
<!--<pitch-offset-deg>0</pitch-offset-deg>-->
<default-field-of-view-deg>60</default-field-of-view-deg>
</config>
</view>
<somepath>
    <ground-observer-lon-deg type="double">55.6124</ground-observer-lon-deg>
    <ground-observer-lat-deg type="double" >-4.1243</ground-observer-lat-deg>
    <ground-observer-alt-ft type="double">700.5</ground-observer-alt-ft>
    <target-lon-deg type="double">55.6124</target-lon-deg>
    <target-lat-deg type="double" >-4.1243</target-lat-deg>
    <target-alt-ft type="double">700.5</target-alt-ft>
    <eyepitch>0</eyepitch>
    <eyeroll>0</eyeroll>  
    <eyeheading>0</eyeheading>
    <targetxoff>0</targetxoff>
    <targetyoff>0</targetyoff>
    <targetzoff>0</targetzoff>
    <playernumber type="int">0</playernumber>
</somepath>
</syntaxhighlight>
Also add this to the <input><keyboard> element: It lets you spacebar through each view of a multiplayer or real plane.
<syntaxhighlight lang="xml">
<key n="32">
  <name>space</name>
  <desc>cycle playernumber</desc>
<binding>
  <command>nasal</command>
  <script>
    var modeltype = "multiplayer";
    if (getprop("sim/current-view/name") == "Real Traffic"){
modeltype = "aircraft";
}
   
   
   
   
    var player = getprop("sim/somepath/playernumber");
    var count=-1;
    var inc = 0;
    var eng = props.globals.getNode("/ai/models/");    
    foreach (c; eng.getChildren(modeltype)) { 
  count = count + 1;
  if (c.getNode("valid",1).getValue()){  
      if (count > player){
inc = 1;
break;
      }
  }    
    }
    if(inc){
setprop("sim/somepath/playernumber",count);
    }
    else{
        setprop("sim/somepath/playernumber",0);
    }   
   
 
  </script>
      </binding>
</key>
</syntaxhighlight>
You also need this view helper in your '''/Aircraft/xxxx/Nasal''' folder
<syntaxhighlight type="nasal">
var windowup = 0;
var window = 0;
var mptrack = func{
  if (windowup == 0){
window = screen.window.new(nil, 110, 1, 1);
windowup = 1;
  }
  if (getprop("sim/current-view/name") == "Real Traffic"){
      player = getprop("sim/somepath/playernumber"); 
      if (getprop("/ai/models/multiplayer[" ~ player ~ "]/valid")){
#print(getprop("/ai/models/multiplayer[player]/callsign"));
window.write(getprop("/ai/models/multiplayer[" ~ player ~ "]/callsign"));
elev =getprop("/ai/models/multiplayer[" ~ player ~ "]/position/altitude-ft");
lat = getprop("/ai/models/multiplayer[" ~ player ~ "]/position/latitude-deg");
lon = getprop("/ai/models/multiplayer[" ~ player ~ "]/position/longitude-deg");
setprop("sim/somepath/ground-observer-lon-deg",lon);
setprop("sim/somepath/ground-observer-lat-deg",lat);
setprop("sim/somepath/ground-observer-alt-ft",elev);
      }
  }
}
var rttrack = func{
  if (windowup == 0){
window = screen.window.new(nil, 110, 1, 1);
windowup = 1;
  }
  if (getprop("sim/current-view/name") == "MPlayers"){
      player = getprop("sim/somepath/playernumber"); 
      if (getprop("/ai/models/aircraft[" ~ player ~ "]/valid")){
#print(getprop("/ai/models/multiplayer[player]/callsign"));
window.write(getprop("/ai/models/aircraft[" ~ player ~ "]/callsign"));
elev =getprop("/ai/models/aircraft[" ~ player ~ "]/position/altitude-ft");
lat = getprop("/ai/models/aircraft[" ~ player ~ "]/position/latitude-deg");
lon = getprop("/ai/models/aircraft[" ~ player ~ "]/position/longitude-deg");
setprop("sim/somepath/ground-observer-lon-deg",lon);
setprop("sim/somepath/ground-observer-lat-deg",lat);
setprop("sim/somepath/ground-observer-alt-ft",elev);
      }
  }
}
var rttracker = maketimer(0,rttrack);
rttracker.start();
var tracker = maketimer(0,mptrack);
tracker.start();
setlistener( "sim/current-view/pitch-offset-deg", func {
if (getprop("sim/current-view/name") != "MPlayers"){
  if (getprop("sim/current-view/name") != "Real Traffic"){
      return;
  }
}
targetdistance = 100;
alpha = getprop("sim/current-view/pitch-offset-deg") * (math.pi / 180);
newYoffset = 0 - (targetdistance * math.sin(alpha));
setprop("sim/current-view/y-offset-m", newYoffset);
});
setlistener( "sim/current-view/heading-offset-deg", func {
if (getprop("sim/current-view/name") != "MPlayers"){
  if (getprop("sim/current-view/name") != "Real Traffic"){
      return;
  }
}
targetdistance = 100;
alpha = getprop("sim/current-view/heading-offset-deg") * (math.pi / 180);
#print(alpha);
newXoffset = targetdistance * math.sin(alpha);
newZoffset = targetdistance * (math.cos(alpha));
#print(newXoffset);
#print(newZoffset);
setprop("sim/current-view/x-offset-m", newXoffset);
setprop("sim/current-view/z-offset-m", newZoffset);
}
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,969: Line 1,556:
add /Aircraft/name/Nasal/viewhelper.nas as in '''Extra Views'''
add /Aircraft/name/Nasal/viewhelper.nas as in '''Extra Views'''


I added these entries to '''/Nasal/IORules''' to allow access
<syntaxhighlight>
READ ALLOW /mnt/cache1/*
WRITE ALLOW /mnt/GameData/flightgear-fgdata/AI/rttemp/*
WRITE ALLOW /mnt/cache1/*
</syntaxhighlight>


== References ==
== References ==
{{Appendix}}
{{Appendix}}
36

edits

Navigation menu