@@ -32,7 +32,8 @@ def __init__(self, alpaca_config: AlpacaConfig, detect_config: DetectConfig):
3232 self .transaction_lock = threading .Lock ()
3333
3434 # Device state
35- self .connected_clients : Dict [Tuple [str , int ], datetime ] = {} # (IP, ClientID) -> ConnectionTime
35+ self .connected_clients : Dict [Tuple [str , int ], datetime ] = {} # (IP, ClientID) -> Connection Start Time
36+ self .client_last_seen : Dict [Tuple [str , int ], datetime ] = {} # (IP, ClientID) -> Last Heartbeat Time
3637 self .disconnected_clients : Dict [Tuple [str , int ], Tuple [datetime , datetime ]] = {} # (IP, ClientID) -> (ConnectionTime, DisconnectionTime)
3738 self .connection_lock = threading .Lock ()
3839 self .connecting = False
@@ -167,24 +168,36 @@ def _prune_stale_clients(self):
167168 cutoff_time = now - timedelta (seconds = CLIENT_TIMEOUT_SECONDS )
168169
169170 stale_clients = []
170- for key , last_seen in list (self .connected_clients .items ()):
171+ # Check last_seen for staleness, not initial connection time
172+ for key , last_seen in list (self .client_last_seen .items ()):
171173 if last_seen < cutoff_time :
172174 stale_clients .append (key )
173175
174176 for key in stale_clients :
175177 client_ip , client_id = key
176- conn_time = self .connected_clients [key ]
178+
179+ # Retrieve original connection time for the record
180+ conn_time = self .connected_clients .get (key , now )
181+
182+ # Move to disconnected list
177183 self .disconnected_clients [key ] = (conn_time , now )
178- del self .connected_clients [key ]
184+
185+ # Remove from active tracking
186+ if key in self .connected_clients :
187+ del self .connected_clients [key ]
188+ if key in self .client_last_seen :
189+ del self .client_last_seen [key ]
190+
179191 logger .warning (f"Watchdog: Pruned stale client { client_ip } (ID: { client_id } ) - "
180- f"inactive for { (now - conn_time ).total_seconds ():.0f} s" )
192+ f"inactive for { (now - last_seen ).total_seconds ():.0f} s" )
181193
182194 def register_heartbeat (self , client_ip : str , client_id : int ):
183195 """Update the last seen timestamp for a connected client"""
184196 with self .connection_lock :
185197 key = (client_ip , client_id )
186198 if key in self .connected_clients :
187- self .connected_clients [key ] = get_current_time (self .alpaca_config .timezone )
199+ # Only update last_seen, preserve connected_clients (start time)
200+ self .client_last_seen [key ] = get_current_time (self .alpaca_config .timezone )
188201
189202 def _setup_mqtt (self ):
190203 """Setup and return MQTT client based on detect_config"""
@@ -334,7 +347,9 @@ def connect(self, client_ip: str, client_id: int):
334347 """Connect a client to the device"""
335348 with self .connection_lock :
336349 key = (client_ip , client_id )
337- self .connected_clients [key ] = get_current_time (self .alpaca_config .timezone )
350+ current_time = get_current_time (self .alpaca_config .timezone )
351+ self .connected_clients [key ] = current_time
352+ self .client_last_seen [key ] = current_time
338353
339354 # Remove from disconnected clients if reconnecting
340355 if key in self .disconnected_clients :
@@ -358,6 +373,7 @@ def disconnect(self, client_ip: str = None, client_id: int = None):
358373 disc_time = get_current_time (self .alpaca_config .timezone )
359374 self .disconnected_clients [key ] = (conn_time , disc_time )
360375 self .connected_clients .clear ()
376+ self .client_last_seen .clear ()
361377 self .disconnected_at = disc_time
362378 if self .connected_at :
363379 duration = (self .disconnected_at - self .connected_at ).total_seconds ()
@@ -372,6 +388,9 @@ def disconnect(self, client_ip: str = None, client_id: int = None):
372388 self .disconnected_clients [key ] = (conn_time , disc_time )
373389
374390 del self .connected_clients [key ]
391+ if key in self .client_last_seen :
392+ del self .client_last_seen [key ]
393+
375394 logger .info (f"Client disconnected: { client_ip } (ID: { client_id } ). Total clients: { len (self .connected_clients )} " )
376395
377396 if len (self .connected_clients ) == 0 :
0 commit comments