5f96a1263f6f7e87efde98f2f441574be2668e30
[fg:hoorays-fgdata.git] / Nasal / canvas / map / TFC.lcontroller
1 # See: http://wiki.flightgear.org/MapStructure
2 # FIXME: this needs to be configurable via the ctor to optionally differentiate between MP and AI traffic, and exclude ground/sea traffic
3 # or at least use a different, non-TCAS symbol for those (looking pretty weird ATM)
4
5 # Class things:
6 var name = 'TFC';
7 var parents = [SymbolLayer.Controller];
8 var __self__ = caller(0)[0];
9 SymbolLayer.Controller.add(name, __self__);
10 SymbolLayer.add(name, {
11         parents: [MultiSymbolLayer],
12         type: name, # Symbol type
13         df_controller: __self__, # controller to use by default -- this one
14 });
15
16 var model_root = props.globals.getNode("/ai/models/");
17
18 var new = func(layer) {
19         var m = {
20                 parents: [__self__],
21                 layer: layer,
22                 map: layer.map,
23                 listeners: [],
24                 searchCmd: searchCmd_default,
25         };
26         layer.searcher._equals = func(l,r) l.equals(r);
27         m.addVisibilityListener();
28
29         return m;
30 };
31 var del = func() {
32         #print(name~".lcontroller.del()");
33         foreach (var l; me.listeners)
34                 removelistener(l);
35 };
36
37 var TrafficModel = {
38         new: func(node, id=nil, layer=nil) {
39                 if (id == nil) id = node.getValue("id");
40                 var m = {
41                         # Note: because this inherits from props.Node, Symbol.Controller.equals
42                         # will call l.equals(r) -- the one defined below
43                         parents: [TrafficModel, geo.Coord, node], # note we don't implement a full geo.Coord API
44                         id: id,
45                         node: node,
46                         pos: node.getNode("position",1),
47                         type: node.getName(),
48                 };
49                 return m;
50         },
51         equals: func(other) other.id == me.id,
52         latlon: func() { # this makes sure to look like a geo.Coord to MapStructure, but will internally use the AI/MP traffic properties instead
53                 return [
54                         me.pos.getValue("latitude-deg"),
55                         me.pos.getValue("longitude-deg"),
56                         me.pos.getValue("altitude-ft")
57                 ];
58         },
59         # these are helper methods related to TCAS handling (TAs/RAs)
60         get_threat_lvl: func() me.getValue("tcas/threat-level"),
61         get_vspd: func() (me.getValue("velocities/vertical-speed-fps") or 0)*60,
62         get_alt:  func() (me.getValue("position/altitude-ft") or 0),
63 };
64
65 var get_alt_diff = func(model) {
66         # debug.dump( keys(me) );
67         var model_alt = model.get_alt();
68         var alt = me.map.getAlt();
69         if (alt == nil or model_alt == nil) return 0;
70         return alt-model_alt;
71 };
72
73 ##
74 # dummy/placeholder (will be overridden in ctor and set to the default callback)
75 var searchCmd  = func;
76
77 var searchCmd_default = func {
78         # TODO: this would be a good candidate for splitting across frames
79         printlog(_MP_dbg_lvl, "Doing query: "~name);
80
81         if (me.map.getRange() == nil) return;
82
83         var result = [];
84         var models = 0;
85
86         # AI and Multiplayer traffic
87         foreach (var t; model_root.getChildren()) {
88                 if (!t.getValue("valid")) continue;
89                 var t_id = t.getNode("id");
90                 if (t_id == nil or t_id.getValue() == -1) continue;
91                 models += 1;
92                 var (lat,lon) = (t.getValue("position/latitude-deg"),
93                                  t.getValue("position/longitude-deg"));
94                 if (lat == nil or lon == nil) {
95                         printlog("alert", "lat/lon was nil for AI node "~t.getPath());
96                         continue;
97                 }
98                 if (me.map.controller.in_range(lat, lon))
99                         append(result, TrafficModel.new(t, nil, me.layer));
100         }
101
102         #print("Found "~size(result)~" TrafficModel's in range out of "~models~" total.");
103         return result;
104 };
105