@@ -103,6 +103,8 @@ export interface BackhaulRow {
103103 kind : TopoLinkKind
104104 type : string
105105 speed : string
106+ /** Latencia del extremo destino en ms, formateada (#1384); "—" sin dato. */
107+ ping : string
106108 signal : string
107109 tone : 'ok' | 'warn' | 'tunnel'
108110 statusLabel : string
@@ -1616,9 +1618,15 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
16161618 // gateway. Sin semántica o sin match → gateway (comportamiento de siempre,
16171619 // demo incluido).
16181620 const uplinkSemByNodeId = new Map < string , TopoSemLink > ( )
1621+ // #1384: velocidad del cable de un dispositivo cuando el padre la midió
1622+ // en esa boca (server, fillWiredSpeeds: p. ej. los hipervisores en las
1623+ // bocas a 2,5 G del switch gestionado, aunque el device no traiga
1624+ // speedMbps de ningún controlador).
1625+ const wiredSemSpeedByTo = new Map < string , number > ( )
16191626 if ( sem ) {
16201627 for ( const sl of sem . links ) {
16211628 if ( sl . kind === 'uplink' ) uplinkSemByNodeId . set ( sl . to , sl )
1629+ if ( sl . kind === 'wired' && sl . speedMbps && sl . speedMbps > 0 ) wiredSemSpeedByTo . set ( sl . to , sl . speedMbps )
16221630 }
16231631 }
16241632 const uplinkParentName = ( nodeId : string ) : string => {
@@ -1639,15 +1647,29 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
16391647 }
16401648 const uplinkSpeed = ( nodeId : string ) : string =>
16411649 linkSpeed ( uplinkSemByNodeId . get ( nodeId ) ?. speedMbps )
1650+ // #1384: ping del enlace = latencia sondeada del equipo destino (ms, un
1651+ // decimal bajo 10 ms para que se aprecien las LAN); "—" sin sonda.
1652+ const fmtPing = ( ms ?: number ) : string => {
1653+ if ( ms == null || ms < 0 ) return '—'
1654+ if ( ms === 0 ) return '<1 ms'
1655+ return ms < 10 ? `${ ms . toFixed ( 1 ) } ms` : `${ Math . round ( ms ) } ms`
1656+ }
16421657 if ( gatewayNode ) {
16431658 backhauls . push ( {
16441659 id : 'wan' , a : gatewayNode . router . name , b : 'Internet' , kind : 'wan' ,
1645- type : 'topology.links.fiberWan' , speed : wan . plan , signal : `${ wan . latencyMs } ms` ,
1660+ type : 'topology.links.fiberWan' , speed : wan . plan ,
1661+ ping : fmtPing ( wan . latencyMs ) , signal : '—' ,
16461662 tone : 'ok' , statusLabel : 'common.status.online' ,
16471663 spark : gatewayNode . router . sparkline , sparkColor : COLOR . accent ,
16481664 } )
16491665 }
1650- for ( const node of apNodes ) {
1666+ for ( const node of [ ...apNodes , ...switchNodes ] ) {
1667+ // #1384: el uplink lo lista TODA unidad de flota no-gateway, no solo los
1668+ // APs: el switch gestionado (roleBadge SW) también cuelga del gateway y
1669+ // su enlace (p. ej. Flint2 <-> KP-9000 a 2,5 Gbps) estaba en el mapa y
1670+ // en la semántica pero no en la tabla. No hay duplicado con el bucle
1671+ // 'dist' de abajo: la sombra distnode del switch se absorbe server-side
1672+ // (#1374/#1376) y no llega en distributionNodes.
16511673 const isWifi = node . router . backhaul === 'wifi'
16521674 backhauls . push ( {
16531675 id : `uplink-${ node . id } ` , a : `${ uplinkParentName ( node . id ) } ${ uplinkPortSuffix ( node . id ) } ` , b : node . router . name , kind : 'uplink' ,
@@ -1657,6 +1679,7 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
16571679 // boca (#1321); "—" si nadie la midió. El tono sí es información
16581680 // real: un enlace por wifi merece mirarse más que uno por cable.
16591681 speed : uplinkSpeed ( node . id ) ,
1682+ ping : fmtPing ( node . router . latencyMs ) ,
16601683 signal : '—' ,
16611684 tone : isWifi ? 'warn' : 'ok' ,
16621685 statusLabel : isWifi ? 'common.status.warn' : 'common.status.online' ,
@@ -1680,7 +1703,7 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
16801703 id : `dist-${ dv . id } ` , a,
16811704 b : [ dv . node . name , dv . node . ip , via , dv . node . portLabel ?? dv . node . port ] . filter ( Boolean ) . join ( ' · ' ) ,
16821705 kind : 'dist' , type : dv . node . role === 'ap' ? 'topology.links.managedAp' : 'topology.links.managedSwitch' ,
1683- speed : linkSpeed ( dv . node . speedMbps ) , signal : '—' ,
1706+ speed : linkSpeed ( dv . node . speedMbps ) , ping : fmtPing ( rn ?. router . latencyMs ) , signal : '—' ,
16841707 tone : 'ok' , statusLabel : 'common.status.online' ,
16851708 spark, sparkColor : COLOR . accent ,
16861709 } )
@@ -1689,7 +1712,7 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
16891712 id : `dist-${ dv . id } ` , a,
16901713 b : '' , bKey : 'topology.links.inferredSwitch' , bVars : { port : dv . node . portLabel ?? dv . node . port } ,
16911714 kind : 'dist' , type : 'common.cable' ,
1692- speed : linkSpeed ( dv . node . speedMbps ) , signal : '—' ,
1715+ speed : linkSpeed ( dv . node . speedMbps ) , ping : fmtPing ( rn ?. router . latencyMs ) , signal : '—' ,
16931716 tone : 'ok' , statusLabel : 'common.status.online' ,
16941717 spark, sparkColor : COLOR . ok ,
16951718 } )
@@ -1707,7 +1730,10 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
17071730 id : `wired-${ host . id } ` , a : hubNameOf ( host ) ,
17081731 b : [ host . name , port , `${ ctCountByHost . get ( host . id ) ?? 0 } CT` ] . filter ( Boolean ) . join ( ' · ' ) ,
17091732 kind : 'wired' , type : 'topology.links.hypervisorCable' ,
1710- speed : linkSpeed ( host . speedMbps ) , signal : '—' ,
1733+ // #1384: primero la boca medida por el padre en este sondeo (sem
1734+ // wired, fillWiredSpeeds); si no, el sello del controlador en el
1735+ // device; si ninguno, "—" honesta.
1736+ speed : linkSpeed ( wiredSemSpeedByTo . get ( host . id ) ?? host . speedMbps ) , ping : '—' , signal : '—' ,
17111737 tone : 'ok' , statusLabel : 'common.status.online' ,
17121738 spark : host . sparkline , sparkColor : COLOR . ok ,
17131739 } )
@@ -1717,7 +1743,7 @@ export function buildTopologyModel({ routers, devices, wan, wireguard, distribut
17171743 const device = devices . find ( ( d ) => d . id === node . peer . id )
17181744 backhauls . push ( {
17191745 id : `wg-${ node . peer . id } ` , a : 'Internet' , b : node . peer . name , kind : 'wg' ,
1720- type : 'WireGuard' , speed : '—' , signal : '—' ,
1746+ type : 'WireGuard' , speed : '—' , ping : '—' , signal : '—' ,
17211747 tone : 'tunnel' , statusLabel : 'common.active' ,
17221748 spark : device ?. sparkline ?? [ 0 , 0 ] , sparkColor : COLOR . tunnel ,
17231749 } )
0 commit comments