@@ -156,10 +156,7 @@ class Monitor {
156156 . linkSource ( 'source' )
157157 . linkTarget ( 'target' )
158158 . linkColor ( link => {
159- const sourceNode = this . gData . nodes . find ( n => n . ipport === link . source ) ;
160- const targetNode = this . gData . nodes . find ( n => n . ipport === link . target ) ;
161- return ( sourceNode && this . offlineNodes . has ( sourceNode . ip ) ) ||
162- ( targetNode && this . offlineNodes . has ( targetNode . ip ) ) ? '#ff0000' : '#999' ;
159+ return '#999' ;
163160 } )
164161 . linkOpacity ( 0.6 )
165162 . linkWidth ( 2 )
@@ -274,7 +271,7 @@ class Monitor {
274271 } ;
275272
276273 if ( ! nodeData . status ) {
277- this . offlineNodes . add ( nodeData . ip ) ;
274+ this . offlineNodes . add ( nodeId ) ;
278275 this . log ( `Node ${ nodeData . ip } :${ nodeData . port } marked as offline from initial data` ) ;
279276 }
280277
@@ -364,7 +361,7 @@ class Monitor {
364361
365362 // Track offline nodes
366363 if ( ! nodeData . status ) {
367- this . offlineNodes . add ( nodeData . ip ) ;
364+ this . offlineNodes . add ( ` ${ nodeData . ip } : ${ nodeData . port } ` ) ;
368365 this . log ( 'Node marked as offline during initialization:' , nodeData . ip ) ;
369366 }
370367
@@ -411,7 +408,7 @@ class Monitor {
411408 const sourceIP = sourceNode . ip ;
412409
413410 // Skip if source node is offline
414- if ( this . offlineNodes . has ( sourceIP ) ) {
411+ if ( this . offlineNodes . has ( sourceNode . ipport ) ) {
415412 this . log ( 'Skipping links for offline source node:' , sourceIP ) ;
416413 continue ;
417414 }
@@ -421,7 +418,7 @@ class Monitor {
421418 const targetIP = targetNode . ip ;
422419
423420 // Skip if target node is offline
424- if ( this . offlineNodes . has ( targetIP ) ) {
421+ if ( this . offlineNodes . has ( targetNode . ipport ) ) {
425422 this . log ( 'Skipping link to offline target node:' , targetIP ) ;
426423 continue ;
427424 }
@@ -480,9 +477,15 @@ class Monitor {
480477 this . gData . nodes [ existingNodeIndex ] = updatedNode ;
481478 }
482479
483- // If node is offline, remove its links but preserve others
484- if ( ! data . status || this . offlineNodes . has ( data . ip ) ) {
485- this . log ( 'Node is offline, removing its links' ) ;
480+ // If node is offline, remove its links and set color to red
481+ if ( ! data . status || this . offlineNodes . has ( nodeId ) ) {
482+ this . log ( 'Node is offline, removing its links and setting color to red' ) ;
483+ // Set color to red
484+ const idx = this . gData . nodes . findIndex ( n => n . ipport === nodeId ) ;
485+ if ( idx !== - 1 ) {
486+ this . gData . nodes [ idx ] . color = '#ff0000' ;
487+ }
488+ // Remove all links involving this node
486489 this . gData . links = this . gData . links . filter ( link => {
487490 const sourceIP = typeof link . source === 'object' ? link . source . ipport : link . source ;
488491 const targetIP = typeof link . target === 'object' ? link . target . ipport : link . target ;
@@ -491,69 +494,7 @@ class Monitor {
491494 return ;
492495 }
493496
494- // For online nodes, update their connections
495- if ( data . neighbors ) {
496- // Parse neighbors using consistent format
497- const neighbors = data . neighbors . split ( / [ \s , ] + / ) . filter ( ip => ip . trim ( ) !== '' ) ;
498- this . log ( 'Processing neighbors:' , neighbors ) ;
499-
500- // Get current links for this node
501- const currentLinks = this . gData . links . filter ( link => {
502- const sourceIP = typeof link . source === 'object' ? link . source . ipport : link . source ;
503- const targetIP = typeof link . target === 'object' ? link . target . ipport : link . target ;
504- return sourceIP === nodeId || targetIP === nodeId ;
505- } ) ;
506-
507- // Create a set of current neighbor IDs
508- const currentNeighbors = new Set (
509- currentLinks . map ( link => {
510- const neighborId = link . source === nodeId ? link . target : link . source ;
511- return neighborId ;
512- } )
513- ) ;
514-
515- // Create a set of new neighbor IDs
516- const newNeighbors = new Set (
517- neighbors . map ( neighbor => {
518- const [ neighborIP , neighborPort ] = neighbor . split ( ':' ) ;
519- return `${ neighborIP } :${ neighborPort || data . port } ` ;
520- } )
521- ) ;
522-
523- // Only update if there are actual changes in neighbors
524- if ( ! this . areSetsEqual ( currentNeighbors , newNeighbors ) ) {
525- this . log ( 'Neighbor changes detected, updating links' ) ;
526-
527- // Remove existing links for this node
528- this . gData . links = this . gData . links . filter ( link => {
529- const sourceIP = typeof link . source === 'object' ? link . source . ipport : link . source ;
530- const targetIP = typeof link . target === 'object' ? link . target . ipport : link . target ;
531- return sourceIP !== nodeId && targetIP !== nodeId ;
532- } ) ;
533-
534- // Add new links for online neighbors
535- neighbors . forEach ( neighbor => {
536- const neighborIP = neighbor . split ( ':' ) [ 0 ] ;
537- if ( ! this . offlineNodes . has ( neighborIP ) ) {
538- const normalizedNeighbor = neighbor . includes ( ':' ) ? neighbor : `${ neighbor } :${ data . port } ` ;
539- const neighborNode = this . gData . nodes . find ( n =>
540- n . ipport === normalizedNeighbor ||
541- n . ipport . split ( ':' ) [ 0 ] === neighborIP
542- ) ;
543-
544- if ( neighborNode ) {
545- this . gData . links . push ( {
546- source : nodeId ,
547- target : normalizedNeighbor ,
548- value : 1.0
549- } ) ;
550- }
551- }
552- } ) ;
553- } else {
554- this . log ( 'No neighbor changes detected, skipping link update' ) ;
555- }
556- }
497+ // Modifying links based on individual WebSocket updates leads to blinking/race conditions.
557498 }
558499
559500 randomFloatFromInterval ( min , max ) {
@@ -602,7 +543,7 @@ class Monitor {
602543
603544 getNodeColor ( node ) {
604545 // Check if the node is offline using the IP
605- if ( this . offlineNodes . has ( node . ip ) ) {
546+ if ( this . offlineNodes . has ( node . ipport ) ) {
606547 return '#ff0000' ; // Red color for offline nodes
607548 }
608549
@@ -952,6 +893,11 @@ class Monitor {
952893 return sourceIP !== nodeId && targetIP !== nodeId ;
953894 } ) ;
954895
896+ const idx = this . gData . nodes . findIndex ( n => n . ipport === nodeId ) ;
897+ if ( idx !== - 1 ) {
898+ this . gData . nodes [ idx ] . color = '#ff0000' ;
899+ }
900+
955901 this . log ( `Removed ${ previousLinkCount - this . gData . links . length } links for node ${ nodeId } ` ) ;
956902
957903 // Remove lines from map
@@ -965,6 +911,7 @@ class Monitor {
965911 }
966912
967913 // Also remove links from other nodes to this offline node
914+ const offlineNodeIpPort = `${ data . ip } :${ data . port } ` ;
968915 Object . entries ( this . droneMarkers ) . forEach ( ( [ uid , marker ] ) => {
969916 if ( marker . neighbors ) {
970917 const neighbors = Array . isArray ( marker . neighbors )
@@ -974,7 +921,10 @@ class Monitor {
974921 : [ ] ) ;
975922
976923 // If this marker has the offline node as a neighbor, update its lines
977- if ( neighbors . some ( ip => ip . startsWith ( data . ip ) ) ) {
924+ if ( neighbors . some ( neighbor => {
925+ const normalizedNeighbor = neighbor . includes ( ':' ) ? neighbor : `${ neighbor } :${ marker . port } ` ;
926+ return normalizedNeighbor === offlineNodeIpPort ;
927+ } ) ) {
978928 this . updateNeighborLines ( uid , marker . getLatLng ( ) , neighbors , true ) ;
979929 }
980930 }
@@ -1126,6 +1076,7 @@ class Monitor {
11261076 this . updateDronePosition (
11271077 nodeData . uid ,
11281078 nodeData . ip ,
1079+ nodeData . port ,
11291080 nodeData . latitude ,
11301081 nodeData . longitude ,
11311082 neighborsIPs ,
@@ -1142,10 +1093,11 @@ class Monitor {
11421093 }
11431094 }
11441095
1145- updateDronePosition ( uid , ip , lat , lng , neighborIPs , neighborsDistance ) {
1096+ updateDronePosition ( uid , ip , port , lat , lng , neighborIPs , neighborsDistance ) {
11461097 this . log ( 'Updating drone position:' , { uid, ip, lat, lng } ) ;
11471098 const droneId = uid ;
11481099 const newLatLng = new L . LatLng ( lat , lng ) ;
1100+ const ipport = `${ ip } :${ port } ` ;
11491101
11501102 // Create popup content with node information
11511103 const popupContent = `
@@ -1160,10 +1112,10 @@ class Monitor {
11601112 this . log ( 'Creating new marker for node:' , droneId ) ;
11611113 // Create new marker
11621114 const marker = L . marker ( newLatLng , {
1163- icon : this . offlineNodes . has ( ip ) ? this . droneIconOffline : this . droneIcon ,
1115+ icon : this . offlineNodes . has ( ipport ) ? this . droneIconOffline : this . droneIcon ,
11641116 title : `Node ${ uid } ` ,
11651117 alt : `Node ${ uid } ` ,
1166- className : this . offlineNodes . has ( ip ) ? 'drone-offline' : ''
1118+ className : this . offlineNodes . has ( ipport ) ? 'drone-offline' : ''
11671119 } ) . addTo ( this . map ) ;
11681120
11691121 marker . bindPopup ( popupContent , {
@@ -1180,14 +1132,15 @@ class Monitor {
11801132 } ) ;
11811133
11821134 marker . ip = ip ;
1135+ marker . port = port ;
11831136 marker . neighbors = neighborIPs ;
11841137 marker . neighbors_distance = neighborsDistance ;
11851138 this . droneMarkers [ droneId ] = marker ;
11861139 this . log ( 'Marker created and added to map:' , marker ) ;
11871140 } else {
11881141 this . log ( 'Updating existing marker for node:' , droneId ) ;
11891142 // Update existing marker
1190- if ( this . offlineNodes . has ( ip ) ) {
1143+ if ( this . offlineNodes . has ( ipport ) ) {
11911144 this . droneMarkers [ droneId ] . setIcon ( this . droneIconOffline ) ;
11921145 this . droneMarkers [ droneId ] . getElement ( ) . classList . add ( 'drone-offline' ) ;
11931146 } else {
@@ -1211,7 +1164,8 @@ class Monitor {
12111164 this . cleanupDroneLines ( droneId ) ;
12121165
12131166 // If no neighbors or drone is offline, don't create any lines
1214- if ( ! neighborsIPs || neighborsIPs . length === 0 || ! this . droneMarkers [ droneId ] || this . offlineNodes . has ( this . droneMarkers [ droneId ] . ip ) ) {
1167+ const droneMarker = this . droneMarkers [ droneId ] ;
1168+ if ( ! neighborsIPs || neighborsIPs . length === 0 || ! droneMarker || this . offlineNodes . has ( `${ droneMarker . ip } :${ droneMarker . port } ` ) ) {
12151169 this . log ( 'No neighbors or drone is offline, skipping line creation' ) ;
12161170 return ;
12171171 }
@@ -1229,7 +1183,7 @@ class Monitor {
12291183
12301184 if ( neighborMarker ) {
12311185 // Skip if neighbor is offline
1232- if ( this . offlineNodes . has ( neighborIPOnly ) ) {
1186+ if ( this . offlineNodes . has ( ` ${ neighborMarker . ip } : ${ neighborMarker . port } ` ) ) {
12331187 this . log ( 'Skipping line creation - neighbor is offline:' , neighborIPOnly ) ;
12341188 return ;
12351189 }
@@ -1383,14 +1337,16 @@ class Monitor {
13831337 startStaleNodeCheck ( ) {
13841338 // Check for stale nodes every 5 seconds
13851339 setInterval ( ( ) => {
1340+ const numberOfNodes = this . gData . nodes . length ;
1341+ const staleThreshold = 20000 + ( numberOfNodes * 500 ) ;
1342+
13861343 const currentTime = Date . now ( ) ;
1387- const staleThreshold = 20000 ; // 20 seconds in milliseconds
13881344
13891345 // Check all nodes for staleness
13901346 this . nodeTimestamps . forEach ( ( timestamp , nodeId ) => {
13911347 const timeSinceLastUpdate = currentTime - timestamp ;
13921348 if ( timeSinceLastUpdate > staleThreshold ) {
1393- this . log ( `Node ${ nodeId } is stale (${ timeSinceLastUpdate } ms since last update) ` ) ;
1349+ this . log ( `Node ${ nodeId } is stale (${ timeSinceLastUpdate } ms > ${ staleThreshold } ms). Marking as offline. ` ) ;
13941350 this . markNodeAsOffline ( nodeId ) ;
13951351 }
13961352 } ) ;
@@ -1408,7 +1364,7 @@ class Monitor {
14081364 this . log ( `Marking node ${ nodeId } as offline` ) ;
14091365
14101366 // Add to offline nodes set
1411- this . offlineNodes . add ( node . ip ) ;
1367+ this . offlineNodes . add ( nodeId ) ;
14121368
14131369 // Update node color in graph data
14141370 const nodeIndex = this . gData . nodes . findIndex ( n => n . ipport === nodeId ) ;
@@ -1534,7 +1490,7 @@ class Monitor {
15341490 } ;
15351491
15361492 if ( ! nodeData . status ) {
1537- this . offlineNodes . add ( nodeData . ip ) ;
1493+ this . offlineNodes . add ( nodeId ) ;
15381494 this . log ( `Node ${ nodeData . ip } :${ nodeData . port } marked as offline from status check` ) ;
15391495 }
15401496
@@ -1551,6 +1507,7 @@ class Monitor {
15511507 this . updateDronePosition (
15521508 nodeData . uid ,
15531509 nodeData . ip ,
1510+ nodeData . port ,
15541511 parseFloat ( nodeData . latitude ) ,
15551512 parseFloat ( nodeData . longitude ) ,
15561513 nodeData . neighbors ? nodeData . neighbors . split ( / [ \s , ] + / ) . filter ( ip => ip . trim ( ) !== '' ) : [ ] ,
@@ -1805,17 +1762,27 @@ class Monitor {
18051762 } ) ;
18061763 } ) ;
18071764
1808- // Create links between online nodes
1765+ // Create links between online nodes only
18091766 nodesTable . forEach ( sourceNode => {
18101767 if ( sourceNode . status && sourceNode . neighbors ) {
18111768 const neighbors = sourceNode . neighbors . split ( / [ \s , ] + / ) . filter ( ip => ip . trim ( ) !== '' ) ;
18121769 neighbors . forEach ( neighbor => {
1813- const [ neighborIP , neighborPort ] = neighbor . split ( ':' ) ;
1814- const targetNode = nodesTable . find ( n => n . ip === neighborIP && n . port === neighborPort ) ;
1815- if ( targetNode && targetNode . status ) {
1770+ let neighborIpPort ;
1771+ if ( neighbor . includes ( ':' ) ) {
1772+ neighborIpPort = neighbor ;
1773+ } else {
1774+ // Try to find the port from nodesTable
1775+ const found = nodesTable . find ( n => n . ip === neighbor ) ;
1776+ neighborIpPort = found ? `${ found . ip } :${ found . port } ` : `${ neighbor } :${ sourceNode . port } ` ;
1777+ }
1778+ const isOffline = this . offlineNodes . has ( neighborIpPort ) ;
1779+ this . log ( 'Checking neighbor' , neighborIpPort , 'offline?' , isOffline ) ;
1780+ // Find the target node and ensure it is online
1781+ const targetNode = nodesTable . find ( n => `${ n . ip } :${ n . port } ` === neighborIpPort && n . status ) ;
1782+ if ( targetNode && ! isOffline ) {
18161783 this . gData . links . push ( {
18171784 source : `${ sourceNode . ip } :${ sourceNode . port } ` ,
1818- target : ` ${ neighborIP } : ${ neighborPort } ` ,
1785+ target : neighborIpPort ,
18191786 value : this . randomFloatFromInterval ( 1.0 , 1.3 )
18201787 } ) ;
18211788 }
@@ -1826,8 +1793,8 @@ class Monitor {
18261793
18271794 updateAllMarkers ( ) {
18281795 Object . entries ( this . droneMarkers ) . forEach ( ( [ uid , marker ] ) => {
1829- const ip = marker . ip ;
1830- if ( this . offlineNodes . has ( ip ) ) {
1796+ const ipport = ` ${ marker . ip } : ${ marker . port } ` ;
1797+ if ( this . offlineNodes . has ( ipport ) ) {
18311798 marker . setIcon ( this . droneIconOffline ) ;
18321799 marker . getElement ( ) . classList . add ( 'drone-offline' ) ;
18331800 } else {
0 commit comments