structure Main : sig val main: string * string list -> OS.Process.status end = struct structure J = JSON structure JP = JSONParser structure JU = JSONUtil structure M : MESH = Mesh structure PC = PromConfig val link_prefix = PC.link_prefix val mesh_prefix = PC.mesh_prefix val newline = String.str #"\n" val link_header = "# HELP " ^ link_prefix ^ " link quality between two nodes" ^ newline ^ "# TYPE " ^ link_prefix ^ " gauge" ^ newline val mesh_header = "# HELP " ^ mesh_prefix ^ " members and (nodes + edges / 1000) of a mesh" ^ newline ^ "# TYPE " ^ mesh_prefix ^ " gauge" ^ newline fun prom2string (metric, labels, scalar) = let fun esc #"\"" = "\\\"" | esc #"\\" = "\\\\" | esc #"\n" = "\\n" | esc c = str c in metric ^ (ListFormat.fmt {init = "{", sep= ",", final = "}", fmt = fn (label, value) => label ^ "=\"" ^ (String.translate esc value) ^ "\""} labels) ^ " " ^ scalar ^ (* " " ^ timestamp ^ *) newline end (* exnMessage from JSONUtil - slightly extended *) fun v2bs' (J.ARRAY []) = "[]" | v2bs' (J.ARRAY _) = "[...]" | v2bs' (J.BOOL v) = Bool.toString v | v2bs' (J.FLOAT v) = Real.toString v | v2bs' (J.INT v) = IntInf.toString v | v2bs' J.NULL = "null" | v2bs' (J.OBJECT []) = "{}" | v2bs' (J.OBJECT _) = "{...}" | v2bs' (J.STRING v) = v fun v2bs (J.ARRAY []) = "[]" | v2bs (J.ARRAY vl) = ListFormat.fmt { init = "[", sep = ",", final = "]", fmt = v2bs' } vl | v2bs (J.OBJECT []) = "{}" | v2bs (J.OBJECT fl) = ListFormat.fmt { init = "{", sep = ",", final = "}", fmt = fn (n, v) => n ^ ":" ^ (v2bs' v) } fl | v2bs v = v2bs' v fun json_handler logstring exn = (TextIO.output (TextIO.stdErr, "json_handler(" ^ logstring ^ "): " ^ (JU.exnMessage exn) ^ ": " ^ (v2bs (case exn of JU.NotBool v => v | JU.NotInt v => v | JU.NotNumber v => v | JU.NotString v => v | JU.NotObject v => v | JU.FieldNotFound(v, fld) => v | JU.NotArray v => v | _ => raise exn)) ^ "\n") ; ()) fun get_version obj = JU.asInt (JU.lookupField obj "version") fun get_fields (J.OBJECT flds) = flds | get_fields v = raise Fail ("value is not an object (" ^ (v2bs v) ^ ")") fun get_list (J.OBJECT flds) = map #2 flds | get_list (J.ARRAY nds) = nds | get_list v = raise Fail ("value does not contain a list (" ^ (v2bs v) ^ ")") fun get_nodes obj = get_list (JU.lookupField (JU.lookupField obj "batadv") "nodes") fun get_links obj = get_list (JU.lookupField (JU.lookupField obj "batadv") "links") fun extract_node obj = { id = JU.asString (JU.lookupField obj "id"), node_id = JU.asString (JU.lookupField obj "node_id") } fun extract_link obj = { source = JU.asInt (JU.lookupField obj "source"), target = JU.asInt (JU.lookupField obj "target"), tq = JU.asNumber (JU.lookupField obj "tq"), vpn = JU.asBool (JU.lookupField obj "vpn") } fun link2prom nodes_id_vector { source, target, tq, vpn } = let val source_id = Vector.sub (nodes_id_vector, source) val target_id = Vector.sub (nodes_id_vector, target) in prom2string (link_prefix, [("source", source_id), ("target", target_id), ("vpn", Bool.toString vpn)], Real.toString tq) end fun mesh2prom nodes_id_vector { nodes, edges } = let val node_count = Real.fromInt (M.Set.numItems nodes) val edge_count = Real.fromInt (M.PairSet.numItems edges) val members = ListMergeSort.sort String.> (map (fn i => Vector.sub (nodes_id_vector, i)) (M.Set.listItems nodes)) in prom2string (mesh_prefix, [("node_id", hd members), ("members", ListFormat.fmt { init = "", final = "", sep = "|", fmt = fn m => m } members)], Real.toString (node_count + edge_count / 1000.0)) end fun complain (p, s) = (TextIO.output (TextIO.stdErr, concat [p, ": ", s, "\n"]); OS.Process.failure) fun main (p, [inf]) = (let val json = JP.parseFile inf val _ = get_version json = 1 orelse raise Fail "version must be 1" val nodes_json = get_nodes json handle exn => (json_handler "get_nodes" exn ; raise Fail "get_nodes") val nodes_vector = Vector.fromList (map extract_node nodes_json) val nodes_id_vector = Vector.map #node_id nodes_vector val links_json = get_links json handle exn => (json_handler "get_links" exn ; raise Fail "get_links") val links_extract = map extract_link links_json val links_prom = map (link2prom nodes_id_vector) links_extract val graph = M.links2graph (map (fn l => (#source l, #target l)) links_extract) val meshes_prom = map (mesh2prom nodes_id_vector) (M.Map.listItems (#meshes graph)) in (print link_header ; print mesh_header ; app print links_prom ; app print meshes_prom ; OS.Process.success) end handle e => complain (p, exnMessage e)) | main (p, _) = complain (p, "usage: " ^ p ^ " graph.json") end