From eceffb8bd535e233e0a0640a0f6547521562ef30 Mon Sep 17 00:00:00 2001 From: Gautier Hattenberger Date: Tue, 9 Nov 2021 16:04:04 +0100 Subject: [PATCH] [ocaml] apply a second topological sort to modules (#2793) * [ocaml] apply a second topological sort to modules After the first one, all functionalities are checked, but corresponding modules are not ordered with the other one. This second sorting algorithm (simpler) is just checking that final selection of modules is correctly ordered and check for possible cyclic dependencies not yet found. ahrs_common dep node is fixed to avoid such a situation. * [modules] trying to fix cyclic dependency in rotorcraft gnc modules the main issue is that the rotorcraft navigation is not explicitely included by the user but automagically included, which is not a good idea, same goes for guidance. * [modules] fix dep system_core is already provided by firmware module and @no_settings was making a cyclic dep for fbw --- conf/modules/ahrs_common.xml | 3 -- conf/modules/firmwares/rotorcraft.xml | 2 +- conf/modules/guidance_rotorcraft.xml | 3 +- conf/modules/nav_basic_rotorcraft.xml | 1 - conf/modules/stabilization_rotorcraft.xml | 2 +- conf/modules/targets/fbw.xml | 2 +- sw/lib/ocaml/aircraft.ml | 62 ++++++++++++++++++++--- 7 files changed, 59 insertions(+), 16 deletions(-) diff --git a/conf/modules/ahrs_common.xml b/conf/modules/ahrs_common.xml index 94377818dd..48a20ca729 100644 --- a/conf/modules/ahrs_common.xml +++ b/conf/modules/ahrs_common.xml @@ -8,9 +8,6 @@ - - @ahrs -
diff --git a/conf/modules/firmwares/rotorcraft.xml b/conf/modules/firmwares/rotorcraft.xml index c2cdb812f0..6f2bc0e763 100644 --- a/conf/modules/firmwares/rotorcraft.xml +++ b/conf/modules/firmwares/rotorcraft.xml @@ -7,7 +7,7 @@ - system_core,autopilot_gnc + system_core,autopilot_gnc,guidance_rotorcraft diff --git a/conf/modules/guidance_rotorcraft.xml b/conf/modules/guidance_rotorcraft.xml index b9c03021bb..625efdd3ac 100644 --- a/conf/modules/guidance_rotorcraft.xml +++ b/conf/modules/guidance_rotorcraft.xml @@ -51,7 +51,8 @@ - @navigation + + nav_basic_rotorcraft,@stabilization guidance,attitude_command
diff --git a/conf/modules/nav_basic_rotorcraft.xml b/conf/modules/nav_basic_rotorcraft.xml index 41e24d7670..24d80d292c 100644 --- a/conf/modules/nav_basic_rotorcraft.xml +++ b/conf/modules/nav_basic_rotorcraft.xml @@ -18,7 +18,6 @@ - guidance_rotorcraft navigation
diff --git a/conf/modules/stabilization_rotorcraft.xml b/conf/modules/stabilization_rotorcraft.xml index 5e654163b0..125eb93bd1 100644 --- a/conf/modules/stabilization_rotorcraft.xml +++ b/conf/modules/stabilization_rotorcraft.xml @@ -16,7 +16,7 @@ - nav_basic_rotorcraft + stabilization
diff --git a/conf/modules/targets/fbw.xml b/conf/modules/targets/fbw.xml index c119205dce..9f442e7f85 100644 --- a/conf/modules/targets/fbw.xml +++ b/conf/modules/targets/fbw.xml @@ -5,7 +5,7 @@ FBW target specific module - system_core,electrical + electrical no_settings diff --git a/sw/lib/ocaml/aircraft.ml b/sw/lib/ocaml/aircraft.ml index 8fbee7c625..7c00f1f767 100644 --- a/sw/lib/ocaml/aircraft.ml +++ b/sw/lib/ocaml/aircraft.ml @@ -111,7 +111,7 @@ type sort_result = { unloaded: (load_type * Module.t) list; (* modules not loaded for this target / firmware *) conflicts: (string * string) list; (* modules in conflict *) required: (GC.bool_expr * string) list; (* functionalities required *) - provided: string list; (* functionalities provided (should contain required) *) + provided: (string * string) list; (* functionalities provided (should contain required) (func * module name) *) } let init_sort_result = fun () -> @@ -121,9 +121,6 @@ let init_sort_result = fun () -> (* topological sort to load modules and their dependencies *) let resolve_modules_dep = fun config_by_target firmware user_target -> - let add_unique = fun l s -> - if List.exists (fun e -> String.compare s e = 0) l then l else l @ [s] - in (* recursive dependency resolution *) let rec dep_resolve = fun sol m target load_type -> let test_module = fun s _m lt -> @@ -164,8 +161,8 @@ let resolve_modules_dep = fun config_by_target firmware user_target -> ) sol m.Module.autoloads in (* add conflicts to list *) let sol = { sol with conflicts = sol.conflicts @ (List.map (fun c -> (c, name)) dep.Module.conflicts) } in - (* add provides to list (if not present) *) - let sol = { sol with provided = List.fold_left add_unique sol.provided dep.Module.provides } in + (* add provides to list *) + let sol = { sol with provided = sol.provided @ List.map (fun p -> (p, name)) dep.Module.provides } in (* all dep and autoload resolved, add to list *) if not (name = "root") then (* don't add root module *) { sol with resolved = sol.resolved @ [(name, (load_type, m))] } (* add to list and return solution *) @@ -192,6 +189,53 @@ let resolve_modules_dep = fun config_by_target firmware user_target -> (* remove from unresolved list to make search faster and return current solution *) { sol with unresolved = List.remove_assoc name sol.unresolved } in + (* ordering solution after finding all dependencies *) + let dep_order = fun sol -> + let dep_list = List.map (fun (n, (_, m)) -> + let rec extract_dep l = function + | GC.Any | GC.Not _ -> l + | GC.And (e1, e2) | GC.Or (e1, e2) -> extract_dep (extract_dep l e1) e2 + | GC.Var s -> + if Str.string_match (Str.regexp "^@.*") s 0 then + (* substitute functionality with module names *) + List.fold_left (fun acc (f, m_name) -> if String.compare f s = 0 then m_name :: acc else acc) l sol.provided + else + s :: l (* return normal module name *) + in + let depend_names = match m.Module.dependencies with + | Some d -> GC.singletonize (List.fold_left extract_dep [] d.Module.requires) + | None -> [] + in + n, depend_names + ) sol.resolved + in + (* list items, each being unique + * code from https://rosettacode.org/wiki/Topological_sort#OCaml + *) + let all = GC.singletonize (List.flatten (List.map (fun (m, dep) -> m::dep) dep_list)) in + (* second topologic sort function *) + let get_deps = fun x -> try List.assoc x dep_list with Not_found -> [] in + let rec order acc later todo progress = + match todo, later with + | [], [] -> (List.rev acc) + | [], _ -> + if progress then order acc [] later false + else begin + let fail_s = List.fold_left (fun s m -> Printf.sprintf "%s '%s'" s m) "" later in + failwith (Printf.sprintf "Error [Aircraft]: un-orderable modules %s" fail_s) + end + | x::xs, _ -> + let deps = get_deps x in + let ok = List.for_all (fun dep -> List.mem dep acc) deps in + if ok then order (x::acc) later xs true + else order acc (x::later) xs progress + in + let starts, todo = List.partition (fun x -> get_deps x = []) all in + let res = order starts [] todo false in + (* apply new order to solution *) + let resolved = List.fold_left (fun l m -> try (m, List.assoc m sol.resolved) :: l with Not_found -> l) [] res in + { sol with resolved=List.rev resolved } + in (* iter on all targets *) Hashtbl.iter (fun target conf -> (* check if a board, target or firmware specific module is available *) @@ -218,17 +262,19 @@ let resolve_modules_dep = fun config_by_target firmware user_target -> if name = c then failwith (Printf.sprintf "Error [Aircraft]: find conflict with module '%s' while loading '%s' in target '%s'" cname name target) ) solution.resolved; - List.iter (fun name -> + List.iter (fun (name, _) -> if name = c then failwith (Printf.sprintf "Error [Aircraft]: find conflict with funcionality while loading '%s' for '%s' in target '%s'" name cname target) ) solution.provided ) solution.conflicts; (* chek that all required functionalities or modules are provided *) List.iter (fun (r, name) -> - if not (List.exists (fun p -> GC.eval_bool p r) (solution.provided @ (fst (List.split solution.resolved)))) then + if not (List.exists (fun p -> GC.eval_bool p r) (fst (List.split solution.provided) @ (fst (List.split solution.resolved)))) then failwith (Printf.sprintf "Error [Aircraft]: functionality '%s' is not provided for '%s' in target '%s'" (GC.sprint_expr r) name target) ) solution.required end; + (* find final order *) + let solution = dep_order solution in (* add configure, defines and modules to conf for all resolved modules *) let new_conf = List.fold_left (fun c (lt, m) -> target_conf_add_module_config c target firmware m lt