@@ -328,6 +328,15 @@ async fn persistent_connection(
328328 } ;
329329 } ) ;
330330 }
331+ messages:: WsMessage :: StartShaperTreeStreaming { request_id } => {
332+ let socket_sender_tx = socket_sender_tx. clone( ) ;
333+ tokio:: spawn( async move {
334+ let Ok ( ( ) ) = tree_snapshot_streaming( request_id, socket_sender_tx) . await else {
335+ error!( "Tree snapshot streaming failed" ) ;
336+ return ;
337+ } ;
338+ } ) ;
339+ }
331340 _ => { }
332341 }
333342 }
@@ -919,3 +928,73 @@ async fn circuit_snapshot_streaming(
919928 }
920929 Ok ( ( ) )
921930}
931+
932+ async fn tree_snapshot_streaming (
933+ request_id : u64 ,
934+ reply : tokio:: sync:: mpsc:: Sender < Message > ,
935+ ) -> anyhow:: Result < ( ) > {
936+ #[ derive( Serialize , Deserialize ) ]
937+ struct LiveNetworkTransport {
938+ name : String ,
939+ max_throughput : ( u32 , u32 ) ,
940+ current_throughput : ( u64 , u64 ) ,
941+ current_packets : ( u64 , u64 ) ,
942+ current_tcp_packets : ( u64 , u64 ) ,
943+ current_udp_packets : ( u64 , u64 ) ,
944+ current_icmp_packets : ( u64 , u64 ) ,
945+ current_retransmits : ( u64 , u64 ) ,
946+ current_marks : ( u64 , u64 ) ,
947+ current_drops : ( u64 , u64 ) ,
948+ rtts : Vec < f32 > ,
949+ parents : Vec < usize > ,
950+ immediate_parent : Option < usize > ,
951+ #[ serde( rename = "type" ) ]
952+ node_type : Option < String > ,
953+ }
954+
955+ // Use the same data source as local_api::network_tree
956+ let net_json = crate :: shaped_devices_tracker:: NETWORK_JSON . read ( ) . unwrap ( ) ;
957+ let result: Vec < ( usize , LiveNetworkTransport ) > = net_json
958+ . get_nodes_when_ready ( )
959+ . iter ( )
960+ . enumerate ( )
961+ . map ( |( i, n) | {
962+ let t = n. clone_to_transit ( ) ;
963+ let mapped = LiveNetworkTransport {
964+ name : t. name ,
965+ max_throughput : t. max_throughput ,
966+ current_throughput : t. current_throughput ,
967+ current_packets : t. current_packets ,
968+ current_tcp_packets : t. current_tcp_packets ,
969+ current_udp_packets : t. current_udp_packets ,
970+ current_icmp_packets : t. current_icmp_packets ,
971+ current_retransmits : t. current_retransmits ,
972+ current_marks : t. current_marks ,
973+ current_drops : t. current_drops ,
974+ rtts : t. rtts ,
975+ parents : t. parents ,
976+ immediate_parent : t. immediate_parent ,
977+ node_type : t. node_type ,
978+ } ;
979+ ( i, mapped)
980+ } )
981+ . collect ( ) ;
982+
983+ let Ok ( bytes) = serde_cbor:: to_vec ( & result) else {
984+ error ! ( "Failed to serialize LiveNetworkTransport payload" ) ;
985+ return Ok ( ( ) ) ;
986+ } ;
987+
988+ let message = messages:: WsMessage :: StreamingShaperTree { request_id, data : bytes } ;
989+ let Ok ( ( _, _, ws_bytes) ) = message. to_bytes ( ) else {
990+ error ! ( "Failed to serialize StreamingShaperTree message" ) ;
991+ return Ok ( ( ) ) ;
992+ } ;
993+ if let Err ( e) = reply. try_send ( Message :: Binary ( ws_bytes. into ( ) ) ) {
994+ match e {
995+ TrySendError :: Full ( _) => warn ! ( "Send unavailable: StreamingShaperTree queue full; dropping reply" ) ,
996+ TrySendError :: Closed ( _) => error ! ( "Failed to send StreamingShaperTree: channel closed" ) ,
997+ }
998+ }
999+ Ok ( ( ) )
1000+ }
0 commit comments