-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
pc_aircraft.ml
505 lines (456 loc) · 20.3 KB
/
pc_aircraft.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
(*
* Paparazzi center aircraft handling
*
* Copyright (C) 2007 ENAC, Pascal Brisset, Antoine Drouin
*
* This file is part of paparazzi.
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*)
module Utils = Pc_common
module CP = Pc_control_panel
open Printf
let (//) = Filename.concat
let gcs = Env.paparazzi_src // "sw/ground_segment/cockpit/gcs"
let regexp_space = Str.regexp "[ ]+"
let string_of_gdkcolor = fun c ->
sprintf "#%04x%04x%04x" (Gdk.Color.red c) (Gdk.Color.green c) (Gdk.Color.blue c)
let aircraft_sample = fun name ac_id ->
Xml.Element ("aircraft",
[ "name", name;
"ac_id", ac_id;
"airframe", "airframes/examples/microjet.xml";
"radio", "radios/cockpitSX.xml";
"telemetry", "telemetry/default_fixedwing.xml";
"flight_plan", "flight_plans/basic.xml";
"settings", "settings/fixedwing_basic.xml";
"settings_modules", "";
"gui_color", "blue" ],
[])
let write_conf_xml = fun ?(user_save = false) () ->
let l = Hashtbl.fold (fun _ a r -> a::r) Utils.aircrafts [] in
let l = List.sort (fun ac1 ac2 -> compare (Xml.attrib ac1 "name") (Xml.attrib ac2 "name")) l in
let c = Xml.Element ("conf", [], l) in
if c <> Xml.parse_file Utils.conf_xml_file then begin
if not (Sys.file_exists Utils.backup_xml_file) then
ignore (Sys.command (sprintf "cp %s %s" Utils.conf_xml_file Utils.backup_xml_file));
let f = open_out Utils.conf_xml_file in
fprintf f "%s\n" (ExtXml.to_string_fmt ~tab_attribs:true c);
close_out f
end;
if user_save && Sys.file_exists Utils.backup_xml_file then begin
let today = Unix.localtime (Unix.gettimeofday ()) in
Sys.rename Utils.backup_xml_file (sprintf "%s.%04d-%02d-%02d_%02d:%02d" Utils.conf_xml_file (1900+today.Unix.tm_year) (today.Unix.tm_mon+1) today.Unix.tm_mday today.Unix.tm_hour today.Unix.tm_min)
end
let new_ac_id = fun () ->
let used = Array.make 256 false in
Hashtbl.iter
(fun _ x ->
used.(int_of_string (ExtXml.attrib x "ac_id")) <- true)
Utils.aircrafts ;
let rec first_unused = fun i ->
if i < 256 then
if not used.(i) then i else first_unused (i+1)
else
failwith "Already 256 A/C in your conf.xml file !" in
first_unused 1
let parse_conf_xml = fun vbox ->
let strings = ref [] in
Hashtbl.iter (fun name _ac -> strings := name :: !strings) Utils.aircrafts;
let compare_ignore_case = fun s1 s2 ->
String.compare (String.lowercase s1) (String.lowercase s2) in
let ordered = List.sort compare_ignore_case ("" :: !strings) in
Gtk_tools.combo ordered vbox
let editor =
try Sys.getenv "EDITOR" with _ -> (
if Os_calls.contains (Os_calls.os_name) "Darwin" then
"open"
else
"gedit"
)
let edit = fun file ->
ignore (Sys.command (sprintf "%s %s&" editor file))
let gcs_or_edit = fun file ->
match GToolbox.question_box ~title:"Flight plan editing" ~default:2 ~buttons:["Text editor"; "GCS"] "Which editor do you want to use ?" with
1 -> edit file
| 2 -> ignore (Sys.command (sprintf "%s -edit '%s'&" gcs file))
| _ -> failwith "Internal error: gcs_or_edit"
type ac_data =
Label of GMisc.label
| Tree of Gtk_tools.tree
let string_of_ac_data = fun d ->
match d with
Label l -> l#text
| Tree t -> Gtk_tools.tree_values t
(* Awful but easier *)
let current_color = ref "white"
let correct_ac_id = fun s ->
try
let n = int_of_string s in
0 < n && n < 256
with
_ -> false
let correct_ac_name = fun s ->
let allowed_char = function
'a'..'z' | 'A'..'Z' | '0'..'9' | '_' -> ()
| _ -> raise Exit in
try
String.iter allowed_char s;
s <> ""
with
Exit -> false
(*TODO function text of date_type*)
let save_callback = fun ?user_save gui ac_combo tree tree_modules () ->
let ac_name = Gtk_tools.combo_value ac_combo
and ac_id = gui#entry_ac_id#text in
if ac_name <> "" && ac_id <> "" then begin
if not (correct_ac_id ac_id) then
GToolbox.message_box ~title:"Error on A/C id" "A/C id must be a non null number less than 255"
else
let color = !current_color in
let aircraft =
Xml.Element ("aircraft",
[ "name", ac_name;
"ac_id", ac_id;
"airframe", gui#label_airframe#text;
"radio", gui#label_radio#text;
"telemetry", gui#label_telemetry#text;
"flight_plan", gui#label_flight_plan#text;
"settings", Gtk_tools.tree_values ~only_checked:false tree;
"settings_modules", Gtk_tools.tree_values ~only_checked:false tree_modules;
"gui_color", color ],
[]) in
begin try Hashtbl.remove Utils.aircrafts ac_name with _ -> () end;
Hashtbl.add Utils.aircrafts ac_name aircraft
end;
write_conf_xml ?user_save ()
(* selected state type *)
type selected_t = Selected | Unselected | Unknown
(* Get the settings (string list) with current modules *)
let get_settings_modules = fun ac_xml settings_modules ->
(* get modules *)
let modules = Gen_common.get_modules_of_airframe ac_xml in
let modules = List.map (fun m -> m.Gen_common.xml, m.Gen_common.file ) modules in
(* get list of settings files *)
let settings = List.fold_left (fun l (m, f) ->
(* get list of settings_file xml node if any *)
let settings_file_list = List.filter (fun t -> Xml.tag t = "settings_file") (Xml.children m) in
let file_list = List.map (fun s -> "settings/"^(Xml.attrib s "name")) settings_file_list in
(* include module file in the list only if it has a 'settings' node *)
let settings_list = List.filter (fun t -> Xml.tag t = "settings") (Xml.children m) in
(*let module_file = if List.length settings_list > 0 then [Env.filter_absolute_path f] else [] in*)
(* include module file with specific name if they exist *)
let settings_list = List.fold_left (fun l s ->
try
let name = Xml.attrib s "name" in
(* test if there is no white space in settings name *)
if Str.string_match (Str.regexp ".* .*") name 0
then failwith "Paparazzicenter: no white space allowed in modules settings name";
l @ [(Env.filter_absolute_path f)^"~"^name^"~"]
with
| Failure x -> prerr_endline x; l @ [Env.filter_absolute_path f]
| _ -> l @ [Env.filter_absolute_path f]
) [] settings_list in
l @ file_list (*@ module_file*) @ settings_list
) [] modules in
(* store current state in a hashtable *)
let current = Hashtbl.create 7 in
let set = Str.split regexp_space settings_modules in
List.iter (fun s ->
let l = String.length s in
if s.[0] == '[' && s.[l - 1] = ']'
then Hashtbl.add current (String.sub s 1 (l - 2)) Unselected
else Hashtbl.add current s Selected
) set;
(* build list with previous state if necessary *)
List.map (fun s ->
(* get previous state, unknonw otherwise (new module, will be selected by default) *)
let checked = try Hashtbl.find current s with _ -> Unknown in
(* add to tree with correct state *)
match checked with
| Selected | Unknown -> s
| Unselected -> ("["^s^"]")
) settings
let first_word = fun s ->
try
let n = String.index s ' ' in
String.sub s 0 n
with
Not_found -> s
(** Test if an element is available for the current target *)
(** Get list of targets of an airframe *)
let get_targets_list = fun ac_xml ->
let firmwares = List.filter (fun x -> ExtXml.tag_is x "firmware") (Xml.children ac_xml) in
let targets = List.map (fun f -> List.filter (fun x -> ExtXml.tag_is x "target") (Xml.children f)) firmwares in
List.flatten targets
(** Parse Airframe File for Targets **)
let parse_ac_targets = fun target_combo ac_file (log:string->unit) ->
(* Clear ComboBox *)
let (store, column) = Gtk_tools.combo_model target_combo in
store#clear ();
(* add targets *)
try
let af_xml = Xml.parse_file (Env.paparazzi_home // "conf" // ac_file) in
let targets = get_targets_list af_xml in
if List.length targets > 0 then
List.iter (fun t -> Gtk_tools.add_to_combo target_combo (Xml.attrib t "name")) targets
else begin
Gtk_tools.add_to_combo target_combo "ap";
Gtk_tools.add_to_combo target_combo "sim"
end;
let combo_box = Gtk_tools.combo_widget target_combo in
combo_box#set_active 0
with _ -> log (sprintf "Error while parsing targets from file %s\n" ac_file)
(* Parse AC file for flash mode *)
let parse_ac_flash = fun target flash_combo ac_file ->
let (store, column) = Gtk_tools.combo_model flash_combo in
store#clear ();
Gtk_tools.add_to_combo flash_combo "Default";
try
let af_xml = Xml.parse_file (Env.paparazzi_home // "conf" // ac_file) in
let targets = get_targets_list af_xml in
let board = Xml.attrib (List.find (fun t -> Xml.attrib t "name" = target) targets) "board" in
(* board names as regexp *)
let flash_modes = ref [] in
Hashtbl.iter (fun b m ->
if Str.string_match (Str.regexp b) board 0 then
flash_modes := !flash_modes @ m;
) (snd CP.flash_modes);
List.iter (fun m -> Gtk_tools.add_to_combo flash_combo m) !flash_modes;
Gtk_tools.select_in_combo flash_combo "Default";
(Gtk_tools.combo_widget flash_combo)#misc#set_sensitive (List.length !flash_modes > 0)
with _ ->
(* not a valid airframe file *)
(Gtk_tools.combo_widget flash_combo)#misc#set_sensitive false
(* Link A/C to airframe & flight_plan labels *)
let ac_combo_handler = fun gui (ac_combo:Gtk_tools.combo) target_combo flash_combo (log:string->unit) ->
(* build tree for settings *)
let tree_set = Gtk_tools.tree ~check_box:true gui#tree_settings in
(* build tree for modules settings *)
let tree_set_mod = Gtk_tools.tree ~check_box:true gui#tree_settings_modules in
(* connect save_callback to the two toggle signals
* it can't be done before because we need the two tree models
*)
let (_, _, _, tree_signal) = Gtk_tools.tree_model tree_set in
ignore (tree_signal#toggled ~callback:(fun _ -> save_callback gui ac_combo tree_set tree_set_mod ()));
let (_, _, _, tree_signal) = Gtk_tools.tree_model tree_set_mod in
ignore (tree_signal#toggled ~callback:(fun _ -> save_callback gui ac_combo tree_set tree_set_mod ()));
(* Link AC conf with labels and buttons *)
let ac_files =
[ "airframe", "airframes", Label gui#label_airframe, Some gui#button_browse_airframe, Some gui#button_edit_airframe, edit, None;
"flight_plan", "flight_plans", Label gui#label_flight_plan, Some gui#button_browse_flight_plan, Some gui#button_edit_flight_plan, gcs_or_edit, None;
"settings", "settings", Tree tree_set, Some gui#button_browse_settings, Some gui#button_edit_settings, edit, Some gui#button_remove_settings;
"settings_modules", "settings", Tree tree_set_mod, None, None, (fun _ -> ()), None;
"radio", "radios", Label gui#label_radio, Some gui#button_browse_radio, Some gui#button_edit_radio, edit, None;
"telemetry", "telemetry", Label gui#label_telemetry, Some gui#button_browse_telemetry, Some gui#button_edit_telemetry, edit, None]
in
(* Update_params callback *)
let update_params = fun ac_name ->
try
let aircraft = Hashtbl.find Utils.aircrafts ac_name in
let sample = aircraft_sample ac_name "42" in
(* update list of modules settings *)
let settings_modules = try
let af_xml = Xml.parse_file (Env.paparazzi_home // "conf" // (Xml.attrib aircraft "airframe")) in
get_settings_modules af_xml (ExtXml.attrib_or_default aircraft "settings_modules" "")
with
| Failure x -> prerr_endline x; []
| _ -> []
in
(* update aicraft hashtable *)
let aircraft = ExtXml.subst_attrib "settings_modules" (String.concat " " settings_modules) aircraft in
begin try Hashtbl.remove Utils.aircrafts ac_name with _ -> () end;
Hashtbl.add Utils.aircrafts ac_name aircraft;
let value = fun a -> try (ExtXml.attrib aircraft a) with _ -> Xml.attrib sample a in
(* update elements *)
List.iter (fun (a, _subdir, label, _, _, _, _) ->
match label with
| Label l -> l#set_text (value a)
| Tree t ->
ignore (Gtk_tools.clear_tree t);
let names = Str.split regexp_space (value a) in
List.iter (Gtk_tools.add_to_tree t) names;
) ac_files;
let ac_id = ExtXml.attrib aircraft "ac_id"
and gui_color = ExtXml.attrib_or_default aircraft "gui_color" "white" in
gui#button_clean#misc#set_sensitive true;
gui#button_build#misc#set_sensitive true;
gui#eventbox_gui_color#misc#modify_bg [`NORMAL, `NAME gui_color];
current_color := gui_color;
gui#entry_ac_id#set_text ac_id;
(Gtk_tools.combo_widget target_combo)#misc#set_sensitive true;
(Gtk_tools.combo_widget flash_combo)#misc#set_sensitive true;
parse_ac_targets target_combo (ExtXml.attrib aircraft "airframe") log;
parse_ac_flash (Gtk_tools.combo_value target_combo) flash_combo (ExtXml.attrib aircraft "airframe");
with
Not_found ->
gui#label_airframe#set_text "";
gui#label_flight_plan#set_text "";
gui#button_clean#misc#set_sensitive false;
gui#button_build#misc#set_sensitive false;
(Gtk_tools.combo_widget target_combo)#misc#set_sensitive false;
(Gtk_tools.combo_widget flash_combo)#misc#set_sensitive false
in
Gtk_tools.combo_connect ac_combo update_params;
(* New A/C button *)
let callback = fun _ ->
match GToolbox.input_string ~title:"New A/C" ~text:"MYAC" "New A/C name ?" with
None -> ()
| Some s ->
if not (correct_ac_name s) then
GToolbox.message_box ~title:"Error on A/C nae" "A/C name must contain only letters, digits or underscores"
else begin
Gtk_tools.add_to_combo ac_combo s;
let a = aircraft_sample s (string_of_int (new_ac_id ())) in
Hashtbl.add Utils.aircrafts s a;
update_params s
end
in
ignore (gui#menu_item_new_ac#connect#activate ~callback);
(* Delete A/C *)
let callback = fun _ ->
let ac_name = Gtk_tools.combo_value ac_combo in
if ac_name <> "" then
match GToolbox.question_box ~title:"Delete A/C" ~buttons:["Cancel"; "Delete"] ~default:2 (sprintf "Delete %s ? (no undo after Save)" ac_name) with
2 -> begin
begin try Hashtbl.remove Utils.aircrafts ac_name with _ -> () end;
let combo_box = Gtk_tools.combo_widget ac_combo in
match combo_box#active_iter with
| None -> ()
| Some row ->
let (store, _column) = Gtk_tools.combo_model ac_combo in
ignore (store#remove row);
combo_box#set_active 1
end
| _ -> ()
in
ignore (gui#delete_ac_menu_item#connect#activate ~callback);
(* New Target button *)
let callback = fun _ ->
match GToolbox.input_string ~title:"New Target" ~text:"tunnel" "New build target ?" with
None -> ()
| Some s ->
let (store, column) = Gtk_tools.combo_model target_combo in
let row = store#append () in
store#set ~row ~column s;
(Gtk_tools.combo_widget target_combo)#set_active_iter (Some row)
in
ignore (gui#menu_item_new_target#connect#activate ~callback);
(* GUI color *)
let callback = fun _ ->
let csd = GWindow.color_selection_dialog ~show:true () in
let callback = fun _ ->
let colorname = string_of_gdkcolor csd#colorsel#color in
gui#eventbox_gui_color#misc#modify_bg [`NORMAL, `NAME colorname];
current_color := colorname;
save_callback gui ac_combo tree_set tree_set_mod ();
csd#destroy () in
ignore (csd#ok_button#connect#clicked ~callback);
ignore (csd#cancel_button#connect#clicked ~callback:csd#destroy) in
ignore(gui#button_gui_color#connect#clicked ~callback);
(* A/C id *)
ignore(gui#entry_ac_id#connect#changed ~callback:(fun () -> save_callback gui ac_combo tree_set tree_set_mod ()));
(* refresh button *)
ignore(gui#button_refresh#connect#clicked ~callback:(fun () -> update_params (Gtk_tools.combo_value ac_combo)));
(* Conf *)
List.iter (fun (name, subdir, label, button_browse, button_edit, editor, button_remove) ->
(* editor button callback *)
let callback = fun _ ->
let rel_files = match label with
Label l -> Str.split regexp_space l#text
| Tree t -> Str.split regexp_space (Gtk_tools.tree_values ~only_checked:true t)
in
let abs_files = List.map (Filename.concat Utils.conf_dir) rel_files in
let quoted_files = List.map (fun s -> "'"^s^"'") abs_files in
let arg = String.concat " " quoted_files in
editor arg in
(* connect editor button *)
ignore (match button_edit with Some e -> ignore(e#connect#clicked ~callback) | _ -> ());
(* browse button callback *)
let callback = fun _ ->
let cb = fun names ->
ignore (match label with
Label l ->
let names = String.concat " " names in
l#set_text names
| Tree t ->
List.iter (Gtk_tools.add_to_tree t) names
);
save_callback gui ac_combo tree_set tree_set_mod ();
let ac_name = Gtk_tools.combo_value ac_combo in
update_params ac_name
in
Utils.choose_xml_file name subdir cb in
(* connect browse button *)
ignore (match button_browse with Some b -> ignore(b#connect#clicked ~callback) | _ -> ());
(* remove button callback *)
let callback = fun _ ->
match label with
Tree t ->
Gtk_tools.remove_selected_from_tree t;
save_callback gui ac_combo tree_set tree_set_mod ()
| _ -> ()
in
(* connect remove button *)
ignore (match button_remove with Some r -> ignore(r#connect#clicked ~callback) | _ -> ())
)
ac_files;
(* Save button *)
ignore(gui#menu_item_save_ac#connect#activate ~callback:(save_callback ~user_save:true gui ac_combo tree_set tree_set_mod))
let build_handler = fun ~file gui ac_combo (target_combo:Gtk_tools.combo) (flash_combo:Gtk_tools.combo) (log:string->unit) ->
(* Link target to upload button *)
Gtk_tools.combo_connect target_combo
(fun target ->
let ac_name = Gtk_tools.combo_value ac_combo in
let aircraft = Hashtbl.find Utils.aircrafts ac_name in
parse_ac_flash (Gtk_tools.combo_value target_combo) flash_combo (ExtXml.attrib aircraft "airframe");
(* if target is sim or nps, deactivate the upload button *)
gui#button_upload#misc#set_sensitive (target <> "sim" && target <> "nps"));
(* Clean button *)
let callback = fun () ->
Utils.command ~file gui log (Gtk_tools.combo_value ac_combo) "clean_ac" in
ignore (gui#button_clean#connect#clicked ~callback);
(* Build button *)
let callback = fun () ->
try (
let ac_name = Gtk_tools.combo_value ac_combo
and target = Gtk_tools.combo_value target_combo
and config = if gui#checkbutton_printconfig#active then "PRINT_CONFIG=1 " else "" in
let target_cmd = sprintf "%s%s.compile" config target in
gui#button_build#misc#set_sensitive false;
gui#button_upload#misc#set_sensitive false;
let finished_callback = fun () ->
gui#button_build#misc#set_sensitive true;
gui#button_upload#misc#set_sensitive true in
Utils.command ~file ~finished_callback gui log ac_name target_cmd
) with _ -> log "ERROR: Nothing to build!!!\n" in
ignore (gui#button_build#connect#clicked ~callback);
(* Upload button *)
let callback = fun () ->
let ac_name = Gtk_tools.combo_value ac_combo
and target = Gtk_tools.combo_value target_combo
and flash = Gtk_tools.combo_value flash_combo
and config = if gui#checkbutton_printconfig#active then "PRINT_CONFIG=1 " else "" in
let options = try Hashtbl.find (fst CP.flash_modes) flash with _ -> "" in
let target_cmd = sprintf "%s%s %s.upload" config options target in
gui#button_build#misc#set_sensitive false;
gui#button_upload#misc#set_sensitive false;
let finished_callback = fun () ->
gui#button_build#misc#set_sensitive true;
gui#button_upload#misc#set_sensitive true in
Utils.command ~file ~finished_callback gui log ac_name target_cmd in
ignore (gui#button_upload#connect#clicked ~callback)