1var mode = 0; #current mode
2var displayed_screen = 0; #screenModeAndSettings
3var page = 0; #current page
4var blocked = 0; #boolean: 0 -> possible to cycle pages
5var isOn = 0; #ON/OFF: 0 -> OFF
6var refresh_timer = 0; #avoid multiple settimers
7var freq = 1; #settimer frequency (in sec)
8var screen = []; #array containing all screens
9var line = []; #array containing the displayed lines
10var routes = []; #array containing the preprogrammed tasks
11var alt_unit_full_name = ["Feet", "Meters"];
12var dist_unit_full_name = ["Nautic Miles", "Kilometers"];
13var spd_unit_full_name = ["Knots", "KM/H"];
14var alt_unit_short_name = ["ft", "m"];
15var dist_unit_short_name = ["nm", "km"];
16var spd_unit_short_name = ["kt", "km/h"];
17var spd_unit = 0;
18var dist_unit = 0;
19var alt_unit = 0;
20var startpos = nil; #geo.nas aircraft position
21var waypointindex = 0; #step in actual task
22var thresold_alert = [120, 60, 30, 15];
23var thresold_alert_index = 1;
24var thresold_next_waypoint = 5;
25NOT_YET_IMPLEMENTED = [
26    "",
27    "    NOT",
28    "    YET",
29    "IMPLEMENTED",
30    ""
31];
32var LINES = 5; #lines in display
33var page_list = [
34    [0,0,0,0,0,0],      #0 ModeAndSettings: 1 page for mode, 5 pages for settings
35    [1,2,3],            #1 PositionMain, Odometers, WindInfos
36    [5,4,1,2,3,6,7],    #2 AirportMain, NavigationMain, PositionMain, Odometers, WindInfos, AirportInfos, SearchAirport
37    [8,4,1,2,3,9],      #3 TurnpointSelect, NavigationMain, PositionMain, Odometers, WindInfos, TurnpointInfos
38    [10,4,1,2,3,11,12], #4 TaskSelect, NavigationMain, PositionMain, Odometers, WindInfos, WaypointInfos, WaypointsList
39    [13]	        #5 Edit (special mode for editing waypoint, called from other modes)
40];
41
42             #to ft      m
43var alt_conv = [[1.0000,0.3048],  #from ft
44                [3.2808,1.0000]]; #from m
45
46	      #to nm      km     m
47var dist_conv = [[1.00000   ,1.852, 1852],  #from nm
48                 [0.53996   ,1.000, 1000],  #from km
49	         [0.00053996,0.001, 1.00]]; #from m
50
51var gps_data = props.globals.getNode("/instrumentation/gps",1);
52var scratch = gps_data.getNode("scratch",1);
53var gps_wp = gps_data.getNode("wp",1);
54var route = props.globals.getNode("/autopilot/route-manager/route",1);
55
56#### warps for buttons and knobs ########################################"
57var right_knob = func(dir) { #manage right knob, depends of displayed screen
58    isOn > 0 or return;
59    screen[displayed_screen].right(dir);
60    refresh_display();
61}
62
63var enter_button = func() { #manage enter button, depends of displayed screen
64    isOn > 0 or return;
65    screen[displayed_screen].enter();
66    refresh_display();
67}
68
69var escape_button = func() { #manage escape button, depends of displayed screen
70    isOn > 0 or return;
71    screen[displayed_screen].escape();
72    refresh_display();
73}
74
75var start_button = func() { #manage start button, depends of displayed screen
76    isOn > 0 or return;
77    screen[displayed_screen].start();
78    refresh_display();
79}
80
81var left_knob = func(dir) { #manage left button, cycle in mode's pages if not blocked
82    isOn > 0 or return;
83    if (blocked == 0) {
84	if (displayed_screen == 13 and dir) {
85	    mode = screenEdit.previous_mode;
86	    page = screenEdit.previous_page;
87	}
88	page = cycle(size(page_list[mode]), page, dir);
89	displayed_screen = page_list[mode][page];
90    }
91    refresh_display();
92}
93
94var select_mode = func(dir) { #manage mode knob, cycle into available modes
95    isOn > 0 or return;
96    blocked = 0;
97    if (displayed_screen != 0) {
98	displayed_screen = 0; #screenModeAndSettings
99	page = 0;
100	mode = 0;
101    }
102    elsif (page == 0)
103	screen[0].mode_ = cycle(size(screen[0].available_modes), screen[0].mode_, dir);
104    refresh_display();
105}
106
107var power_knob = func() { #manage POWER knob
108    if (arg[0] > 0 and isOn < 11) isOn += 1;
109    elsif (arg[0] < 0 and isOn > 0) isOn -= 1;
110    else return;
111    props.globals.getNode("/instrumentation/zkv500/power",1).setIntValue(isOn);
112    var light = 0;
113    if (isOn > 0 and getprop("instrumentation/gps/serviceable") != 0)
114	light = (isOn - 1)/20;
115    props.globals.getNode("/instrumentation/zkv500/retro-light").setDoubleValue(light);
116    refresh_display();
117}
118
119### useful funcs #########################################################
120var display = func () { #display the array line[]
121    for (var i = 0; i < LINES; i += 1) line[i].setValue(arg[0][i]);
122}
123
124var apply_command = func (command) {
125    gps_data.getNode("command").setValue(command);
126}
127
128var browse = func (entries_nbr, index_pointer, index_page,dir) {
129    #browse multipaged entries, returns [pointer in page, page]
130    nl = entries_nbr - (index_page * LINES) >= LINES ? LINES : math.mod(entries_nbr - (index_page * LINES), LINES);
131    if (index_pointer + 1 == nl) {
132       np = int(entries_nbr / LINES) + (math.mod(entries_nbr,LINES) ? 1 : 0);
133       index_page = cycle(np, index_page, dir);
134    }
135    index_pointer = cycle(nl, index_pointer, dir);
136    return [index_pointer, index_page];
137}
138
139var cycle = func (entries_nbr, actual_entrie, dir) {
140    #cycle through entries, return entry index
141    entries_nbr -= 1;
142    if (dir == 1 and actual_entrie == entries_nbr) return 0;
143    elsif (dir == -1 and actual_entrie == 0) return entries_nbr;
144    else return actual_entrie + dir;
145}
146
147var refresh_display = func(forced = 1) { #refresh displayed lines, settimer if necessary
148    if (!forced) refresh_timer -= 1;
149    screen[displayed_screen].lines();
150    if (isOn and 0 < displayed_screen and displayed_screen < 5 and !refresh_timer) {
151	refresh_timer += 1;
152	settimer(func { refresh_display(0); }, freq, 1);
153    }
154    waypointAlert();
155}
156
157var seconds_to_string = func (time) { #converts secs (double) in string "hh:mm:ss"
158    var hh = int(time / 3600);
159    if (hh > 100) return "--:--:--";
160    var mm = int((time - (hh * 3600)) / 60);
161    var ss = int(time - (hh * 3600 + mm * 60));
162    return sprintf("%02d:%02d:%02d", hh, mm, ss);
163}
164
165### route management ######################################################
166var list_routes = func { #load preprogrammed tasks
167    routes = [];
168    var path = getprop("/sim/fg-home") ~ "/Routes";
169    var s = io.stat(path);
170    if (s != nil and s[11] == "dir") {
171	foreach (var file; directory(path))
172	    if (file[0] != 46) append(routes, file);
173#	size(routes) != 0 or return;
174#	routes = sort(routes, func(a,b) {
175#	    num(a[1]) == nil or num(b[1]) == nil ? cmp(a[1], b[1]) : a[1] - b[1];
176#	});
177#	print(size(routes));
178#	foreach (var r; routes) print (r ~ ":" ~ r[0]);
179    }
180    return size(routes);
181}
182
183var add_waypoint = func (ID, name, type, coord) { #add a waypoint to a route
184    var waypoint = gps_data.getNode("route/Waypoint["~screenWaypointsList.n~"]/",1);
185    screenWaypointsList.n += 1;
186    waypoint.getNode("ID",1).setValue(ID);
187    waypoint.getNode("latitude-deg",1).setDoubleValue(coord[0]);
188    waypoint.getNode("longitude-deg",1).setDoubleValue(coord[1]);
189    waypoint.getNode("altitude-ft",1).setDoubleValue(coord[2]*alt_conv[1][0]);
190    waypoint.getNode("name",1).setValue(name);
191    waypoint.getNode("desc",1).setValue("no infos");
192    waypoint.getNode("waypoint-type",1).setValue(type);
193}
194
195var save_route = func { #save the route
196    screenWaypointsList.n != 0 or return;
197    var first_id = gps_data.getNode("route/Waypoint/ID").getValue();
198    var last_id = gps_data.getNode("route/Waypoint["~(screenWaypointsList.n - 1)~"]/ID").getValue();
199    var path = getprop("/sim/fg-home") ~ "/Export/"~first_id~"-"~last_id~".xml";
200    var args = props.Node.new({ filename : path });
201    var export = args.getNode("data", 1);
202    props.copy(gps_data.getNode("route"), export);
203    fgcommand("savexml", args);
204}
205
206var Waypoint_to_scratch = func (node) {
207    scratch.getNode("latitude-deg",1).setValue(node.getNode("latitude-deg").getValue());
208    scratch.getNode("longitude-deg",1).setValue(node.getNode("longitude-deg").getValue());
209    scratch.getNode("altitude-ft",1).setValue(node.getNode("altitude-ft").getValue());
210    scratch.getNode("ident").setValue(node.getNode("ID").getValue());
211    if (node.getNode("name") != nil)
212        scratch.getNode("name",1).setValue(node.getNode("name").getValue());
213    else
214        scratch.getNode("name",1).setValue("");
215    if (node.getNode("type") != nil)
216        scratch.getNode("type",1).setValue(node.getNode("waypoint-type").getValue());
217    else
218        scratch.getNode("type",1).setValue("");
219}
220
221var waypointAlert = func { #alert pilot about waypoint approach
222    if (mode) {
223	var ttw = getprop("/instrumentation/gps/wp/wp[1]/TTW-sec");
224	ttw > -1 or return;
225	if (ttw < thresold_alert[thresold_alert_index])
226	    gps_data.getNode("waypoint-alert",1).setBoolValue(1);
227	else
228	    gps_data.getNode("waypoint-alert",1).setBoolValue(0);
229    }
230}
231
232### turnpoints management ######################################################
233var load_bookmarks = func { #load turnpoints
234    var n = 0;
235    gps_data.getNode("bookmarks",1).removeChildren("bookmark");
236    var file = getprop("/sim/fg-home") ~ "/Export/bookmarks.xml";
237    var s = io.stat(file);
238    if (s != nil) {
239	fgcommand("loadxml", props.Node.new({
240	    "filename": file,
241	    "targetnode": "/instrumentation/gps/bookmarks"
242	}));
243	foreach (var c ;props.globals.getNode("/instrumentation/gps/bookmarks").getChildren("bookmark")) n += 1;
244    }
245    else
246	print(file ~ " not found...");
247    return n;
248}
249
250var save_bookmarks = func { #save turnpoints
251    var path = getprop("/sim/fg-home") ~ "/Export/bookmarks.xml";
252    var args = props.Node.new({ filename : path });
253    var export = args.getNode("data", 1);
254    props.copy(gps_data.getNode("bookmarks"), export);
255    fgcommand("savexml", args);
256}
257
258var add_bookmark = func (ID, name, type, coord) { #add turnpoint
259    var bookmark = gps_data.getNode("bookmarks/bookmark["~screenTurnpointSelect.n~"]/",1);
260    screenTurnpointSelect.n += 1;
261    bookmark.getNode("ID",1).setValue(ID);
262    bookmark.getNode("latitude-deg",1).setDoubleValue(coord[0]);
263    bookmark.getNode("longitude-deg",1).setDoubleValue(coord[1]);
264    bookmark.getNode("altitude-ft",1).setDoubleValue(coord[2]*alt_conv[1][0]);
265    bookmark.getNode("desc",1).setValue("no infos");
266    bookmark.getNode("name",1).setValue(name);
267    bookmark.getNode("waypoint-type",1).setValue(type);
268    save_bookmarks();
269}
270
271var EditMode = func (length, title, start_command, numcar = 0) {
272    #special mode for editing simple text
273    screenEdit.previous_mode = mode;
274    screenEdit.previous_page = page;
275    mode = 5; #ID edition
276    page = 0;
277    screenEdit.init(length, title, start_command, numcar);
278}
279
280### initialisation stuff ###################################################
281var load_screens = func {
282    var zkv500_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/zkv500/";
283    io.load_nasal(zkv500_dir ~ "AirportScreens.nas","zkv500");
284    io.load_nasal(zkv500_dir ~ "TurnpointScreens.nas","zkv500");
285    io.load_nasal(zkv500_dir ~ "MainScreens.nas","zkv500");
286    io.load_nasal(zkv500_dir ~ "TaskScreens.nas","zkv500");
287}
288
289var organize_screens = func {
290    screen = []; #empty screens
291    append(screen, zkv500.screenModeAndSettings); #0
292    append(screen, zkv500.screenPositionMain);    #1
293    append(screen, zkv500.screenOdometers);	  #2
294    append(screen, zkv500.screenWindInfos);	  #3
295    append(screen, zkv500.screenNavigationMain);  #4
296    append(screen, zkv500.screenAirportMain);     #5
297    append(screen, zkv500.screenAirportInfos);    #6
298    append(screen, zkv500.screenSearchAirport);   #7
299    append(screen, zkv500.screenTurnpointSelect); #8
300    append(screen, zkv500.screenTurnpointInfos);  #9
301    append(screen, zkv500.screenTaskSelect);      #10
302    append(screen, zkv500.screenWaypointInfos);   #11
303    append(screen, zkv500.screenWaypointsList);   #12
304    append(screen, zkv500.screenEdit);		  #13
305}
306
307var init_gps_variables = func {
308    mode = 0;
309    page = 0;
310    displayed_screen = 0; #screenModeAndSettings
311    blocked = 0; #unlock left_knob
312    #isOn = 0; #start OFF
313    startpos = nil; #unset start position
314    waypointindex = 0; #route waypoint index on beginning
315}
316
317var init_gps_props = func {
318    for (var i = 0; i < LINES; i += 1) {
319	append(line, props.globals.getNode("/instrumentation/zkv500/line[" ~ i ~ "]", 1));
320	line[i].setValue("");
321    }
322    props.globals.getNode("/instrumentation/zkv500/retro-light",1).setDoubleValue(0);
323    props.globals.getNode("/instrumentation/zkv500/power",1).setIntValue(0);
324    aircraft.light.new("/sim/model/gps/redled", [0.1, 0.1, 0.1, 0.7], "/instrumentation/gps/waypoint-alert");
325    aircraft.light.new("/sim/model/gps/greenled", [0.6, 0.3], "/instrumentation/gps/message-alert");
326    startpos = geo.Coord.new(geo.aircraft_position());
327    screenPositionMain.begin_time = props.globals.getNode("/sim/time/elapsed-sec",1).getValue();
328    setlistener("/instrumentation/gps/serviceable", func {
329	if (getprop("/instrumentation/gps/serviceable") == 0)
330	    setprop("/instrumentation/zkv500/retro-light", 0);
331	elsif (isOn > 0)
332	    setprop("/instrumentation/zkv500/retro-light", (isOn - 1)/20);
333    }, 0, 0);
334}
335
336var init = func() {
337    load_screens();
338    organize_screens();
339    init_gps_variables();
340    init_gps_props();
341    print("GPS... initialized");
342}
343
344setlistener("/sim/signals/fdm-initialized",init);
345