|  | @@ -0,0 +1,120 @@
 | 
	
		
			
				|  |  | +structure Main :
 | 
	
		
			
				|  |  | +sig
 | 
	
		
			
				|  |  | +	val main: string * string list -> OS.Process.status
 | 
	
		
			
				|  |  | +end =
 | 
	
		
			
				|  |  | +struct
 | 
	
		
			
				|  |  | +	structure J = JSON
 | 
	
		
			
				|  |  | +	structure JP = JSONParser
 | 
	
		
			
				|  |  | +	structure JU = JSONUtil
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	structure PC = PromConfig
 | 
	
		
			
				|  |  | +	val link_prefix = PC.link_prefix
 | 
	
		
			
				|  |  | +	val mesh_prefix = PC.mesh_prefix
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	(* val timestamp = LargeInt.toString (Time.toMilliseconds (Time.now ())) *)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	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 ^ " path length between two nodes in 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 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
 | 
	
		
			
				|  |  | +		 in (print link_header ;
 | 
	
		
			
				|  |  | +		     app print links_prom ;
 | 
	
		
			
				|  |  | +		     OS.Process.success)
 | 
	
		
			
				|  |  | +		 end handle e => complain (p, exnMessage e))
 | 
	
		
			
				|  |  | +	  | main (p, _) = complain (p, "usage: " ^ p ^ " nodes.json")
 | 
	
		
			
				|  |  | +end
 |