11import { webSerialDevices , vendorIdNames } from "./devices" ;
22import GUI from "../gui" ;
3+ import logger from "../logger" ;
34
45const logHead = "[WEBSERIAL]" ;
56
@@ -57,7 +58,7 @@ class WebSerial extends EventTarget {
5758 this . reading = false ;
5859
5960 if ( ! navigator ?. serial ) {
60- console . error ( `${ logHead } Web Serial API not supported` ) ;
61+ logger . error ( `${ logHead } Web Serial API not supported` ) ;
6162 return ;
6263 }
6364
@@ -92,7 +93,7 @@ class WebSerial extends EventTarget {
9293 }
9394
9495 handleDisconnect ( ) {
95- console . log ( `${ logHead } Device disconnected externally` ) ;
96+ logger . info ( `${ logHead } Device disconnected externally` ) ;
9697 this . disconnect ( ) ;
9798 }
9899
@@ -119,7 +120,7 @@ class WebSerial extends EventTarget {
119120 const ports = await navigator . serial . getPorts ( ) ;
120121 this . ports = ports . map ( ( port ) => this . createPort ( port ) ) ;
121122 } catch ( error ) {
122- console . error ( `${ logHead } Error loading devices:` , error ) ;
123+ logger . error ( `${ logHead } Error loading devices:` , error ) ;
123124 }
124125 }
125126
@@ -135,9 +136,9 @@ class WebSerial extends EventTarget {
135136 if ( ! newPermissionPort ) {
136137 newPermissionPort = this . handleNewDevice ( userSelectedPort ) ;
137138 }
138- console . info ( `${ logHead } User selected SERIAL device from permissions:` , newPermissionPort . path ) ;
139+ logger . info ( `${ logHead } User selected SERIAL device from permissions:` , newPermissionPort . path ) ;
139140 } catch ( error ) {
140- console . error ( `${ logHead } User didn't select any SERIAL device when requesting permission:` , error ) ;
141+ logger . error ( `${ logHead } User didn't select any SERIAL device when requesting permission:` , error ) ;
141142 }
142143 return newPermissionPort ;
143144 }
@@ -150,7 +151,7 @@ class WebSerial extends EventTarget {
150151 async connect ( path , options = { baudRate : 115200 } ) {
151152 // Prevent double connections
152153 if ( this . connected ) {
153- console . log ( `${ logHead } Already connected, not connecting again` ) ;
154+ logger . info ( `${ logHead } Already connected, not connecting again` ) ;
154155 return true ;
155156 }
156157
@@ -160,7 +161,7 @@ class WebSerial extends EventTarget {
160161 try {
161162 const device = this . ports . find ( ( device ) => device . path === path ) ;
162163 if ( ! device ) {
163- console . error ( `${ logHead } Device not found:` , path ) ;
164+ logger . error ( `${ logHead } Device not found:` , path ) ;
164165 this . dispatchEvent ( new CustomEvent ( "connect" , { detail : false } ) ) ;
165166 return false ;
166167 }
@@ -173,7 +174,7 @@ class WebSerial extends EventTarget {
173174 this . connectionInfo = connectionInfo ;
174175 this . isNeedBatchWrite = this . checkIsNeedBatchWrite ( ) ;
175176 if ( this . isNeedBatchWrite ) {
176- console . log ( `${ logHead } Enabling batch write mode for AT32 on macOS` ) ;
177+ logger . info ( `${ logHead } Enabling batch write mode for AT32 on macOS` ) ;
177178 }
178179 this . writer = this . port . writable . getWriter ( ) ;
179180 this . reader = this . port . readable . getReader ( ) ;
@@ -190,7 +191,7 @@ class WebSerial extends EventTarget {
190191 this . port . addEventListener ( "disconnect" , this . handleDisconnect ) ;
191192 this . addEventListener ( "receive" , this . handleReceiveBytes ) ;
192193
193- console . log ( `${ logHead } Connection opened with ID: ${ this . connectionId } , Baud: ${ options . baudRate } ` ) ;
194+ logger . info ( `${ logHead } Connection opened with ID: ${ this . connectionId } , Baud: ${ options . baudRate } ` ) ;
194195
195196 this . dispatchEvent ( new CustomEvent ( "connect" , { detail : connectionInfo } ) ) ;
196197
@@ -202,7 +203,7 @@ class WebSerial extends EventTarget {
202203 } else if ( connectionInfo && this . openCanceled ) {
203204 this . connectionId = path ;
204205
205- console . log ( `${ logHead } Connection opened with ID: ${ path } , but request was canceled, disconnecting` ) ;
206+ logger . info ( `${ logHead } Connection opened with ID: ${ path } , but request was canceled, disconnecting` ) ;
206207 // some bluetooth dongles/dongle drivers really doesn't like to be closed instantly, adding a small delay
207208 setTimeout ( ( ) => {
208209 this . openRequested = false ;
@@ -214,12 +215,102 @@ class WebSerial extends EventTarget {
214215 return false ;
215216 } else {
216217 this . openRequested = false ;
217- console . log ( `${ logHead } Failed to open serial port` ) ;
218+ logger . warn ( `${ logHead } Failed to open serial port` ) ;
218219 this . dispatchEvent ( new CustomEvent ( "connect" , { detail : false } ) ) ;
219220 return false ;
220221 }
221222 } catch ( error ) {
222- console . error ( `${ logHead } Error connecting:` , error ) ;
223+ logger . error ( `${ logHead } Error connecting:` , error ) ;
224+
225+ // If the port is already open (InvalidStateError) we can attempt to
226+ // recover by attaching to the already-open port. Some browser
227+ // implementations throw when open() is called concurrently but the
228+ // underlying port is already usable. Try to initialize reader/writer
229+ // and proceed as a successful connection.
230+ if (
231+ error &&
232+ ( error . name === "InvalidStateError" ||
233+ ( typeof error . message === "string" && error . message . includes ( "already open" ) ) )
234+ ) {
235+ // NOTE: Some browser implementations throw InvalidStateError when
236+ // open() is called concurrently while the underlying port is
237+ // already open. In those cases, we try to attach to the already-
238+ // open port by obtaining the reader/writer objects. If we can
239+ // obtain both reader and writer we treat the port as successfully
240+ // recovered; otherwise we perform a graceful failure path below.
241+ try {
242+ const connectionInfo = this . port ?. getInfo ? this . port . getInfo ( ) : null ;
243+ this . connectionInfo = connectionInfo ;
244+ this . isNeedBatchWrite = connectionInfo ? this . checkIsNeedBatchWrite ( ) : false ;
245+ // Attempt to obtain writer/reader if available
246+ try {
247+ if ( this . port ?. writable && ! this . writer ) {
248+ this . writer = this . port . writable . getWriter ( ) ;
249+ }
250+ } catch ( e ) {
251+ logger . warn ( `${ logHead } Could not get writer from already-open port:` , e ) ;
252+ }
253+
254+ try {
255+ if ( this . port ?. readable && ! this . reader ) {
256+ this . reader = this . port . readable . getReader ( ) ;
257+ }
258+ } catch ( e ) {
259+ logger . warn ( `${ logHead } Could not get reader from already-open port:` , e ) ;
260+ }
261+
262+ if ( connectionInfo ) {
263+ if ( ! this . writer || ! this . reader ) {
264+ logger . warn ( `${ logHead } Recovered port missing reader/writer` ) ;
265+ // Release partial writer/reader locks before aborting recovery
266+ if ( this . writer ) {
267+ try {
268+ this . writer . releaseLock ( ) ;
269+ } catch ( releaseErr ) {
270+ logger . warn ( `${ logHead } Failed to release partial writer lock:` , releaseErr ) ;
271+ }
272+ this . writer = null ;
273+ }
274+ if ( this . reader ) {
275+ try {
276+ this . reader . releaseLock ?. ( ) ;
277+ } catch ( releaseErr ) {
278+ logger . warn ( `${ logHead } Failed to release partial reader lock:` , releaseErr ) ;
279+ }
280+ this . reader = null ;
281+ }
282+ // Reset request state and signal failure to callers instead of throwing
283+ this . openRequested = false ;
284+ this . dispatchEvent ( new CustomEvent ( "connect" , { detail : false } ) ) ;
285+ return false ;
286+ }
287+ this . connected = true ;
288+ this . connectionId = path ;
289+ this . bitrate = options . baudRate ;
290+ this . bytesReceived = 0 ;
291+ this . bytesSent = 0 ;
292+ this . failed = 0 ;
293+ this . openRequested = false ;
294+
295+ this . port . addEventListener ( "disconnect" , this . handleDisconnect ) ;
296+ this . addEventListener ( "receive" , this . handleReceiveBytes ) ;
297+
298+ logger . info ( `${ logHead } Recovered already-open serial port, ID: ${ this . connectionId } ` ) ;
299+ this . dispatchEvent ( new CustomEvent ( "connect" , { detail : connectionInfo } ) ) ;
300+
301+ // Start read loop if not already running
302+ if ( ! this . reading ) {
303+ this . reading = true ;
304+ this . readLoop ( ) ;
305+ }
306+
307+ return true ;
308+ }
309+ } catch ( e ) {
310+ logger . warn ( `${ logHead } Failed to recover already-open port:` , e ) ;
311+ }
312+ }
313+
223314 this . openRequested = false ;
224315 this . dispatchEvent ( new CustomEvent ( "connect" , { detail : false } ) ) ;
225316 return false ;
@@ -232,7 +323,7 @@ class WebSerial extends EventTarget {
232323 this . dispatchEvent ( new CustomEvent ( "receive" , { detail : value } ) ) ;
233324 }
234325 } catch ( error ) {
235- console . error ( `${ logHead } Error reading:` , error ) ;
326+ logger . error ( `${ logHead } Error reading:` , error ) ;
236327 if ( this . connected ) {
237328 this . disconnect ( ) ;
238329 }
@@ -272,7 +363,7 @@ class WebSerial extends EventTarget {
272363 try {
273364 await this . reader . cancel ( ) ;
274365 } catch ( e ) {
275- console . warn ( `${ logHead } Reader cancel error (can be ignored):` , e ) ;
366+ logger . warn ( `${ logHead } Reader cancel error (can be ignored):` , e ) ;
276367 }
277368 }
278369
@@ -284,7 +375,7 @@ class WebSerial extends EventTarget {
284375 try {
285376 this . writer . releaseLock ( ) ;
286377 } catch ( e ) {
287- console . warn ( `${ logHead } Writer release error (can be ignored):` , e ) ;
378+ logger . warn ( `${ logHead } Writer release error (can be ignored):` , e ) ;
288379 }
289380 this . writer = null ;
290381 }
@@ -295,12 +386,12 @@ class WebSerial extends EventTarget {
295386 try {
296387 await this . port . close ( ) ;
297388 } catch ( e ) {
298- console . warn ( `${ logHead } Port already closed or error during close:` , e ) ;
389+ logger . warn ( `${ logHead } Port already closed or error during close:` , e ) ;
299390 }
300391 this . port = null ;
301392 }
302393
303- console . log (
394+ logger . info (
304395 `${ logHead } Connection with ID: ${ this . connectionId } closed, Sent: ${ this . bytesSent } bytes, Received: ${ this . bytesReceived } bytes` ,
305396 ) ;
306397
@@ -312,7 +403,7 @@ class WebSerial extends EventTarget {
312403 this . dispatchEvent ( new CustomEvent ( "disconnect" , { detail : true } ) ) ;
313404 return true ;
314405 } catch ( error ) {
315- console . error ( `${ logHead } Error disconnecting:` , error ) ;
406+ logger . error ( `${ logHead } Error disconnecting:` , error ) ;
316407 this . closeRequested = false ;
317408 // Ensure connectionInfo is reset even on error if port was potentially open
318409 this . connectionInfo = null ;
@@ -341,7 +432,7 @@ class WebSerial extends EventTarget {
341432 try {
342433 await this . writer . write ( sliceData ) ;
343434 } catch ( error ) {
344- console . error ( `${ logHead } Error writing batch chunk:` , error ) ;
435+ logger . error ( `${ logHead } Error writing batch chunk:` , error ) ;
345436 throw error ; // Re-throw to be caught by the send method
346437 }
347438 }
@@ -350,7 +441,7 @@ class WebSerial extends EventTarget {
350441
351442 async send ( data , callback ) {
352443 if ( ! this . connected || ! this . writer ) {
353- console . error ( `${ logHead } Failed to send data, serial port not open` ) ;
444+ logger . error ( `${ logHead } Failed to send data, serial port not open` ) ;
354445 if ( callback ) {
355446 callback ( { bytesSent : 0 } ) ;
356447 }
@@ -371,7 +462,7 @@ class WebSerial extends EventTarget {
371462 }
372463 return result ;
373464 } catch ( error ) {
374- console . error ( `${ logHead } Error sending data:` , error ) ;
465+ logger . error ( `${ logHead } Error sending data:` , error ) ;
375466 if ( callback ) {
376467 callback ( { bytesSent : 0 } ) ;
377468 }
0 commit comments