From 00fd74dbfcb51409553d170e1bb29939c414a07c Mon Sep 17 00:00:00 2001 From: Erwan Jahier <erwan.jahier@univ-grenoble-alpes.fr> Date: Sun, 8 Jan 2023 16:50:53 +0100 Subject: [PATCH] fix: various fixes and improvements in rdbgui4sasa --- test/dijkstra-ring/my-rdbg-tuning.ml | 5 - tools/rdbg4sasa/gtkgui.ml | 702 +++++++++++++++------------ tools/rdbg4sasa/sasa-rdbg-cmds.ml | 29 +- 3 files changed, 412 insertions(+), 324 deletions(-) delete mode 100644 test/dijkstra-ring/my-rdbg-tuning.ml diff --git a/test/dijkstra-ring/my-rdbg-tuning.ml b/test/dijkstra-ring/my-rdbg-tuning.ml deleted file mode 100644 index 2839202f..00000000 --- a/test/dijkstra-ring/my-rdbg-tuning.ml +++ /dev/null @@ -1,5 +0,0 @@ - (* *) -#use "rdbg-cmds.ml";; -#use "sasa-rdbg-cmds.ml";; - - diff --git a/tools/rdbg4sasa/gtkgui.ml b/tools/rdbg4sasa/gtkgui.ml index 8cffd03a..d5079d04 100644 --- a/tools/rdbg4sasa/gtkgui.ml +++ b/tools/rdbg4sasa/gtkgui.ml @@ -1,4 +1,4 @@ -(* Time-stamp: <modified the 04/01/2023 (at 11:56) by Erwan Jahier> *) +(* Time-stamp: <modified the 08/01/2023 (at 16:29) by Erwan Jahier> *) #thread #require "lablgtk3" @@ -52,10 +52,48 @@ let rdbg_nodes_enabled e = in last::res +(**********************************************************************************) type daemon_kind = Distributed | Synchronous | Central | LocCentral | ManualCentral | Manual let daemon_kind = ref ManualCentral +type daemon_mode = Internal (* sasa with internal daemon *) | GuiAuto | GuiManual +(* Possible way of using rdbgui4sasa w.r.t. daemon modes: +|-------+----------+---------+-----------| +| | Internal | GuiAuto | GuiManual | +|-------+----------+---------+-----------| +| sasa | yes | yes | yes | +| salut | | yes | yes | +|-------+----------+---------+-----------| +*) + +let daemon_mode () = + if not custom_mode then Internal else + match !daemon_kind with + | Distributed | Synchronous | Central | LocCentral -> GuiAuto + | ManualCentral | Manual -> GuiManual + +let gui_mode = custom_mode (* a better name? *) + +let check_point_event e = + (* Depending on the way rdbgui4sasa is used, some kinds of events + make more sense as checkpoint candidate. Moreover, it is better to have only + one checkpoint per step. + *) + match daemon_mode () with + | Internal -> e.kind = Ltop + (* | GuiAuto -> e.kind = Ltop *) + | GuiAuto -> e.kind = Call && e.name = "mv_hook" + | GuiManual -> e.kind = Call && e.name = "mv_hook" + +let _ = check_ref := fun e -> e.nb = 1 || (check_point_event e) && round e;; + +(* modify the one in rdbg-cmd.ml to inhibit it when the undo is + impossible: in manual modes, it would require to store all the + user manual inputs to replay the simulation from the last + checkpoint (because of the PRGs) *) +let store i = if daemon_mode() = GuiManual then () else store i + let refresh_fun_tbl = Hashtbl.create 1 let _ = Hashtbl.add refresh_fun_tbl "update dot" d let refresh () = @@ -118,28 +156,23 @@ let display_event b = blue_add b#buffer "----------------------------------------\n"; blue_add b#buffer (str_of_sasa_event false !e) -(**********************************************************************************) -let custom_mode_ref = ref custom_mode (* *) -let goto_hook_call e = - if !custom_mode_ref then + +let goto_hook_call e = + if gui_mode then next_cond e (fun e -> e.name = "mv_hook" && e.kind = Call) else e let goto_hook_exit e = - if !custom_mode_ref then + if gui_mode then next_cond e (fun e -> e.name = "mv_hook" && e.kind = Exit) else e -let goto_top e = - if !custom_mode_ref then - next_cond e (fun e -> e.kind = Ltop) - else - e +let goto_top e = next_cond e (fun e -> e.kind = Ltop) let init_rdbg_hook () = let guidaemon sl = @@ -190,18 +223,18 @@ let init_rdbg_hook () = in match !rdbg_mv_hook with | None -> rdbg_mv_hook := Some guidaemon - | _ -> () (* custom_mode_ref := false *) + | _ -> () let set_tooltip b = b#misc#set_tooltip_text let start () = - if !custom_mode_ref then init_rdbg_hook (); + if gui_mode then init_rdbg_hook (); if args.salut_mode then (* In this mode, the hook plays first to provide fake values to sasa but the hook does not need input at this first step *) e:=next_cond_gen !e (fun e -> e.name="mv_hook" && e.kind=Exit) (fun e -> e.next()); - if !custom_mode_ref then - e:=next_cond_gen !e (fun e -> e.name="mv_hook" && e.kind=Call) (fun e -> e.next()) + if gui_mode then + e:=next_cond_gen !e check_point_event (fun e -> e.next()) else (* internal daemon mode *) e:=next_cond_gen !e (fun e -> e.kind=Ltop) (fun e -> e.next()); @@ -210,29 +243,100 @@ let start () = !e.save_state !e.nb let restart p _ = - !e.RdbgEvent.reset(); Seed.replay_seed := true; let seed = Seed.get dotfile in Seed.set seed; p (Printf.sprintf "Restarting using the seed %d" seed); + !e.RdbgEvent.reset(); (* should be done after the see reset! *) Round.reinit(); e:=RdbgStdLib.run ~call_hooks:true (); start (); d() +let counter_map = Hashtbl.create 0 -let custom_daemon p gtext vbox step_button back_step_button round_button - legitimate_button undo_button = - +let update_daemongui_tbl e = + (* In rdbgui automatic modes (ie, not in Manual nor CentralManual + nor internal sasa daemons): + - looks who is enabled, + - chooses whom to activate, and + - sets the tbl shared with the hook function + + Should therefore be used at events where the enabled process info is available + + nb: do side-effects via PRGs via Daemons + *) + assert (gui_mode); + let rec get_higher_priority nl = + let prio n = + let counter = try Hashtbl.find counter_map n with Not_found -> assert false in + counter#get + in + let rec aux p acc = function + | [] -> acc + | (n, false)::t -> aux p acc t + | (n, true)::t -> + let pn = prio n in + if p > pn then aux p acc t else + if p = pn then aux p (n::acc) t else + aux pn [n] t + in + aux 0 [] nl + in + let nodes_enabled = rdbg_nodes_enabled e in + let nodes = List.filter (fun (_,b) -> b) nodes_enabled in + let nodes = get_higher_priority nodes in + (* p ("==> gtkgui: CALL =" ^ (string_of_event e)); *) + (match !daemon_kind with + | Distributed -> ( + let nodes = List.map (fun x -> [x]) nodes in + let to_activate = Daemon.distributed nodes in (* PRGs inside! *) + Hashtbl.clear daemongui_activate; + List.iter (fun n -> Hashtbl.replace daemongui_activate n true) to_activate + ) + | Synchronous -> ( + Hashtbl.clear daemongui_activate; + List.iter (fun n -> Hashtbl.replace daemongui_activate n true) nodes + ) + | Central -> ( + let nodes = List.map (fun x -> [x]) nodes in + let to_activate = Daemon.central nodes in + Hashtbl.clear daemongui_activate; + List.iter (fun n -> + (* Printf.printf "Activating %s\n" n; *) + Hashtbl.replace daemongui_activate n true) to_activate + ) + | LocCentral -> ( + let get_neigbors x = + let pred = snd (List.split (topology.pred x)) in + let succ = topology.succ x in + let res = List.fold_left + (fun acc x -> if List.mem x acc then acc else x::acc) succ pred + in + (* p (Printf.sprintf "voisins(%s)=%s\n" x (String.concat "," res)); *) + res + in + let nodes = List.map (fun x -> [x, get_neigbors x]) nodes in + let to_activate = Daemon.locally_central nodes in + Hashtbl.clear daemongui_activate; + List.iter (fun n -> Hashtbl.replace daemongui_activate n true) to_activate + ) + | ManualCentral -> () (* SNO; the step is done in pushbox callbacks *) + | Manual -> () + ) + +(* redraws the gui buttons a each move, to show only buttons related to enabled nodes *) +let draw_manual_daemons_buttons p gtext vbox step_button back_step_button round_button + legitimate_button undo_button = let daemon_box = GPack.hbox ~packing:vbox#add () ~homogeneous:true ~height:15 in - let dk_dd = GButton.radio_button ~active:(!daemon_kind=Distributed) - ~label:"Distributed" ~packing:daemon_box#add () + let dk_cd = GButton.radio_button ~active:(!daemon_kind=Central) + ~label:"Central" ~packing:daemon_box#add () in let make_but act lbl = GButton.radio_button ~active:act ~label:lbl - ~group:dk_dd#group ~packing:daemon_box#add () + ~group:dk_cd#group ~packing:daemon_box#add () in - let dk_cd = make_but (!daemon_kind=Central) "Central" in let dk_lcd = make_but (!daemon_kind=LocCentral) "Locally Central" in + let dk_dd = make_but (!daemon_kind=Distributed) "Distributed" in let dk_sd = make_but (!daemon_kind=Synchronous) "Synchronous" in let dk_manual = make_but (!daemon_kind=Manual) "Manual" in let dk_manual_central = make_but (!daemon_kind=ManualCentral) "Manual Central" in @@ -246,25 +350,25 @@ let custom_daemon p gtext vbox step_button back_step_button round_button blue_add gtext#buffer (str_of_sasa_event false !e); d(); let nodes_enabled = rdbg_nodes_enabled !e in - + let nodes = fst (List.split nodes_enabled) in let update_rdbg_hook node activate = (match !daemon_kind with | Distributed | Synchronous | Central | LocCentral -> - assert false (* SNO *) + assert false (* SNO *) | ManualCentral -> ( - let txt = Printf.sprintf "ManualCentral step: %s\n\n%s" - node (str_of_sasa_event false !e) in - (* gtext#buffer#set_text txt; *) - blue gtext#buffer txt; - Hashtbl.filter_map_inplace (fun n _prev_status -> Some (n = node)) daemongui_activate; - ) + let txt = Printf.sprintf "ManualCentral step: %s\n\n%s" + node (str_of_sasa_event false !e) in + (* gtext#buffer#set_text txt; *) + blue gtext#buffer txt; + List.iter (fun n -> Hashtbl.add daemongui_activate n (n = node)) nodes; + (* Hashtbl.filter_map_inplace (fun n _prev_status -> Some (n = node)) daemongui_activate; *) + ) | Manual -> let txt = Printf.sprintf "Set %s to %b\n" node activate in - blue_add gtext#buffer txt; - Hashtbl.replace daemongui_activate node activate + blue_add gtext#buffer txt; + Hashtbl.replace daemongui_activate node activate ); in - (* 1 case par noeud : activer/pas activer *) (* NB : lablgtk3 ne propose pas le FlowBox (pourtant dispo dans GTK >= 3.12) *) let n = List.length nodes_enabled in @@ -275,13 +379,12 @@ let custom_daemon p gtext vbox step_button back_step_button round_button let i = ref 0 in let checkbox_grid = GPack.vbox ~packing:vbox#add () in let checkbox_scrolled_grid = GBin.scrolled_window ~border_width:10 ~hpolicy:`AUTOMATIC - ~vpolicy:`AUTOMATIC - ~height:300 - ~shadow_type:`OUT ~packing:checkbox_grid#add () + ~vpolicy:`AUTOMATIC + ~height:300 + ~shadow_type:`OUT ~packing:checkbox_grid#add () in let checkbox_scrolled_grid_box = GPack.vbox ~packing:checkbox_scrolled_grid#add () in let checkbox_line = GPack.hbox ~packing:checkbox_scrolled_grid_box#add () in - let checkbox_line_ref = ref checkbox_line in let checkbox_map = Hashtbl.create n in List.iter (fun (name, enabled) -> @@ -306,9 +409,9 @@ let custom_daemon p gtext vbox step_button back_step_button round_button (* Des boutons pour le mode Manuel Central *) let pushbox_grid = GPack.vbox ~packing:vbox#add () ~homogeneous:true in let pushbox_scrolled_grid = GBin.scrolled_window ~border_width:10 ~hpolicy:`AUTOMATIC - ~vpolicy:`AUTOMATIC - ~height:300 - ~shadow_type:`OUT ~packing:pushbox_grid#add () + ~vpolicy:`AUTOMATIC + ~height:300 + ~shadow_type:`OUT ~packing:pushbox_grid#add () in let pushbox_scrolled_grid_box = GPack.vbox ~homogeneous:true ~packing:pushbox_scrolled_grid#add () @@ -330,13 +433,13 @@ let custom_daemon p gtext vbox step_button back_step_button round_button (* Quand on appuie, met à jour le rdbg_mv_hook *) ignore(pushbox#event#connect#button_press ~callback: (fun _ -> - update_rdbg_hook name true; - e := goto_hook_exit !e; - e := goto_hook_call !e; - display_event gtext; - store !e.nb; - refresh (); - false)); + update_rdbg_hook name true; + e := goto_hook_exit !e; + e := goto_hook_call !e; + display_event gtext; + store !e.nb; + refresh (); + false)); Hashtbl.add pushbox_map name pushbox ) nodes_enabled; @@ -344,13 +447,12 @@ let custom_daemon p gtext vbox step_button back_step_button round_button (* Des compteurs pour les modes automatiques *) let counter_grid = GPack.vbox ~packing:vbox#add () in let counter_scrolled_grid = GBin.scrolled_window ~border_width:10 ~hpolicy:`AUTOMATIC - ~vpolicy:`AUTOMATIC ~height:400 - ~shadow_type:`OUT ~packing:counter_grid#add () + ~vpolicy:`AUTOMATIC ~height:400 + ~shadow_type:`OUT ~packing:counter_grid#add () in let counter_scrolled_grid_box = GPack.vbox ~packing:counter_scrolled_grid#add () in let counter_line = GPack.hbox ~packing:counter_scrolled_grid_box#add () in let counter_line_ref = ref counter_line in - let counter_map = Hashtbl.create n in i := 0; List.iter (fun (name, enabled) -> incr i; @@ -385,48 +487,43 @@ let custom_daemon p gtext vbox step_button back_step_button round_button Hashtbl.add counter_map name counter ) nodes_enabled; - let hide b = b#misc#hide() in let show b = b#misc#show() in let update_checkbox node enabled = match !daemon_kind with | Manual -> - show step_button; show checkbox_grid; - hide back_step_button; - hide legitimate_button; - hide undo_button; - (match !oracle_button_ref with Some b -> hide b | None -> ()); - hide round_button; hide pushbox_grid; hide counter_grid; - let checkbox = try Hashtbl.find checkbox_map node with Not_found -> assert false in - if enabled then - show checkbox - else ( - checkbox#set_active false; (* on decoche *) - hide checkbox - ); - checkbox#set_sensitive enabled + show step_button; show checkbox_grid; + hide back_step_button; + hide legitimate_button; + hide undo_button; + (match !oracle_button_ref with Some b -> hide b | None -> ()); + hide round_button; hide pushbox_grid; hide counter_grid; + let checkbox = try Hashtbl.find checkbox_map node with Not_found -> assert false in + if enabled then + show checkbox + else ( + checkbox#set_active false; (* on decoche *) + hide checkbox + ); + checkbox#set_sensitive enabled | ManualCentral -> - hide undo_button; - hide legitimate_button; - (match !oracle_button_ref with Some b -> hide b | None -> ()); - hide back_step_button; (* requires to store all the inputs from the last ckpt! *) - hide step_button; - hide round_button; hide checkbox_grid; hide counter_grid; - show pushbox_grid; - let pushbox = try Hashtbl.find pushbox_map node with Not_found -> assert false in - if enabled then show pushbox else hide pushbox; - pushbox#set_sensitive enabled - + hide undo_button; + hide legitimate_button; + (match !oracle_button_ref with Some b -> hide b | None -> ()); + hide back_step_button; (* requires to store all the inputs from the last ckpt! *) + hide step_button; + hide round_button; hide checkbox_grid; hide counter_grid; + show pushbox_grid; + let pushbox = try Hashtbl.find pushbox_map node with Not_found -> assert false in + if enabled then show pushbox else hide pushbox; + pushbox#set_sensitive enabled + | Synchronous | Distributed | Central | LocCentral -> - if !daemon_kind = Synchronous then ( - show undo_button; hide counter_grid - ) else ( - hide undo_button; show counter_grid - ); - (match !oracle_button_ref with Some b -> show b | None -> ()); - show back_step_button; show step_button; show round_button; - if not args.salut_mode then show legitimate_button; (* for the time being *) - hide checkbox_grid; hide pushbox_grid; + show undo_button; show counter_grid; + (match !oracle_button_ref with Some b -> show b | None -> ()); + show back_step_button; show step_button; show round_button; + if not args.salut_mode then show legitimate_button; (* for the time being *) + hide checkbox_grid; hide pushbox_grid; in let update_all_checkboxes () = (* only display the buttons of enabled nodes (for the manual daemon) *) @@ -434,7 +531,6 @@ let custom_daemon p gtext vbox step_button back_step_button round_button List.iter (fun (name, enabled) -> update_checkbox name enabled) nodes_enabled in Hashtbl.add refresh_fun_tbl "" update_all_checkboxes; - let set_dd_mode () = black gtext#buffer"==> Switch to a distributed daemon\n"; daemon_kind := Distributed; refresh () in @@ -461,74 +557,9 @@ let custom_daemon p gtext vbox step_button back_step_button round_button ignore(dk_manual_central#connect#clicked ~callback:set_manual_central_mode); (* Affichage d'informations *) (* gtext#buffer#set_text !gtext_content; *) - - let rec get_higher_priority nl = - let prio n = - let counter = try Hashtbl.find counter_map n with Not_found -> assert false in - counter#get - in - let rec aux p acc = function - | [] -> acc - | (n, false)::t -> aux p acc t - | (n, true)::t -> - let pn = prio n in - if p > pn then aux p acc t else - if p = pn then aux p (n::acc) t else - aux pn [n] t - in - aux 0 [] nl - in - let set_daemongui_tbl e = - (* In automatic modes: - - looks who is enabled, - - chooses whom to activate, and - - sets the the tbl shared with the hook function - - Should therefore be used at events where the enabled process info is available - *) - let nodes_enabled = rdbg_nodes_enabled e in - let nodes = List.filter (fun (_,b) -> b) nodes_enabled in - let nodes = get_higher_priority nodes in - (* p ("==> gtkgui: CALL =" ^ (string_of_event e)); *) - (match !daemon_kind with - | Distributed -> ( - let nodes = List.map (fun x -> [x]) nodes in - let to_activate = Daemon.distributed nodes in - Hashtbl.clear daemongui_activate; - List.iter (fun n -> Hashtbl.replace daemongui_activate n true) to_activate - ) - | Synchronous -> ( - Hashtbl.clear daemongui_activate; - List.iter (fun n -> Hashtbl.replace daemongui_activate n true) nodes - ) - | Central -> ( - let nodes = List.map (fun x -> [x]) nodes in - let to_activate = Daemon.central nodes in - Hashtbl.clear daemongui_activate; - List.iter (fun n -> - (* Printf.printf "Activating %s\n" n; *) - Hashtbl.replace daemongui_activate n true) to_activate - ) - | LocCentral -> ( - let get_neigbors x = - let pred = snd (List.split (topology.pred x)) in - let succ = topology.succ x in - let res = List.fold_left - (fun acc x -> if List.mem x acc then acc else x::acc) succ pred - in - (* p (Printf.sprintf "voisins(%s)=%s\n" x (String.concat "," res)); *) - res - in - let nodes = List.map (fun x -> [x, get_neigbors x]) nodes in - let to_activate = Daemon.locally_central nodes in - Hashtbl.clear daemongui_activate; - List.iter (fun n -> Hashtbl.replace daemongui_activate n true) to_activate - ) - | ManualCentral -> () (* SNO; the step is done in pushbox callbacks *) - | Manual -> () - ) - in - set_daemongui_tbl + (* update_daemongui_tbl *) + () +(* End of draw_manual_daemons_buttons *) let prefix = try @@ -549,26 +580,28 @@ let main () = let _locale = GtkMain.Main.init () in let _thread = GtkThread.start () in let window = GWindow.window - (* ~width:320 ~height:240 *) - ~title:"A rdbg GUI for sasa" - ~show:true () + (* ~width:320 ~height:240 *) + ~title:"A rdbg GUI for sasa" + ~show:true () in let w = GPack.vbox ~packing:window#add () ~homogeneous:false in - let box = GPack.vbox ~packing: w#add () in + let box0 = GPack.vbox ~packing: w#add () in + let box = GPack.vbox ~packing: box0#add () in let gbox = GPack.hbox ~packing: box#add () in let gbox2 = GPack.hbox ~packing: box#add () in let sw2 = GBin.scrolled_window ~border_width:10 ~shadow_type:`OUT ~height:250 - ~packing:box#add () + ~packing:box#add () in set_tooltip sw2 "This window displays commands outputs"; let text_out = GText.view ~wrap_mode:`CHAR ~height:250 ~editable:false - ~packing: sw2#add () ~cursor_visible:true + ~packing: sw2#add () ~cursor_visible:true in let p str = black text_out#buffer str in (* It should be better to rely on the gtk event handler *) restart p (); + let bbox0 = GPack.hbox ~packing: box0#add () in let bbox = GPack.hbox ~packing: box#add () in let change_label button str = @@ -596,23 +629,13 @@ let main () = refresh () in - let back_step_button = button ~use_mnemonic:true ~stock:`GO_BACK ~packing:bbox#add () in - set_tooltip back_step_button "Move BACKWARD to the previous STEP"; - change_label back_step_button "Ste_p"; - - let step_button = button ~use_mnemonic:true ~packing:bbox#add ~stock:`GO_FORWARD () in - let back_round_button = - button ~packing:bbox#add ~stock:`MEDIA_PREVIOUS ~use_mnemonic:true ~label:"back round" () - in - let round_button = - button ~use_mnemonic:true ~stock:`MEDIA_FORWARD ~packing:bbox#add ~label:"round" () + let make_button0 stock lbl msg cmd = + let butt = button ~use_mnemonic:true ~stock:stock ~packing:bbox0#add ~label:lbl () in + set_tooltip butt msg; + change_label butt lbl; + ignore (butt#connect#clicked ~callback:cmd); + butt in - let legitimate_button = button ~use_mnemonic:true ~packing:bbox#add () in - set_tooltip legitimate_button - "Move FORWARD until a legitimate configuration is reached (silence by default)"; - let image = GMisc.image ~file:(libui_prefix^"/chut_small.svg") () in - legitimate_button#set_image image#coerce; - let make_button stock lbl msg cmd = let butt = button ~use_mnemonic:true ~stock:stock ~packing:bbox#add ~label:lbl () in set_tooltip butt msg; @@ -620,226 +643,285 @@ let main () = ignore (butt#connect#clicked ~callback:cmd); butt in - let undo_button = make_button `UNDO "_Undo" "Undo the last move" - (button_cb true false (fun ()->u();d())) + (* Going backward requires special care here to remain + deterministic, because of all the PRGs! It concerns back_step, + back_round, and undo. *) + let goto_gui i = + if not gui_mode then ( u(); d() ) + else + let lnext e = + if args.salut_mode then ( + update_daemongui_tbl e; + let e = goto_hook_call e in + e + ) + else ( + update_daemongui_tbl e; + if e.kind = Ltop then e.restore_state e.nb; + let e = goto_hook_call e in + e + ) + in + let ne = + if i < !e.nb then + rev_cond_gen !e (fun ne -> ne.nb = i) lnext (fun _ -> ()) + else if i > !e.nb then + next_cond_gen !e (fun ne -> ne.nb = i) lnext + else + !e + in + e:=ne; + ignore (round !e); + d() + in + let undo_gui () = + let i = + match !redos with + | _::i::t -> redos := i::t; i + | i::[] -> i + | [] -> 1 + in + goto_gui i + in + let back_round_button = + button ~packing:bbox0#add ~stock:`MEDIA_PREVIOUS ~use_mnemonic:true + ~label:"back round" () + in + let back_step_button = button ~use_mnemonic:true ~stock:`GO_BACK ~packing:bbox0#add () in + set_tooltip back_step_button "Move BACKWARD to the previous STEP"; + change_label back_step_button "Ste_p"; + + let undo_button = make_button0 `UNDO "_Undo" "Undo the last move" + (button_cb true false undo_gui) + in + let step_button = button ~use_mnemonic:true ~packing:bbox0#add ~stock:`GO_FORWARD () in + let round_button = + button ~use_mnemonic:true ~stock:`MEDIA_FORWARD ~packing:bbox0#add ~label:"round" () + in + let legitimate_button = + button ~use_mnemonic:true ~stock:`GOTO_LAST ~packing:bbox0#add () in + set_tooltip legitimate_button + "Move FORWARD until a legitimate configuration is reached (silence by default)"; + (* let image = GMisc.image ~file:(libui_prefix^"/chut_small.svg") () in *) + (* legitimate_button#set_image image#coerce; *) - let set_daemongui_tbl = - if !custom_mode_ref then - custom_daemon p text_out w step_button back_step_button round_button - legitimate_button undo_button + if gui_mode then draw_manual_daemons_buttons p text_out w step_button + back_step_button round_button legitimate_button undo_button; + let update_daemongui_tbl_and_redraw = + if gui_mode then update_daemongui_tbl else (fun _ -> ()) in - let a_gui_step e = (* set the daemongui_tbl and step to the next event where the user is asked to choose whom to activate *) - if !custom_mode_ref then ( - set_daemongui_tbl e; + if gui_mode then ( + update_daemongui_tbl_and_redraw e; let e = goto_hook_exit e in let e = goto_hook_call e in e ) - else + else (* internal daemon mode *) let e = sasa_step e in store e.nb; print_event e; e in - let rec next_round_gui_loop rn = - if is_silent ~dflt:false !e then ( - if not args.salut_mode && !e.kind <> Ltop then - (* go to Ltop so that the round number can be updated *) - e := next_cond !e (fun e -> e.kind = Ltop); + let postambule () = (* necessary to get the round nb rigth! *) + if not args.salut_mode && not (check_point_event !e) then + e := next_cond !e check_point_event + in + let next_step_gui () = + if not (is_silent ~dflt:false !e) + (* ??? || (not args.salut_mode && !e.kind <> Ltop) *) + then ( + e := a_gui_step !e; + d() ) - else e:=a_gui_step !e; - if (get_round !e) || is_silent ~dflt:false !e then () else (next_round_gui_loop rn); in let next_round_gui () = - if !custom_mode_ref then ( + if gui_mode then ( + let rec next_round_gui_loop rn = + if is_silent ~dflt:false !e then (postambule ()) + else e := a_gui_step !e; + if (get_round !e) || is_silent ~dflt:false !e then () else ( + next_round_gui_loop rn + ); + in next_round_gui_loop (get_round_nb !e); if - !custom_mode_ref && args.salut_mode && !e.name<>"mv_hook" + args.salut_mode && !e.name<>"mv_hook" && !e.kind<>Call && not (is_silent ~dflt:false !e) then e:= goto_hook_call !e ) else ( (* internal daemon mode *) - e := next_round !e - ); - store !e.nb + e := next_cond_gen !e + (fun e -> e.kind = Ltop && (round e || is_legitimate e)) + sasa_next + ) in let back_step_gui () = - if not !custom_mode_ref then ( + if not gui_mode then ( (* internal daemon mode *) e := back_step !e; pe() ) - else ( + else ( if args.salut_mode then let lnext e = - set_daemongui_tbl e; + update_daemongui_tbl e; (* necessary to get the PRGs rigth during backward moves *) let e = goto_hook_call e in e in let ne = rev_cond_gen !e (fun ne -> - if ne.step = !e.step-1 && ne.kind = !e.kind then true else ( - Printf.printf "%d: ne.step=%d (!e.step-1)=%d\n%!" ne.nb ne.step (!e.step-1); - false - ) + if ne.step = !e.step-1 && ne.kind = !e.kind then true else ( + Printf.printf "%d: ne.step=%d (!e.step-1)=%d\n%!" ne.nb ne.step (!e.step-1); + false + ) ) lnext (fun _ -> ()) in e:=ne else (* custom sasa mode *) let lnext e = - set_daemongui_tbl e; + update_daemongui_tbl e; if e.kind = Ltop then - (* Necessary for reproductibility because set_daemongui_tbl - calls Random.int (via Daemon) which changes the PRGS! *) + (* Necessary for reproductibility because update_daemongui_tbl + calls Random.int (via Daemon) which changes the PRGS! *) e.restore_state e.nb; let e = goto_hook_call e in e in let ne = rev_cond_gen !e - (fun ne -> ne.step = !e.step-1 && ne.name = "mv_hook" && ne.kind=Call) - lnext (fun _ -> ()) + (fun ne -> ne.step = !e.step-1 && check_point_event ne) + lnext (fun _ -> ()) in e:=ne - ); - store !e.nb + ) in let back_round_gui () = - if not !custom_mode_ref then ( + if not gui_mode then ( (* internal daemon mode *) e:=goto_last_ckpt !e.nb ) - else ( - let ne1 = goto_last_ckpt !e.nb in - let ne2 = goto_last_ckpt ne1.nb in - if ne1.nb = !e.nb then - (* already at the first event. Do nothing *) - () - else if ne1.nb = ne2.nb then ( - (* Still in the first round. Go at the beginning *) - e:=ne1 - ) - else ( - (* From round n>1, go to round n-1 *) - e:=ne2; - if !e.kind <> Call && not args.salut_mode then - (* only the first event is already a call*) - e:=goto_hook_call !e - ) - ); - store !e.nb; + else + ( + let ne1 = goto_last_ckpt !e.nb in + let ne2 = if round !e then ne1 else goto_last_ckpt ne1.nb in + if ne1.nb = !e.nb then + (* already at the first event. Do nothing *) + () + else if ne1.nb = ne2.nb then ( + (* Still in the first round. Go at the beginning *) + e:=ne1 + ) + else ( + (* From round n>1, go to round n-1 *) + e:=ne2; + if !e.kind <> Call && not args.salut_mode then + (* only the first event is already a call*) + e:=goto_hook_call !e + (* else if args.salut_mode then e:=??? *) + ) + ); refresh () in ignore (back_step_button#connect#clicked - ~callback:(button_cb true false back_step_gui)); + ~callback:(button_cb true true back_step_gui)); - let step () = - if not (is_silent ~dflt:false !e) || (not args.salut_mode && !e.kind <> Ltop) then ( - e := a_gui_step !e; - d() - ) - in let rec legitimate_gui () = - if is_silent ~dflt:false !e then () else e := a_gui_step !e; - if is_legitimate !e || is_silent ~dflt:false !e then ( - if not args.salut_mode && !e.kind <> Ltop then - e := next_cond !e (fun e -> e.kind = Ltop); + if gui_mode then ( + if is_silent ~dflt:false !e then () else e := a_gui_step !e; + if is_legitimate !e || is_silent ~dflt:false !e then ( + postambule (); d() + ) + else legitimate_gui (); ) - else (legitimate_gui ()); + else (* internal daemon mode *) + e:= next_cond !e (fun le -> is_legitimate le && le.kind=Ltop) in - (* change_label legitimate_button "Silen_t"; *) + change_label legitimate_button "_Legit"; ignore (legitimate_button#connect#clicked ~callback: - (button_cb true true (fun () -> - if !custom_mode_ref then legitimate_gui() else - e:= next_cond !e (fun le -> is_legitimate le && le.kind=Ltop)) - ) - ); - if !custom_mode_ref then legitimate_button#misc#hide(); - (* indeed, in the defaut mode (manual central), it should be hided *) + (button_cb true true legitimate_gui) + ); + if daemon_mode()=GuiManual then legitimate_button#misc#hide(); set_tooltip step_button "Move FORWARD to the next STEP"; change_label step_button "_Step"; - ignore (step_button#connect#clicked ~callback:(button_cb true true step)); + ignore (step_button#connect#clicked ~callback:(button_cb true true next_step_gui)); set_tooltip round_button "Move FORWARD to the next ROUND"; change_label round_button "_Round"; - ignore (round_button#connect#clicked ~callback:(button_cb true true next_round_gui) - ); + ignore (round_button#connect#clicked ~callback:(button_cb true true next_round_gui)); set_tooltip back_round_button "Move BACKWARD to the previous ROUND"; change_label back_round_button "Roun_d"; ignore (back_round_button#connect#clicked ~callback:(button_cb true true back_round_gui)); - - let graph () = - let graph_button = button ~use_mnemonic:true ~packing:bbox#add () in - set_tooltip graph_button "Visualize the Topology states: Green=Enabled ; Gold=Active"; - let image = GMisc.image ~file:(libui_prefix^"/graph_small.png") () in - graph_button#set_image image#coerce; - ignore (graph_button#connect#clicked ~callback:(button_cb false false graph_view)); - - in - graph (); - if args.oracles <> [] then ( let oracle_button = make_button `OK "_Oracle" "Move FORWARD until an oracle is violated" (button_cb_string (fun () -> let str = viol_string () in - e:=goto_hook_call !e; d(); store !e.nb; str)) + e:=goto_hook_call !e; d(); store !e.nb; str)) in oracle_button#misc#hide(); (* indeed, in the defaut mode (manual central), it should be hided *) oracle_button_ref := Some oracle_button ); let _ = make_button `REFRESH "Restar_t" "Restart from the beginning" - (button_cb true true (restart p)) + (button_cb true true (restart p)) in let _ = make_button `REFRESH "_New Seed" "Restart from the beginning using a New Seed" - (button_cb true true - (fun ()-> - !e.RdbgEvent.reset(); - Seed.reset(); Seed.replay_seed := false; - let seed = Seed.get dotfile in - change_sasa_seed seed; - Seed.set (seed); - p (Printf.sprintf "Restarting using the seed %d" seed); - - e:=RdbgStdLib.run ~call_hooks:true (); - Round.reinit(); - redos := [1]; - ckpt_list := [!e]; - - if args.salut_mode then - (* in this mode, the hook plays first to provide fake values to sasa + (button_cb true true + (fun ()-> + Seed.reset(); Seed.replay_seed := false; + let seed = Seed.get dotfile in + change_sasa_seed seed; + Seed.set (seed); + !e.RdbgEvent.reset(); + p (Printf.sprintf "Restarting using the seed %d" seed); + + e:=RdbgStdLib.run ~call_hooks:true (); + Round.reinit(); + redos := [1]; + ckpt_list := [!e]; + + if args.salut_mode then + (* in this mode, the hook plays first to provide fake values to sasa but the hook does not need input at this first step - *) - e:=goto_hook_exit !e; - e:=goto_hook_call !e; - d())) + *) + e:=goto_hook_exit !e; + e:=goto_hook_call !e; + d())) in - let _ = make_button `MEDIA_PLAY "_Sim2chro" "Launch sim2chro on the generated data (so far)" - (button_cb false false sim2chro) + let graph () = + let graph_button = button ~use_mnemonic:true ~packing:bbox#add () in + set_tooltip graph_button "Visualize the Topology states: Green=Enabled ; Gold=Active"; + let image = GMisc.image ~file:(libui_prefix^"/graph_small.png") () in + graph_button#set_image image#coerce; + ignore (graph_button#connect#clicked ~callback:(button_cb false false graph_view)); + + in + graph (); + let _ = make_button `MEDIA_PLAY "Sim2_chro" "Launch sim2chro on the generated data (so far)" + (button_cb false false sim2chro) in let _ = make_button `MEDIA_PLAY "_Gnuplot" "Launch gnuplot-rif on the generated data (so far)" - (button_cb false false gnuplot) + (button_cb false false gnuplot) in let _ = make_button `INFO "_Info" "Get information about the current session" - (button_cb_string info_string) + (button_cb_string info_string) in let _ = make_button `QUIT "_Quit" "Quit RDBGUI" (fun() -> p "bye"; Stdlib.exit 0) in let sw1 = GBin.scrolled_window ~border_width:10 ~shadow_type:`IN ~height:130 ~width:50 - ~packing:w#add () + ~packing:w#add () in sw1#misc#set_tooltip_text "This window displays the rdbg.log file"; let text_in = GText.view ~wrap_mode:`CHAR ~height:250 ~editable:true ~width:50 - ~packing: sw1#add () ~cursor_visible:true + ~packing: sw1#add () ~cursor_visible:true in let rdbg_log = open_in "rdbg.log" in create_tags text_in#buffer; @@ -855,11 +937,11 @@ let main () = read_rdbglog () in let _ = Thread.create read_rdbglog () in - + let dot_button = radio_button ~packing:gbox#add ~label:"dot" () in let make_but active lbl = radio_button ~packing:gbox#add - ~active:active ~group:dot_button#group ~label:lbl () + ~active:active ~group:dot_button#group ~label:lbl () in let fd_button = make_but false "fdp" in let sf_button = make_but false "sfdp" in @@ -872,14 +954,14 @@ let main () = let connect button str cmd = ignore (button#connect#clicked ~callback:(fun () -> p ((button#misc#tooltip_text)^"\n"^(help_string str)); - dot_view := cmd; !dot_view())) + dot_view := cmd; !dot_view())) in let have_parent () = (* is there a parent field in the state ? *) List.exists (fun (v,_) -> Str.string_match (Str.regexp ".*_par.*") v 0) !e.data in if have_parent () then ( let make_but lbl = GButton.radio_button ~packing:gbox2#add - ~group:dot_button#group ~label:lbl () + ~group:dot_button#group ~label:lbl () in let par_dot_button = make_but "dot*" in let par_fd_button = make_but "fdp*" in @@ -906,14 +988,14 @@ let main () = connect par_pa_button "pa_par" pa_par; connect par_os_button "os_par" os_par; ); - set_tooltip dot_button "Use the dot engine to display the graph"; - set_tooltip fd_button "Use the fdp engine to display the graph"; - set_tooltip sf_button "Use the sfdp engine to display the graph"; - set_tooltip ne_button "Use the neato engine to display the graph"; - set_tooltip tw_button "Use the twopi engine to display the graph"; - set_tooltip ci_button "Use the circo engine to display the graph"; - set_tooltip pa_button "Use the patchwork engine to display the graph"; - set_tooltip os_button "Use the osage engine to display the graph"; + set_tooltip dot_button "Use the dot engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip fd_button "Use the fdp engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip sf_button "Use the sfdp engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip ne_button "Use the neato engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip tw_button "Use the twopi engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip ci_button "Use the circo engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip pa_button "Use the patchwork engine to display the graph (cf https://graphviz.org/docs/layouts/)"; + set_tooltip os_button "Use the osage engine to display the graph (cf https://graphviz.org/docs/layouts/)"; connect dot_button "d" dot; connect fd_button "fd" fd; @@ -925,9 +1007,9 @@ let main () = connect os_button "os" os; ignore (window#connect#destroy ~callback: ( - fun () -> - quit (); (* quit rdbg, this will stop the readloop below *) - Main.quit () (* terminate gtk *) + fun () -> + quit (); (* quit rdbg, this will stop the readloop below *) + Main.quit () (* terminate gtk *) )); Seed.replay_seed := true; ignore(Seed.get dotfile); diff --git a/tools/rdbg4sasa/sasa-rdbg-cmds.ml b/tools/rdbg4sasa/sasa-rdbg-cmds.ml index 1f356ba5..334523af 100644 --- a/tools/rdbg4sasa/sasa-rdbg-cmds.ml +++ b/tools/rdbg4sasa/sasa-rdbg-cmds.ml @@ -50,7 +50,14 @@ let enabled pl = (* returns the enabled processes *) (* called at each event via the time-travel hook *) let (round : RdbgEvent.t -> bool) = fun e -> - if e.kind=Exit && e.name = "sasa" then get_round e else false + let get_it = + if args.salut_mode then + e.kind=Call && e.name = "mv_hook" + else + true + (* e.kind=Exit && e.name = "sasa" *) + in + if get_it then get_round e else false (**********************************************************************) let d_par () = dot true (get_round_nb !e) p dotfile !e;; @@ -145,12 +152,15 @@ It is the case for sasa and lutin at least. let (string_to_string_list : string -> string list) = fun str -> - Str.split (Str.regexp "[ \t]+") str + Str.split (Str.regexp "[ \t]+") str let (change_ocaml_plugin_seed: int -> RdbgPlugin.t -> RdbgPlugin.t) = - fun seed rdbgplugin -> - let new_sasa_call = Printf.sprintf "%s --seed %d" rdbgplugin.id seed in - Sasa.SasaRun.make(Array.of_list (string_to_string_list new_sasa_call)) + fun seed rdbgplugin -> + if args.salut_mode then ((* nothing to do here *) rdbgplugin) else + let new_sasa_call = Printf.sprintf "%s --seed %d" rdbgplugin.id seed in + Printf.printf "%s\n%!" new_sasa_call; + Sasa.SasaRun.make (Array.of_list (string_to_string_list new_sasa_call)) + let (change_plugin_seed : int -> RdbgArg.reactive_program -> RdbgArg.reactive_program) = fun seed plugin -> @@ -318,11 +328,12 @@ let _ = add_doc_entry "potential" "unit -> float option" "returns the current potential if available" "sasa" "sasa-rdbg-cmds.ml" - - (**********************************************************************) -(* Perform the checkpointing at rounds! *) -let _ = check_ref := fun e -> e.nb = 1 || round e;; +(* overide the default checkpointing at rounds + +nb : this is overridden in gtkgui.ml +*) +let _ = check_ref := fun e -> e.nb = 1 || (e.kind=Exit && e.name="sasa" && round e);; (**********************************************************************) let _ = -- GitLab