diff --git a/sw/ground_segment/cockpit/gcs.ml b/sw/ground_segment/cockpit/gcs.ml index 051a8c2ba6..ee829f71d8 100644 --- a/sw/ground_segment/cockpit/gcs.ml +++ b/sw/ground_segment/cockpit/gcs.ml @@ -508,7 +508,6 @@ let _main = (** Aircraft notebook *) let ac_notebook = GPack.notebook ~tab_border:0 () in - ac_notebook#connect#switch_page ~callback:(fun i -> Printf.printf "tab=%d -> %d\n%!" ac_notebook#current_page i); (** Alerts text frame *) let alert_page = GBin.frame () in @@ -573,25 +572,8 @@ let _main = ignore (frame#event#connect#button_press ~callback) end; - - (** Periodically probe new A/Cs *) - ignore (Glib.Timeout.add 2000 (fun () -> Live.message_request "map2d" "AIRCRAFTS" [] (fun _sender vs -> Live.aircrafts_msg my_alert geomap ac_notebook vs); false)); - - (** New aircraft message *) - Live.safe_bind "NEW_AIRCRAFT" (fun _sender vs -> Live.one_new_ac my_alert geomap ac_notebook (Pprz.string_assoc "ac_id" vs)); - - (** Listen for all messages on ivy *) - Live.listen_flight_params geomap !auto_center_new_ac my_alert; - Live.listen_wind_msg geomap; - Live.listen_fbw_msg (); - Live.listen_engine_status_msg (); - Live.listen_if_calib_msg (); - Live.listen_waypoint_moved (); - Live.listen_infrared (); - Live.listen_svsinfo (); - Live.listen_telemetry_status (); - Live.listen_alert my_alert; - Live.listen_error my_alert; + (** Wait for A/Cs and subsequent messages *) + Live.listen_acs_and_msgs geomap ac_notebook my_alert !auto_center_new_ac; (** Display the window *) let accel_group = menu_fact#accel_group in diff --git a/sw/ground_segment/cockpit/live.ml b/sw/ground_segment/cockpit/live.ml index c107eacac0..bd4430bf66 100644 --- a/sw/ground_segment/cockpit/live.ml +++ b/sw/ground_segment/cockpit/live.ml @@ -69,6 +69,8 @@ type aircraft = { misc_page : Pages.misc; dl_settings_page : Pages.settings option; rc_settings_page : Pages.rc_settings option; + pages : GObj.widget; + notebook_label : GMisc.label; strip : Strip.t; mutable first_pos : bool; mutable last_block_name : string; @@ -83,10 +85,26 @@ type aircraft = { } let live_aircrafts = Hashtbl.create 3 +let active_ac = ref "" let get_ac = fun vs -> let ac_id = Pprz.string_assoc "ac_id" vs in Hashtbl.find live_aircrafts ac_id +let select_ac = fun ?(switch_notebook = true) acs_notebook ac_id -> + if !active_ac <> ac_id then + let ac = Hashtbl.find live_aircrafts ac_id in + ac.notebook_label#set_width_chars 20; + ac.strip#show_buttons (); + if !active_ac <> "" then begin + let ac' = Hashtbl.find live_aircrafts !active_ac in + ac'.strip#hide_buttons (); + ac'.notebook_label#set_width_chars (String.length ac'.notebook_label#text) + end; + active_ac := ac_id; + if switch_notebook then + let n = acs_notebook#page_num ac.pages in + acs_notebook#goto_page n + module M = Map.Make (struct type t = string let compare = compare end) let log = @@ -298,7 +316,6 @@ let create_ac = fun alert (geomap:G.widget) (acs_notebook:GPack.notebook) (ac_id let eb = GBin.event_box () in let _label = GMisc.label ~text:name ~packing:eb#add () in eb#coerce#misc#modify_bg [`NORMAL, `NAME color;`ACTIVE, `NAME color]; - _label#set_width_chars 20; (** Put a notebook for this A/C *) let ac_frame = GBin.frame ~packing:(acs_notebook#append_page ~tab_label:eb#coerce) () in @@ -306,16 +323,9 @@ let create_ac = fun alert (geomap:G.widget) (acs_notebook:GPack.notebook) (ac_id let visible = fun w -> ac_notebook#page_num w#coerce = ac_notebook#current_page in - (** Add a strip and connect it to the A/C notebook *) - let select_this_tab = - let n = acs_notebook#page_num ac_frame#coerce in - fun () -> acs_notebook#goto_page n in + (** Add a strip *) let strip = Strip.add config color center_ac (mark geomap ac_id track !Plugin.frame) in - let deselect_others = fun () -> - Hashtbl.iter (fun ac_id' ac -> if ac_id' <> ac_id then ac.strip#hide_buttons ()) live_aircrafts in - strip#connect (fun () -> select_this_tab (); deselect_others ()); - deselect_others (); - + strip#connect (fun () -> select_ac acs_notebook ac_id); (** Build the XML flight plan, connect then "jump_to_block" *) let fp_xml = ExtXml.child fp_xml_dump "flight_plan" in @@ -426,8 +436,12 @@ let create_ac = fun alert (geomap:G.widget) (acs_notebook:GPack.notebook) (ac_id last_block_name = ""; alt = 0.; target_alt = 0.; in_kill_mode = false; speed = 0.; wind_dir = 42.; ground_prox = true; - wind_speed = 0.; } in + wind_speed = 0.; + pages = ac_frame#coerce; + notebook_label = _label + } in Hashtbl.add live_aircrafts ac_id ac; + select_ac acs_notebook ac_id; (** Periodically send the wind estimation through a WIND_INFO message packed into a RAW_DATALINK *) @@ -816,3 +830,34 @@ let listen_error = fun a -> let msg = Pprz.string_assoc "message" vs in log_and_say a "gcs" msg in safe_bind "TELEMETRY_ERROR" get_error + + +let listen_acs_and_msgs = fun geomap ac_notebook my_alert auto_center_new_ac -> + (** Periodically probe new A/Cs *) + ignore (Glib.Timeout.add 2000 (fun () -> message_request "map2d" "AIRCRAFTS" [] (fun _sender vs -> aircrafts_msg my_alert geomap ac_notebook vs); false)); + + (** New aircraft message *) + safe_bind "NEW_AIRCRAFT" (fun _sender vs -> one_new_ac my_alert geomap ac_notebook (Pprz.string_assoc "ac_id" vs)); + + (** Listen for all messages on ivy *) + listen_flight_params geomap auto_center_new_ac my_alert; + listen_wind_msg geomap; + listen_fbw_msg (); + listen_engine_status_msg (); + listen_if_calib_msg (); + listen_waypoint_moved (); + listen_infrared (); + listen_svsinfo (); + listen_telemetry_status (); + listen_alert my_alert; + listen_error my_alert; + + (** Select the active aircraft on notebook page selection *) + let callback = fun i -> + let ac_page = ac_notebook#get_nth_page i in + Hashtbl.iter + (fun ac_id ac -> + if ac.pages#get_oid = ac_page#get_oid + then select_ac ~switch_notebook:false ac_notebook ac_id) + live_aircrafts in + ignore (ac_notebook#connect#switch_page ~callback) diff --git a/sw/ground_segment/cockpit/live.mli b/sw/ground_segment/cockpit/live.mli index e5d1607943..64ef6c8410 100644 --- a/sw/ground_segment/cockpit/live.mli +++ b/sw/ground_segment/cockpit/live.mli @@ -1,17 +1 @@ -val message_request : string -> string -> Pprz.values -> (string -> Pprz.values -> unit) -> unit - -val aircrafts_msg : Pages.alert -> MapCanvas.widget -> GPack.notebook -> Pprz.values -> unit -val safe_bind : string -> (string -> Pprz.values -> unit) -> unit -val one_new_ac : Pages.alert -> MapCanvas.widget -> GPack.notebook -> string -> unit -val listen_flight_params : - < center : MapCanvas.LL.geographic -> unit; .. > -> bool -> Pages.alert -> unit -val listen_wind_msg : MapCanvas.widget -> unit -val listen_fbw_msg : unit -> unit -val listen_engine_status_msg : unit -> unit -val listen_if_calib_msg : unit -> unit -val listen_waypoint_moved : unit -> unit -val listen_infrared : unit -> unit -val listen_svsinfo : unit -> unit -val listen_alert : Pages.alert -> unit -val listen_error : Pages.alert -> unit -val listen_telemetry_status : unit -> unit +val listen_acs_and_msgs : MapCanvas.widget -> GPack.notebook -> Pages.alert -> bool -> unit diff --git a/sw/ground_segment/cockpit/strip.ml b/sw/ground_segment/cockpit/strip.ml index cbdc548bfd..acb01fddec 100644 --- a/sw/ground_segment/cockpit/strip.ml +++ b/sw/ground_segment/cockpit/strip.ml @@ -31,7 +31,9 @@ type t = set_bat : float -> unit; set_color : string -> string -> unit; set_label : string -> string -> unit; - connect : (unit -> unit) -> unit; hide_buttons : unit -> unit > + connect : (unit -> unit) -> unit; + hide_buttons : unit -> unit; + show_buttons : unit -> unit > let bat_max = 12.5 let bat_min = 9. @@ -66,44 +68,45 @@ class gauge = fun ?(color="green") ?(history_len=50) gauge v_min v_max -> val mutable history_index = -1 method set = fun value string -> let {Gtk.width=width; height=height} = gauge#misc#allocation in - let dr = GDraw.pixmap ~width ~height ~window:gauge () in - dr#set_foreground (`NAME "orange"); - dr#rectangle ~x:0 ~y:0 ~width ~height ~filled:true (); - - let f = (value -. v_min) /. (v_max -. v_min) in - let f = max 0. (min 1. f) in - let h = truncate (float height *. f) in - - (* First call: fill the array with the given value *) - if history_index < 0 then begin + if height > 1 then (* Else the drawing area is not allocated already *) + let dr = GDraw.pixmap ~width ~height ~window:gauge () in + dr#set_foreground (`NAME "orange"); + dr#rectangle ~x:0 ~y:0 ~width ~height ~filled:true (); + + let f = (value -. v_min) /. (v_max -. v_min) in + let f = max 0. (min 1. f) in + let h = truncate (float height *. f) in + + (* First call: fill the array with the given value *) + if history_index < 0 then begin + for i = 0 to history_len - 1 do + history.(i) <- h + done; + history_index <- 0; + end; + + (* Store the value in the history array and update index *) + history.(history_index) <- h; + history_index <- (history_index+1) mod history_len; + + dr#set_foreground (`NAME color); + + (* From left to right, older to new values *) + let polygon = ref [0,height; width,height] in for i = 0 to history_len - 1 do - history.(i) <- h + let idx = (history_index+i) mod history_len in + polygon := ((i*width)/history_len, (height-history.(idx))):: !polygon; done; - history_index <- 0; - end; - - (* Store the value in the history array and update index *) - history.(history_index) <- h; - history_index <- (history_index+1) mod history_len; - - dr#set_foreground (`NAME color); - - (* From left to right, older to new values *) - let polygon = ref [0,height; width,height] in - for i = 0 to history_len - 1 do - let idx = (history_index+i) mod history_len in - polygon := ((i*width)/history_len, (height-history.(idx))):: !polygon; - done; - polygon := (width,height-h):: !polygon; - dr#polygon ~filled:true !polygon; - - let context = gauge#misc#create_pango_context in - let layout = context#create_layout in - Pango.Layout.set_text layout string; - let (w,h) = Pango.Layout.get_pixel_size layout in - dr#put_layout ~x:((width-w)/2) ~y:((height-h)/2) ~fore:`BLACK layout; - - (new GDraw.drawable gauge#misc#window)#put_pixmap ~x:0 ~y:0 dr#pixmap + polygon := (width,height-h):: !polygon; + dr#polygon ~filled:true !polygon; + + let context = gauge#misc#create_pango_context in + let layout = context#create_layout in + Pango.Layout.set_text layout string; + let (w,h) = Pango.Layout.get_pixel_size layout in + dr#put_layout ~x:((width-w)/2) ~y:((height-h)/2) ~fore:`BLACK layout; + + (new GDraw.drawable gauge#misc#window)#put_pixmap ~x:0 ~y:0 dr#pixmap end @@ -160,8 +163,10 @@ let add config color center_ac mark = (** Table (everything except the user buttons) *) let strip = GPack.table ~rows ~columns ~col_spacings:3 ~packing:framevb#add () in - strip#set_row_spacing 0 3; - strip#set_row_spacing (rows-2) 3; + strip#set_row_spacing 0 2; + strip#set_row_spacing 1 2; + strip#set_row_spacing (rows-1) 2; + strip#set_row_spacing (rows-2) 2; (* Name in top left *) let name = (GMisc.label ~text: (ac_name) ~packing: (strip#attach ~top: 0 ~left: 0) ()) in @@ -187,13 +192,13 @@ let add config color center_ac mark = tooltips#set_tip plane_color#coerce ~text:"Flight time - Block time - Stage time - Block name"; (* battery gauge *) - let bat_da = GMisc.drawing_area ~height:60 ~show:true ~packing:(strip#attach ~top:1 ~bottom:(rows-1) ~left:0) () in + let bat_da = GMisc.drawing_area ~show:true ~packing:(strip#attach ~top:1 ~bottom:(rows-1) ~left:0) () in bat_da#misc#realize (); let bat = new gauge bat_da bat_min bat_max in (* AGL gauge *) let agl_box = GBin.event_box ~packing:(strip#attach ~top:1 ~bottom:3 ~left:(columns-1)) () in - let agl_da = GMisc.drawing_area ~width:40 ~height:60 ~show:true ~packing:agl_box#add () in + let agl_da = GMisc.drawing_area ~width:30 ~show:true ~packing:agl_box#add () in agl_da#misc#realize (); tooltips#set_tip agl_box#coerce ~text:"AGL (m)"; let agl = new gauge agl_da 0. agl_max in @@ -225,7 +230,7 @@ let add config color center_ac mark = ) labels_name; (* Buttons *) - let hbox = GPack.hbox ~packing:framevb#add () in + let hbox = GPack.hbox ~spacing:2 ~packing:framevb#add () in let b = GButton.button ~label:"Center A/C" ~packing:hbox#add () in ignore(b#connect#clicked ~callback:center_ac); let b = GButton.button ~label:"Mark" ~packing:hbox#add () in @@ -237,9 +242,9 @@ let add config color center_ac mark = ignore (b#connect#clicked ~callback:mark); (* User buttons *) - let user_hbox = GPack.hbox ~packing:framevb#add () in + let user_hbox = GPack.hbox ~spacing:2 ~packing:framevb#add () in - (object + object method set_agl value = set_agl agl value method set_bat value = set_bat bat value method set_label name value = set_label !strip_labels name value @@ -250,10 +255,8 @@ let add config color center_ac mark = ignore (plus30#connect#clicked (fun () -> callback 30.)); ignore (minus5#connect#clicked (fun () -> callback (-5.))) method hide_buttons () = hbox#misc#hide (); user_hbox#misc#hide () + method show_buttons () = hbox#misc#show (); user_hbox#misc#show () method connect = fun (select: unit -> unit) -> - let callback = fun _ -> - select (); - hbox#misc#show (); user_hbox#misc#show (); - true in + let callback = fun _ -> select (); true in ignore (strip_ebox#event#connect#button_press ~callback) - end:t) + end diff --git a/sw/ground_segment/cockpit/strip.mli b/sw/ground_segment/cockpit/strip.mli index d774a8fdb7..d59d04a30b 100644 --- a/sw/ground_segment/cockpit/strip.mli +++ b/sw/ground_segment/cockpit/strip.mli @@ -1,12 +1,14 @@ -type t = - < add_widget : GObj.widget -> unit; - connect_shift_alt : (float -> unit) -> unit; - set_agl : float -> unit; - set_bat : float -> unit; - set_color : string -> string -> unit; - set_label : string -> string -> unit; - hide_buttons : unit -> unit; - connect : (unit -> unit) -> unit> +type t = < + add_widget : GObj.widget -> unit; + connect_shift_alt : (float -> unit) -> unit; + set_agl : float -> unit; + set_bat : float -> unit; + set_color : string -> string -> unit; + set_label : string -> string -> unit; + hide_buttons : unit -> unit; + show_buttons : unit -> unit; + connect : (unit -> unit) -> unit +> val scrolled : GBin.scrolled_window