mirror of
https://github.com/paparazzi/paparazzi.git
synced 2026-06-04 22:17:01 +08:00
698 lines
25 KiB
OCaml
698 lines
25 KiB
OCaml
(*
|
|
* $Id$
|
|
*
|
|
* Multi aircrafts receiver, logger and broadcaster
|
|
*
|
|
* Copyright (C) 2004 CENA/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.
|
|
*
|
|
*)
|
|
|
|
let my_id = "ground"
|
|
let gps_mode_3D = 3
|
|
let no_md5_check = ref false
|
|
let replay_old_log = ref false
|
|
|
|
open Printf
|
|
open Latlong
|
|
open Server_globals
|
|
open Aircraft
|
|
module U = Unix
|
|
module LL = Latlong
|
|
|
|
module Ground = struct let name = "ground" end
|
|
module Ground_Pprz = Pprz.Messages(Ground)
|
|
module Tm_Pprz = Pprz.Messages (struct let name = "telemetry" end)
|
|
module Alerts_Pprz = Pprz.Messages(struct let name = "alert" end)
|
|
module Dl_Pprz = Pprz.Messages (struct let name = "datalink" end)
|
|
|
|
|
|
|
|
let (//) = Filename.concat
|
|
let logs_path = Env.paparazzi_home // "var" // "logs"
|
|
let conf_xml = Xml.parse_file (Env.paparazzi_home // "conf" // "conf.xml")
|
|
let srtm_path = Env.paparazzi_home // "data" // "srtm"
|
|
|
|
let get_indexed_value = fun t i ->
|
|
if i >= 0 then t.(i) else "UNK"
|
|
|
|
(** The aircrafts store *)
|
|
let aircrafts = Hashtbl.create 3
|
|
|
|
(** Broadcast of the received aircrafts *)
|
|
let aircraft_msg_period = 500 (* ms *)
|
|
let wind_msg_period = 5000 (* ms *)
|
|
let aircraft_alerts_period = 1000 (* ms *)
|
|
let send_aircrafts_msg = fun _asker _values ->
|
|
assert(_values = []);
|
|
let names = String.concat "," (Hashtbl.fold (fun k _v r -> k::r) aircrafts []) ^ "," in
|
|
["ac_list", Pprz.String names]
|
|
|
|
let make_element = fun t a c -> Xml.Element (t,a,c)
|
|
|
|
let log_xml = fun timeofday data_file ->
|
|
let conf_children =
|
|
List.map
|
|
(fun x -> if Xml.tag x = "aircraft" then Env.expand_ac_xml x else x)
|
|
(Xml.children conf_xml) in
|
|
let expanded_conf = make_element (Xml.tag conf_xml) (Xml.attribs conf_xml) conf_children in
|
|
make_element
|
|
"configuration"
|
|
["time_of_day", string_of_float timeofday; "data_file", data_file]
|
|
[expanded_conf; Pprz.messages_xml ()]
|
|
|
|
|
|
let start_time = U.gettimeofday ()
|
|
|
|
(* Opens the log files *)
|
|
let logger = fun () ->
|
|
let d = U.localtime start_time in
|
|
let basename = sprintf "%02d_%02d_%02d__%02d_%02d_%02d" (d.U.tm_year mod 100) (d.U.tm_mon+1) (d.U.tm_mday) (d.U.tm_hour) (d.U.tm_min) (d.U.tm_sec) in
|
|
if not (Sys.file_exists logs_path) then begin
|
|
printf "Creating '%s'\n" logs_path; flush stdout;
|
|
Unix.mkdir logs_path 0o755
|
|
end;
|
|
let log_name = sprintf "%s.log" basename
|
|
and data_name = sprintf "%s.data" basename in
|
|
let f = open_out (logs_path // log_name) in
|
|
output_string f (Xml.to_string_fmt (log_xml start_time data_name));
|
|
close_out f;
|
|
open_out (logs_path // data_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let log = fun ?timestamp logging ac_name msg_name values ->
|
|
match logging with
|
|
Some log ->
|
|
let s = string_of_values values in
|
|
let t =
|
|
match timestamp with
|
|
Some x -> x
|
|
| None -> U.gettimeofday () -. start_time in
|
|
fprintf log "%.3f %s %s %s\n" t ac_name msg_name s; flush log
|
|
| None -> ()
|
|
|
|
|
|
(** Callback for a message from a registered A/C *)
|
|
let ac_msg = fun messages_xml logging ac_name ac ->
|
|
let module Tele_Pprz = Pprz.MessagesOfXml(struct let xml = messages_xml let name="telemetry" end) in
|
|
fun ts m ->
|
|
try
|
|
let timestamp = try Some (float_of_string ts) with _ -> None in
|
|
let (msg_id, values) = Tele_Pprz.values_of_string m in
|
|
let msg = Tele_Pprz.message_of_id msg_id in
|
|
log ?timestamp logging ac_name msg.Pprz.name values;
|
|
Fw_server.log_and_parse ac_name ac msg values;
|
|
Booz_server.log_and_parse ac_name ac msg values
|
|
with
|
|
Telemetry_error (ac_name, msg) ->
|
|
Ground_Pprz.message_send my_id "TELEMETRY_ERROR" ["ac_id", Pprz.String ac_name;"message", Pprz.String msg];
|
|
prerr_endline msg
|
|
| Pprz.Unknown_msg_name (x, c) ->
|
|
fprintf stderr "Unknown message %s in class %s from %s: %s\n%!" x c ac_name m
|
|
| x -> prerr_endline (Printexc.to_string x)
|
|
|
|
|
|
(** If you are 1km above the ground, an angle of 89 degrees between the vertical and
|
|
your camera axis allow you to look 57km away: it should be enough ! **)
|
|
let cam_max_angle = (Deg>>Rad) 89.
|
|
|
|
let send_cam_status = fun a ->
|
|
if a.gps_mode = gps_mode_3D then
|
|
match a.nav_ref with
|
|
None -> () (* No geo ref for camera target *)
|
|
| Some nav_ref ->
|
|
let h = a.agl in
|
|
let phi_absolute = a.cam.phi -. a.roll
|
|
and theta_absolute = a.cam.theta +. a.pitch in
|
|
if phi_absolute > -. cam_max_angle && phi_absolute < cam_max_angle &&
|
|
theta_absolute > -. cam_max_angle && theta_absolute < cam_max_angle then
|
|
let dx = h *. tan phi_absolute
|
|
and dy = h *. tan theta_absolute in
|
|
let alpha = -. a.course in
|
|
let east = dx *. cos alpha -. dy *. sin alpha
|
|
and north = dx *. sin alpha +. dy *. cos alpha in
|
|
let wgs84 = Aircraft.add_pos_to_nav_ref (Geo a.pos) (east, north) in
|
|
let twgs84 = Aircraft.add_pos_to_nav_ref nav_ref a.cam.target in
|
|
let values = ["ac_id", Pprz.String a.id;
|
|
"cam_lat", Pprz.Float ((Rad>>Deg)wgs84.posn_lat);
|
|
"cam_long", Pprz.Float ((Rad>>Deg)wgs84.posn_long);
|
|
"cam_target_lat", Pprz.Float ((Rad>>Deg)twgs84.posn_lat);
|
|
"cam_target_long", Pprz.Float ((Rad>>Deg)twgs84.posn_long)] in
|
|
Ground_Pprz.message_send my_id "CAM_STATUS" values
|
|
|
|
let send_fbw = fun a ->
|
|
let values = [ "ac_id", Pprz.String a.id;
|
|
"rc_mode", Pprz.String a.fbw.rc_mode;
|
|
"rc_status", Pprz.String a.fbw.rc_status;
|
|
"rc_rate", Pprz.Int a.fbw.rc_rate ] in
|
|
Ground_Pprz.message_send my_id "FLY_BY_WIRE" values
|
|
|
|
let send_dl_values = fun a ->
|
|
if a.nb_dl_setting_values > 0 then
|
|
let csv = ref "" in
|
|
for i = 0 to a.nb_dl_setting_values - 1 do
|
|
csv := sprintf "%s%f," !csv a.dl_setting_values.(i)
|
|
done;
|
|
let vs = ["ac_id", Pprz.String a.id; "values", Pprz.String !csv] in
|
|
Ground_Pprz.message_send my_id "DL_VALUES" vs
|
|
|
|
let send_svsinfo = fun a ->
|
|
let svid = ref ""
|
|
and flags= ref ""
|
|
and qi = ref ""
|
|
and cno = ref ""
|
|
and elev = ref ""
|
|
and azim = ref ""
|
|
and age = ref "" in
|
|
let concat = fun ref v ->
|
|
ref := !ref ^ string_of_int v ^ "," in
|
|
for i = 0 to gps_nb_channels - 1 do
|
|
concat svid a.svinfo.(i).svid;
|
|
concat flags a.svinfo.(i).flags;
|
|
concat qi a.svinfo.(i).qi;
|
|
concat cno a.svinfo.(i).cno;
|
|
concat elev a.svinfo.(i).elev;
|
|
concat azim a.svinfo.(i).azim;
|
|
concat age a.svinfo.(i).age
|
|
done;
|
|
let f = fun s r -> (s, Pprz.String !r) in
|
|
let vs = ["ac_id", Pprz.String a.id;
|
|
"pacc", Pprz.Int a.gps_Pacc;
|
|
f "svid" svid; f "flags" flags; f "qi" qi; f "msg_age" age;
|
|
f "cno" cno; f "elev" elev; f "azim" azim] in
|
|
Ground_Pprz.message_send my_id "SVSINFO" vs
|
|
|
|
let send_horiz_status = fun a ->
|
|
match a.horiz_mode with
|
|
Circle (geo, r) ->
|
|
let vs = [ "ac_id", Pprz.String a.id;
|
|
"circle_lat", Pprz.Float ((Rad>>Deg)geo.posn_lat);
|
|
"circle_long", Pprz.Float ((Rad>>Deg)geo.posn_long);
|
|
"radius", Pprz.Int r ] in
|
|
Ground_Pprz.message_send my_id "CIRCLE_STATUS" vs
|
|
| Segment (geo1, geo2) ->
|
|
let vs = [ "ac_id", Pprz.String a.id;
|
|
"segment1_lat", Pprz.Float ((Rad>>Deg)geo1.posn_lat);
|
|
"segment1_long", Pprz.Float ((Rad>>Deg)geo1.posn_long);
|
|
"segment2_lat", Pprz.Float ((Rad>>Deg)geo2.posn_lat);
|
|
"segment2_long", Pprz.Float ((Rad>>Deg)geo2.posn_long) ] in
|
|
Ground_Pprz.message_send my_id "SEGMENT_STATUS" vs
|
|
| UnknownHorizMode -> ()
|
|
|
|
let send_survey_status = fun a ->
|
|
match a.survey with
|
|
None ->
|
|
Ground_Pprz.message_send my_id "SURVEY_STATUS" ["ac_id", Pprz.String a.id]
|
|
| Some (geo1, geo2) ->
|
|
let vs = [ "ac_id", Pprz.String a.id;
|
|
"south_lat", Pprz.Float ((Rad>>Deg)geo1.posn_lat);
|
|
"west_long", Pprz.Float ((Rad>>Deg)geo1.posn_long);
|
|
"north_lat", Pprz.Float ((Rad>>Deg)geo2.posn_lat);
|
|
"east_long", Pprz.Float ((Rad>>Deg)geo2.posn_long) ] in
|
|
Ground_Pprz.message_send my_id "SURVEY_STATUS" vs
|
|
|
|
|
|
|
|
let send_wind = fun a ->
|
|
let id = a.id in
|
|
try
|
|
let (wind_cap_rad, wind_polar, mean, stddev, _nb_sample) = Wind.get id in
|
|
let vs =
|
|
["ac_id", Pprz.String id;
|
|
"dir", Pprz.Float ((Rad>>Deg)wind_cap_rad);
|
|
"wspeed", Pprz.Float wind_polar;
|
|
"mean_aspeed", Pprz.Float mean;
|
|
"stddev", Pprz.Float stddev] in
|
|
Ground_Pprz.message_send my_id "WIND" vs
|
|
with
|
|
_exc -> ()
|
|
|
|
let send_telemetry_status = fun a ->
|
|
let id = a.id in
|
|
try
|
|
let vs =
|
|
["ac_id", Pprz.String id;
|
|
"time_since_last_bat_msg", Pprz.Float (U.gettimeofday () -. a.last_msg_date)] in
|
|
Ground_Pprz.message_send my_id "TELEMETRY_STATUS" vs
|
|
with
|
|
_exc -> ()
|
|
|
|
let send_moved_waypoints = fun a ->
|
|
Hashtbl.iter
|
|
(fun wp_id wp ->
|
|
let geo = wp.wp_geo in
|
|
let vs =
|
|
["ac_id", Pprz.String a.id;
|
|
"wp_id", Pprz.Int wp_id;
|
|
"long", Pprz.Float ((Rad>>Deg)geo.posn_long);
|
|
"lat", Pprz.Float ((Rad>>Deg)geo.posn_lat);
|
|
"alt", Pprz.Float wp.altitude] in
|
|
Ground_Pprz.message_send my_id "WAYPOINT_MOVED" vs)
|
|
a.waypoints
|
|
|
|
|
|
|
|
|
|
|
|
let send_aircraft_msg = fun ac ->
|
|
try
|
|
let a = Hashtbl.find aircrafts ac in
|
|
let f = fun x -> Pprz.Float x in
|
|
let wgs84 = try a.pos with _ -> LL.make_geo 0. 0. in
|
|
let values = ["ac_id", Pprz.String ac;
|
|
"roll", f (Geometry_2d.rad2deg a.roll);
|
|
"pitch", f (Geometry_2d.rad2deg a.pitch);
|
|
"heading", f (Geometry_2d.rad2deg a.heading);
|
|
"lat", f ((Rad>>Deg)wgs84.posn_lat);
|
|
"long", f ((Rad>>Deg) wgs84.posn_long);
|
|
"unix_time", f a.unix_time;
|
|
"itow", Pprz.Int32 a.itow;
|
|
"speed", f a.gspeed;
|
|
"course", f (Geometry_2d.rad2deg a.course);
|
|
"alt", f a.alt;
|
|
"agl", f a.agl;
|
|
"climb", f a.climb] in
|
|
Ground_Pprz.message_send my_id "FLIGHT_PARAM" values;
|
|
|
|
(** send ACINFO messages if more than one A/C registered *)
|
|
if Hashtbl.length aircrafts > 1 then
|
|
begin
|
|
let cm_of_m = fun f -> Pprz.Int (truncate (100. *. f)) in
|
|
let pos = LL.utm_of WGS84 a.pos in
|
|
let ac_info = ["ac_id", Pprz.String ac;
|
|
"utm_east", cm_of_m pos.utm_x;
|
|
"utm_north", cm_of_m pos.utm_y;
|
|
"course", Pprz.Int (truncate (10. *. (Geometry_2d.rad2deg a.course)));
|
|
"alt", cm_of_m a.alt;
|
|
"speed", cm_of_m a.gspeed;
|
|
"climb", cm_of_m a.climb;
|
|
"itow", Pprz.Int32 a.itow] in
|
|
Dl_Pprz.message_send my_id "ACINFO" ac_info;
|
|
end;
|
|
|
|
if !Kml.enabled then
|
|
Kml.update_ac a;
|
|
|
|
begin
|
|
match a.nav_ref with
|
|
Some nav_ref ->
|
|
let values = ["ac_id", Pprz.String ac;
|
|
"cur_block", Pprz.Int a.cur_block;
|
|
"cur_stage", Pprz.Int a.cur_stage;
|
|
"stage_time", Pprz.Int a.stage_time;
|
|
"block_time", Pprz.Int a.block_time;
|
|
"target_lat", f ((Rad>>Deg)a.desired_pos.posn_lat);
|
|
"target_long", f ((Rad>>Deg)a.desired_pos.posn_long);
|
|
"target_alt", Pprz.Float a.desired_altitude;
|
|
"target_climb", Pprz.Float a.desired_climb;
|
|
"target_course", Pprz.Float ((Rad>>Deg)a.desired_course);
|
|
"dist_to_wp", Pprz.Float a.dist_to_wp
|
|
] in
|
|
Ground_Pprz.message_send my_id "NAV_STATUS" values
|
|
| None -> () (* No nav_ref yet *)
|
|
end;
|
|
|
|
let values = ["ac_id", Pprz.String ac;
|
|
"throttle", f a.throttle;
|
|
"throttle_accu", f a.throttle_accu;
|
|
"rpm", f a.rpm;
|
|
"temp", f a.temp;
|
|
"bat", f a.bat;
|
|
"amp", f a.amp;
|
|
"energy", Pprz.Int a.energy] in
|
|
Ground_Pprz.message_send my_id "ENGINE_STATUS" values;
|
|
|
|
let ap_mode = get_indexed_value ap_modes a.ap_mode in
|
|
let gaz_mode = get_indexed_value gaz_modes a.gaz_mode in
|
|
let lat_mode = get_indexed_value lat_modes a.lateral_mode in
|
|
let horiz_mode = get_indexed_value horiz_modes a.horizontal_mode in
|
|
let gps_mode = get_indexed_value gps_modes a.gps_mode
|
|
and kill_mode = if a.kill_mode then "ON" else "OFF" in
|
|
let values = ["ac_id", Pprz.String ac;
|
|
"flight_time", Pprz.Int a.flight_time;
|
|
"ap_mode", Pprz.String ap_mode;
|
|
"gaz_mode", Pprz.String gaz_mode;
|
|
"lat_mode", Pprz.String lat_mode;
|
|
"horiz_mode", Pprz.String horiz_mode;
|
|
"gps_mode", Pprz.String gps_mode;
|
|
"kill_mode", Pprz.String kill_mode
|
|
] in
|
|
Ground_Pprz.message_send my_id "AP_STATUS" values;
|
|
|
|
send_cam_status a;
|
|
send_fbw a;
|
|
send_svsinfo a;
|
|
send_horiz_status a;
|
|
|
|
a.time_since_last_survey_msg <- a.time_since_last_survey_msg +. float aircraft_msg_period /. 1000.;
|
|
if a.time_since_last_survey_msg > 5. then (* FIXME Two missed messages *)
|
|
a.survey <- None;
|
|
|
|
send_survey_status a;
|
|
send_dl_values a;
|
|
send_moved_waypoints a;
|
|
if !Kml.enabled then
|
|
Kml.update_waypoints a;
|
|
send_telemetry_status a
|
|
with
|
|
Not_found -> prerr_endline ac
|
|
| x -> prerr_endline (Printexc.to_string x)
|
|
|
|
(** Check if it is a replayed A/C (c.f. sw/logalizer/play.ml) *)
|
|
let replayed = fun ac_id ->
|
|
let n = String.length ac_id in
|
|
if n > 6 && String.sub ac_id 0 6 = "replay" then
|
|
(true, String.sub ac_id 6 (n - 6), "/var/replay/", Xml.parse_file (Env.paparazzi_home // "var/replay/conf/conf.xml"))
|
|
else
|
|
(false, ac_id, "", conf_xml)
|
|
|
|
(* Store of unknown received A/C ids. To be able to report an error only once *)
|
|
let unknown_aircrafts = Hashtbl.create 5
|
|
|
|
let get_conf = fun real_id id conf_xml ->
|
|
try
|
|
ExtXml.child conf_xml "aircraft" ~select:(fun x -> ExtXml.attrib x "ac_id" = id)
|
|
with
|
|
Not_found ->
|
|
Hashtbl.add unknown_aircrafts real_id ();
|
|
failwith (sprintf "Error: A/C '%s' not found" id)
|
|
|
|
let check_md5sum = fun ac_name alive_md5sum aircraft_conf_dir ->
|
|
(* Read the digest generated by sw/tools/gen_aircraft *)
|
|
let f = open_in (aircraft_conf_dir // "aircraft.md5") in
|
|
let md5sum = input_line f in
|
|
close_in f;
|
|
|
|
try
|
|
match alive_md5sum with
|
|
Pprz.Array array ->
|
|
let n = Array.length array in
|
|
assert(n = String.length md5sum / 2);
|
|
for i = 0 to n - 1 do
|
|
let x = int_of_string (sprintf "0x%c%c" md5sum.[2*i] md5sum.[2*i+1]) in
|
|
assert (x = Pprz.int_of_value array.(i))
|
|
done
|
|
| _ -> failwith "Array expected here"
|
|
with _ ->
|
|
let error_message = sprintf "WARNING: live md5 signature for %s does not match current configuration, please reload your code (disable check with -no_md5_check option)" ac_name in
|
|
if !no_md5_check then
|
|
fprintf stderr "%s; continuing anyway as requested\n%!" error_message
|
|
else
|
|
failwith error_message
|
|
|
|
|
|
let new_aircraft = fun get_alive_md5sum real_id ->
|
|
let is_replayed, id, root_dir, conf_xml = replayed real_id in
|
|
let conf = get_conf real_id id conf_xml in
|
|
let ac_name = ExtXml.attrib conf "name" in
|
|
let var_aircraft_dir = Env.paparazzi_home // root_dir // "var" // ac_name in
|
|
|
|
if not (Sys.file_exists var_aircraft_dir) then begin
|
|
(* Let's look for a backup configuration with the md5 signature *)
|
|
end;
|
|
|
|
let fp_file = var_aircraft_dir // "flight_plan.xml" in
|
|
let xml_fp = ExtXml.child (Xml.parse_file fp_file) "flight_plan" in
|
|
|
|
let aircraft_conf_dir = var_aircraft_dir // "conf" in
|
|
let airframe_file = aircraft_conf_dir // ExtXml.attrib conf "airframe" in
|
|
let airframe_xml = Xml.parse_file airframe_file in
|
|
|
|
if not is_replayed then
|
|
check_md5sum real_id (get_alive_md5sum ()) aircraft_conf_dir;
|
|
|
|
let ac = Aircraft.new_aircraft real_id ac_name xml_fp airframe_xml in
|
|
let update = fun () ->
|
|
for i = 0 to Array.length ac.svinfo - 1 do
|
|
ac.svinfo.(i).age <- ac.svinfo.(i).age + 1;
|
|
done in
|
|
|
|
ignore (Glib.Timeout.add 1000 (fun _ -> update (); true));
|
|
|
|
let messages_xml = Xml.parse_file (Env.paparazzi_home // root_dir // "conf" // "messages.xml") in
|
|
ac, messages_xml
|
|
|
|
let check_alerts = fun a ->
|
|
let bat_section = ExtXml.child a.airframe ~select:(fun x -> Xml.attrib x "name" = "BAT") "section" in
|
|
let fvalue = fun name default ->
|
|
try ExtXml.float_attrib (ExtXml.child bat_section ~select:(fun x -> ExtXml.attrib x "name" = name) "define") "value" with _ -> default in
|
|
|
|
let catastrophic_level = fvalue "CATASTROPHIC_BAT_LEVEL" 9.
|
|
and critic_level = fvalue "CRITIC_BAT_LEVEL" 10.
|
|
and warning_level = fvalue "LOW_BAT_LEVEL" 10.5 in
|
|
|
|
let send = fun level ->
|
|
let vs = [ "ac_id", Pprz.String a.id;
|
|
"level", Pprz.String level;
|
|
"value", Pprz.Float a.bat] in
|
|
Alerts_Pprz.message_send my_id "BAT_LOW" vs in
|
|
if a.bat < catastrophic_level then send "CATASTROPHIC"
|
|
else if a.bat < critic_level then send "CRITIC"
|
|
else if a.bat < warning_level then send "WARNING"
|
|
|
|
let wind_clear = fun _sender vs ->
|
|
Wind.clear (Pprz.string_assoc "ac_id" vs)
|
|
|
|
let periodic = fun period cb ->
|
|
Glib.Timeout.add period (fun () -> cb (); true)
|
|
|
|
|
|
let register_periodic = fun ac x ->
|
|
ac.periodic_callbacks <- x :: ac.periodic_callbacks
|
|
|
|
(** add the periodic airprox check for the aircraft (name) on all aircraft *)
|
|
(** already known *)
|
|
let periodic_airprox_check = fun name ->
|
|
let thisac = Hashtbl.find aircrafts name in
|
|
let aircrafts2list = fun () ->
|
|
let list = ref [] in
|
|
Hashtbl.iter (fun name _a -> list := name :: !list) aircrafts;
|
|
!list in
|
|
let ac_names = List.filter (fun a -> a <> name) (aircrafts2list ()) in
|
|
let list_ac = List.map (fun name -> Hashtbl.find aircrafts name) ac_names in
|
|
|
|
let check_airprox = fun ac ->
|
|
try
|
|
match Airprox.check_airprox thisac ac with
|
|
None -> ()
|
|
| Some level ->
|
|
let vs =
|
|
["ac_id", Pprz.String (thisac.id ^ "," ^ ac.id) ; "level", Pprz.String level] in
|
|
Alerts_Pprz.message_send my_id "AIR_PROX" vs
|
|
with
|
|
x -> fprintf stderr "check_airprox: %s\n%!" (Printexc.to_string x)
|
|
|
|
in
|
|
|
|
List.iter
|
|
(fun ac ->
|
|
register_periodic thisac (periodic aircraft_alerts_period (fun () -> check_airprox ac))
|
|
) list_ac
|
|
|
|
|
|
let register_aircraft = fun name a ->
|
|
Hashtbl.add aircrafts name a;
|
|
register_periodic a (periodic aircraft_msg_period (fun () -> send_aircraft_msg name));
|
|
register_periodic a (periodic aircraft_alerts_period (fun () -> check_alerts a));
|
|
periodic_airprox_check name;
|
|
register_periodic a (periodic wind_msg_period (fun () -> send_wind a));
|
|
Wind.new_ac name 36;
|
|
ignore(Ground_Pprz.message_bind "WIND_CLEAR" wind_clear);
|
|
|
|
if !Kml.enabled then
|
|
Kml.build_files a
|
|
|
|
|
|
(** Identifying message from an A/C *)
|
|
let ident_msg = fun log name vs ->
|
|
try
|
|
if not (Hashtbl.mem aircrafts name) &&
|
|
not (Hashtbl.mem unknown_aircrafts name) then
|
|
let get_md5sum = fun () -> Pprz.assoc "md5sum" vs in
|
|
let ac, messages_xml = new_aircraft get_md5sum name in
|
|
let ac_msg_closure = ac_msg messages_xml log name ac in
|
|
let _b = Ivy.bind (fun _ args -> ac_msg_closure args.(1) args.(2)) (sprintf "^(([0-9]+\\.[0-9]+) )?%s +(.*)" name) in
|
|
register_aircraft name ac;
|
|
Ground_Pprz.message_send my_id "NEW_AIRCRAFT" ["ac_id", Pprz.String name]
|
|
with
|
|
exc -> prerr_endline (Printexc.to_string exc)
|
|
|
|
let new_color = fun () ->
|
|
sprintf "#%02x%02x%02x" (Random.int 256) (Random.int 256) (Random.int 256)
|
|
|
|
(* Waits for new aircrafts *)
|
|
let listen_acs = fun log ->
|
|
(** Wait for any message (they all are identified with the A/C) *)
|
|
ignore (Tm_Pprz.message_bind "ALIVE" (ident_msg log));
|
|
if !replay_old_log then
|
|
ignore (Tm_Pprz.message_bind "PPRZ_MODE" (ident_msg log))
|
|
|
|
|
|
let send_config = fun http _asker args ->
|
|
let ac_id' = Pprz.string_assoc "ac_id" args in
|
|
try
|
|
let _is_replayed, ac_id, root_dir, conf_xml = replayed ac_id' in
|
|
|
|
let conf = ExtXml.child conf_xml "aircraft" ~select:(fun x -> ExtXml.attrib x "ac_id" = ac_id) in
|
|
let ac_name = ExtXml.attrib conf "name" in
|
|
let protocol =
|
|
if http then
|
|
sprintf "http://%s:8889" (Unix.gethostname ())
|
|
else
|
|
sprintf "file://%s" Env.paparazzi_home in
|
|
let prefix = fun s -> sprintf "%s/%s%s" protocol root_dir s in
|
|
|
|
(** Expanded flight plan and settings have been compiled in var/ *)
|
|
let fp = prefix ("var" // ac_name // "flight_plan.xml")
|
|
and af = prefix ("conf" // ExtXml.attrib conf "airframe")
|
|
and rc = prefix ("conf" // ExtXml.attrib conf "radio")
|
|
and settings = prefix ("var" // ac_name // "settings.xml") in
|
|
let col = try Xml.attrib conf "gui_color" with _ -> new_color () in
|
|
let ac_name = try Xml.attrib conf "name" with _ -> "" in
|
|
[ "ac_id", Pprz.String ac_id;
|
|
"flight_plan", Pprz.String fp;
|
|
"airframe", Pprz.String af;
|
|
"radio", Pprz.String rc;
|
|
"settings", Pprz.String settings;
|
|
"default_gui_color", Pprz.String col;
|
|
"ac_name", Pprz.String ac_name ]
|
|
with
|
|
Not_found ->
|
|
failwith (sprintf "ground UNKNOWN %s" ac_id')
|
|
|
|
let ivy_server = fun http ->
|
|
ignore (Ground_Pprz.message_answerer my_id "AIRCRAFTS" send_aircrafts_msg);
|
|
ignore (Ground_Pprz.message_answerer my_id "CONFIG" (send_config http))
|
|
|
|
|
|
let cm_of_m = fun f -> Pprz.Int (truncate (100. *. f))
|
|
let dl_id = "ground_dl" (* Hack, should be [my_id] *)
|
|
|
|
(** Got a ground.MOVE_WAYPOINT and send a datalink.MOVE_WP *)
|
|
let move_wp = fun logging _sender vs ->
|
|
let f = fun a -> List.assoc a vs
|
|
and ac_id = Pprz.string_assoc "ac_id" vs
|
|
and deg7 = fun f -> Pprz.Int32 (Int32.of_float (Pprz.float_assoc f vs *. 1e7)) in
|
|
let vs = [ "ac_id", Pprz.String ac_id;
|
|
"wp_id", f "wp_id";
|
|
"lat", deg7 "lat";
|
|
"lon", deg7 "long";
|
|
"alt", cm_of_m (Pprz.float_assoc "alt" vs) ] in
|
|
Dl_Pprz.message_send dl_id "MOVE_WP" vs;
|
|
log logging ac_id "MOVE_WP" vs
|
|
|
|
(** Got a DL_SETTING, and send an SETTING *)
|
|
let setting = fun logging _sender vs ->
|
|
let ac_id = Pprz.string_assoc "ac_id" vs in
|
|
let vs = ["ac_id", Pprz.String ac_id;
|
|
"index", List.assoc "index" vs;
|
|
"value", List.assoc "value" vs] in
|
|
Dl_Pprz.message_send dl_id "SETTING" vs;
|
|
log logging ac_id "SETTING" vs
|
|
|
|
|
|
(** Got a GET_DL_SETTING, and send an GET_SETTING *)
|
|
let get_setting = fun logging _sender vs ->
|
|
let ac_id = Pprz.string_assoc "ac_id" vs in
|
|
let vs = ["ac_id", Pprz.String ac_id;
|
|
"index", List.assoc "index" vs] in
|
|
Dl_Pprz.message_send dl_id "GET_SETTING" vs;
|
|
log logging ac_id "GET_SETTING" vs
|
|
|
|
|
|
(** Got a JUMP_TO_BLOCK, and send an BLOCK *)
|
|
let jump_block = fun logging _sender vs ->
|
|
let ac_id = Pprz.string_assoc "ac_id" vs in
|
|
let vs = ["ac_id", Pprz.String ac_id; "block_id", List.assoc "block_id" vs] in
|
|
Dl_Pprz.message_send dl_id "BLOCK" vs;
|
|
log logging ac_id "BLOCK" vs
|
|
|
|
(** Got a RAW_DATALINK,send its contents *)
|
|
let raw_datalink = fun logging _sender vs ->
|
|
let ac_id = Pprz.string_assoc "ac_id" vs
|
|
and m = Pprz.string_assoc "message" vs in
|
|
for i = 0 to String.length m - 1 do
|
|
if m.[i] = ';' then m.[i] <- ' '
|
|
done;
|
|
let msg_id, vs = Dl_Pprz.values_of_string m in
|
|
let msg = Dl_Pprz.message_of_id msg_id in
|
|
Dl_Pprz.message_send dl_id msg.Pprz.name vs;
|
|
log logging ac_id msg.Pprz.name vs
|
|
|
|
(** Get the 'ground' uplink messages, log them and send 'datalink' messages *)
|
|
let ground_to_uplink = fun logging ->
|
|
let bind_log_and_send = fun name handler ->
|
|
ignore (Ground_Pprz.message_bind name (handler logging)) in
|
|
bind_log_and_send "MOVE_WAYPOINT" move_wp;
|
|
bind_log_and_send "DL_SETTING" setting;
|
|
bind_log_and_send "GET_DL_SETTING" get_setting;
|
|
bind_log_and_send "JUMP_TO_BLOCK" jump_block;
|
|
bind_log_and_send "RAW_DATALINK" raw_datalink
|
|
|
|
|
|
(* main loop *)
|
|
let () =
|
|
let ivy_bus = ref "127.255.255.255:2010"
|
|
and logging = ref true
|
|
and http = ref false in
|
|
|
|
let options =
|
|
[ "-b", Arg.String (fun x -> ivy_bus := x), (sprintf "Bus\tDefault is %s" !ivy_bus);
|
|
"-hostname", Arg.Set_string hostname, "<hostname> Set the address for the http server";
|
|
"-http", Arg.Set http, "Send http: URLs (default is file:)";
|
|
"-kml", Arg.Set Kml.enabled, "Enable KML file updating";
|
|
"-kml_no_http", Arg.Set Kml.no_http, "KML without web server (local files only)";
|
|
"-kml_port", Arg.Set_int Kml.port, (sprintf "Port for KML files (default is %d)" !Kml.port);
|
|
"-n", Arg.Clear logging, "Disable log";
|
|
"-no_md5_check", Arg.Set no_md5_check, "Disable safety matching of live and current configurations";
|
|
"-replay_old_log", Arg.Set replay_old_log, "Enable aircraft registering on PPRZ_MODE messages"] in
|
|
|
|
Arg.parse
|
|
options
|
|
(fun x -> Printf.fprintf stderr "%s: Warning: Don't do anything with '%s' argument\n" Sys.argv.(0) x)
|
|
"Usage: ";
|
|
|
|
Srtm.add_path srtm_path;
|
|
|
|
Ivy.init "Paparazzi server" "READY" (fun _ _ -> ());
|
|
Ivy.start !ivy_bus;
|
|
|
|
let logging =
|
|
if !logging then
|
|
(* Opens the log file *)
|
|
Some (logger ())
|
|
else
|
|
None in
|
|
|
|
(* Waits for new aircrafts *)
|
|
listen_acs logging;
|
|
|
|
(* Forward messages from ground agents to vehicles *)
|
|
ground_to_uplink logging;
|
|
|
|
(* Waits for client configurations requests on the Ivy bus *)
|
|
ivy_server !http;
|
|
|
|
let loop = Glib.Main.create true in
|
|
while Glib.Main.is_running loop do ignore (Glib.Main.iteration true) done
|