@@ -13,6 +13,11 @@ export interface ScannedDevice {
1313 lastSeen : number ;
1414}
1515
16+ export interface ScanController {
17+ cancel : ( ) => void ;
18+ promise : Promise < ScannedDevice [ ] > ;
19+ }
20+
1621const TIMEOUT_MS = 2000 ; // 2 second timeout per device
1722const QUICK_PROBE_TIMEOUT_MS = 1000 ; // Faster timeout for known IPs
1823
@@ -185,108 +190,109 @@ async function getLocalIP(): Promise<string | null> {
185190 *
186191 * @param onProgress - Callback called as devices are found (passes devices array for real-time display)
187192 * @param recentlyUsedIPs - IPs that were recently found (should be provided by component)
188- * @returns Promise that resolves when scan is complete
193+ * @returns ScanController with cancel() and promise
189194 */
190- export async function scanForDevices (
195+ export function scanForDevices (
191196 onProgress ?: ( devices : ScannedDevice [ ] , checked : number , total : number ) => void ,
192197 recentlyUsedIPs : string [ ] = [ ]
193- ) : Promise < ScannedDevice [ ] > {
194- const devices : ScannedDevice [ ] = [ ] ;
195- let checkedCount = 0 ;
196-
197- // #region agent log - scan start
198- fetch ( 'http://127.0.0.1:7242/ingest/d3a9473c-c512-41da-a870-4c36bb5dd5ec' , { method :'POST' , headers :{ 'Content-Type' :'application/json' } , body :JSON . stringify ( { location :'networkScanService.ts:195' , message :'scanForDevices start' , data :{ recentIPsLength :recentlyUsedIPs . length } , timestamp :Date . now ( ) , hypothesisId :'H3_scan_progress' } ) } ) . catch ( ( ) => { } ) ;
199- // #endregion
198+ ) : ScanController {
199+ let cancelled = false ;
200200
201- console . log ( '[NetworkScan] Starting progressive device scan...' ) ;
202- console . log ( '[NetworkScan] Recently used IPs:' , recentlyUsedIPs ) ;
203-
204- // Phase 1: Quickly probe recently used IPs (should be instant if devices are online)
205- if ( recentlyUsedIPs . length > 0 ) {
206- console . log ( `[NetworkScan] Phase 1: Probing ${ recentlyUsedIPs . length } recently used IPs...` ) ;
201+ const promise = ( async ( ) : Promise < ScannedDevice [ ] > => {
202+ const devices : ScannedDevice [ ] = [ ] ;
203+ let checkedCount = 0 ;
207204
208- const recentResults = await Promise . all (
209- recentlyUsedIPs . map ( ip => probeIP ( ip , QUICK_PROBE_TIMEOUT_MS ) )
210- ) ;
211-
212- recentResults . forEach ( ( result , idx ) => {
213- if ( result ) {
214- console . log ( `[NetworkScan] Found recent device at ${ result . ip } ` ) ;
215- devices . push ( result ) ;
216- }
217- checkedCount ++ ;
218- } ) ;
219-
220- // #region agent log - phase 1 complete
221- fetch ( 'http://127.0.0.1:7242/ingest/d3a9473c-c512-41da-a870-4c36bb5dd5ec' , { method :'POST' , headers :{ 'Content-Type' :'application/json' } , body :JSON . stringify ( { location :'networkScanService.ts:219' , message :'Phase 1 complete' , data :{ devicesFound :devices . length , checkedCount} , timestamp :Date . now ( ) , hypothesisId :'H3_scan_progress' } ) } ) . catch ( ( ) => { } ) ;
222- // #endregion
223-
224- // Report progress after recent IPs checked - pass devices array for real-time display
225- const totalEstimate = recentlyUsedIPs . length + COMMON_IP_RANGES . reduce ( ( sum , r ) => sum + ( r . end - r . start + 1 ) , 0 ) ;
226- onProgress ?.( [ ...devices ] , checkedCount , totalEstimate ) ;
227- }
205+ console . log ( '[NetworkScan] Starting progressive device scan...' ) ;
206+ console . log ( '[NetworkScan] Recently used IPs:' , recentlyUsedIPs ) ;
207+
208+ // Phase 1: Quickly probe recently used IPs (should be instant if devices are online)
209+ if ( recentlyUsedIPs . length > 0 ) {
210+ console . log ( `[NetworkScan] Phase 1: Probing ${ recentlyUsedIPs . length } recently used IPs...` ) ;
211+
212+ const recentResults = await Promise . all (
213+ recentlyUsedIPs . map ( ip => probeIP ( ip , QUICK_PROBE_TIMEOUT_MS ) )
214+ ) ;
215+
216+ recentResults . forEach ( ( result , idx ) => {
217+ if ( result ) {
218+ console . log ( `[NetworkScan] Found recent device at ${ result . ip } ` ) ;
219+ devices . push ( result ) ;
220+ }
221+ checkedCount ++ ;
222+ } ) ;
228223
229- // Phase 2: Scan subnet ranges in parallel for faster coverage
230- console . log ( '[NetworkScan] Phase 2: Scanning full subnets in parallel...' ) ;
231-
232- const allIPs : string [ ] = [ ] ;
233- for ( const range of COMMON_IP_RANGES ) {
234- for ( let i = range . start ; i <= range . end ; i ++ ) {
235- allIPs . push ( `${ range . base } .${ i } ` ) ;
224+ // Report progress after recent IPs checked - pass devices array for real-time display
225+ const totalEstimate = recentlyUsedIPs . length + COMMON_IP_RANGES . reduce ( ( sum , r ) => sum + ( r . end - r . start + 1 ) , 0 ) ;
226+ onProgress ?.( [ ...devices ] , checkedCount , totalEstimate ) ;
236227 }
237- }
238-
239- // Filter out IPs we already checked
240- const recentIPSet = new Set ( recentlyUsedIPs ) ;
241- const uncheckedIPs = allIPs . filter ( ip => ! recentIPSet . has ( ip ) ) ;
242-
243- console . log ( `[NetworkScan] Scanning ${ uncheckedIPs . length } unchecked IP addresses` ) ;
244-
245- // #region agent log - phase 2 start
246- fetch ( 'http://127.0.0.1:7242/ingest/d3a9473c-c512-41da-a870-4c36bb5dd5ec' , { method :'POST' , headers :{ 'Content-Type' :'application/json' } , body :JSON . stringify ( { location :'networkScanService.ts:244' , message :'Phase 2 start' , data :{ uncheckedIPsLength :uncheckedIPs . length } , timestamp :Date . now ( ) , hypothesisId :'H3_scan_progress' } ) } ) . catch ( ( ) => { } ) ;
247- // #endregion
248228
249- // Probe in parallel batches for speed
250- const batchSize = 15 ; // Increased batch size since we're doing it faster
251-
252- for ( let i = 0 ; i < uncheckedIPs . length ; i += batchSize ) {
253- const batch = uncheckedIPs . slice ( i , i + batchSize ) ;
229+ // Phase 2: Scan subnet ranges in parallel for faster coverage
230+ console . log ( '[NetworkScan] Phase 2: Scanning full subnets in parallel...' ) ;
254231
255- const results = await Promise . all ( batch . map ( ip => probeIP ( ip ) ) ) ;
256-
257- results . forEach ( result => {
258- if ( result ) {
259- console . log ( `[NetworkScan] Found device at ${ result . ip } (type: ${ result . type } )` ) ;
260-
261- // Avoid duplicates
262- if ( ! devices . find ( d => d . ip === result . ip ) ) {
263- devices . push ( result ) ;
264- saveRecentIP ( result . ip ) ;
265- }
232+ const allIPs : string [ ] = [ ] ;
233+ for ( const range of COMMON_IP_RANGES ) {
234+ for ( let i = range . start ; i <= range . end ; i ++ ) {
235+ allIPs . push ( `${ range . base } .${ i } ` ) ;
266236 }
267- } ) ;
237+ }
268238
269- checkedCount += batch . length ;
239+ // Filter out IPs we already checked
240+ const recentIPSet = new Set ( recentlyUsedIPs ) ;
241+ const uncheckedIPs = allIPs . filter ( ip => ! recentIPSet . has ( ip ) ) ;
270242
271- // Call progress callback with current devices array for real-time UI updates
272- onProgress ?.( [ ...devices ] , recentlyUsedIPs . length + checkedCount , recentlyUsedIPs . length + allIPs . length ) ;
273- }
274-
275- console . log ( `[NetworkScan] Scan complete. Found ${ devices . length } devices.` ) ;
243+ console . log ( `[NetworkScan] Scanning ${ uncheckedIPs . length } unchecked IP addresses` ) ;
276244
277- // #region agent log - scan complete
278- fetch ( 'http://127.0.0.1:7242/ingest/d3a9473c-c512-41da-a870-4c36bb5dd5ec' , { method :'POST' , headers :{ 'Content-Type' :'application/json' } , body :JSON . stringify ( { location :'networkScanService.ts:279' , message :'Scan complete' , data :{ devicesFound :devices . length } , timestamp :Date . now ( ) , hypothesisId :'H3_scan_progress' } ) } ) . catch ( ( ) => { } ) ;
279- // #endregion
245+ // Probe in parallel batches for speed
246+ const batchSize = 15 ;
247+
248+ for ( let i = 0 ; i < uncheckedIPs . length ; i += batchSize ) {
249+ // Check if scan was cancelled
250+ if ( cancelled ) {
251+ console . log ( '[NetworkScan] Scan cancelled by user' ) ;
252+ break ;
253+ }
254+
255+ const batch = uncheckedIPs . slice ( i , i + batchSize ) ;
256+
257+ const results = await Promise . all ( batch . map ( ip => probeIP ( ip ) ) ) ;
258+
259+ results . forEach ( result => {
260+ if ( result ) {
261+ console . log ( `[NetworkScan] Found device at ${ result . ip } (type: ${ result . type } )` ) ;
262+
263+ // Avoid duplicates
264+ if ( ! devices . find ( d => d . ip === result . ip ) ) {
265+ devices . push ( result ) ;
266+ saveRecentIP ( result . ip ) ;
267+ }
268+ }
269+ } ) ;
280270
281- // Sort by type (telemetry first), then by IP
282- devices . sort ( ( a , b ) => {
283- if ( a . type !== b . type ) {
284- return a . type === 'telemetry' ? - 1 : 1 ;
271+ checkedCount += batch . length ;
272+
273+ // Call progress callback with current devices array for real-time UI updates
274+ onProgress ?. ( [ ... devices ] , recentlyUsedIPs . length + checkedCount , recentlyUsedIPs . length + allIPs . length ) ;
285275 }
286- return a . ip . localeCompare ( b . ip ) ;
287- } ) ;
288276
289- return devices ;
277+ console . log ( `[NetworkScan] Scan ${ cancelled ? 'cancelled' : 'complete' } . Found ${ devices . length } devices.` ) ;
278+
279+ // Sort by type (telemetry first), then by IP
280+ devices . sort ( ( a , b ) => {
281+ if ( a . type !== b . type ) {
282+ return a . type === 'telemetry' ? - 1 : 1 ;
283+ }
284+ return a . ip . localeCompare ( b . ip ) ;
285+ } ) ;
286+
287+ return devices ;
288+ } ) ( ) ;
289+
290+ return {
291+ cancel : ( ) => {
292+ cancelled = true ;
293+ } ,
294+ promise,
295+ } ;
290296}
291297
292298/**
0 commit comments