@@ -477,38 +477,96 @@ onboard_logging.initialize = function (callback) {
477477 }
478478
479479 function flash_save_begin ( ) {
480- if ( GUI . connected_to ) {
481- self . blockSize = self . BLOCK_SIZE ;
480+ if ( ! GUI . connected_to ) return ;
482481
483- flash_update_summary ( function ( ) {
484- const maxBytes = FC . DATAFLASH . usedSize ;
482+ self . blockSize = self . BLOCK_SIZE ;
485483
486- let openedFile ;
487- prepare_file ( function ( fileWriter ) {
488- let nextAddress = 0 ;
489- let totalBytesCompressed = 0 ;
484+ // Refresh occupied size in case it changed while the tab was open
485+ flash_update_summary ( ( ) => {
486+ const maxBytes = FC . DATAFLASH . usedSize ;
487+
488+ let openedFile ;
489+ let totalBytesCompressed = 0 ;
490490
491- show_saving_dialog ( ) ;
491+ show_saving_dialog ( ) ;
492492
493- const MAX_SIMPLE_RETRIES = 5 ;
494- const RETRY_BACKOFF_MS = 30 ;
495- let simpleRetryCount = 0 ;
493+ const MAX_SIMPLE_RETRIES = 5 ;
494+ const RETRY_BACKOFF_MS = 30 ; // ms
495+ const startTime = new Date ( ) . getTime ( ) ;
496496
497- const startTime = new Date ( ) . getTime ( ) ;
497+ prepare_file ( ( fileWriter ) => {
498+ FileSystem . openFile ( fileWriter ) . then ( ( file ) => {
499+ openedFile = file ;
500+
501+ let nextAddress = 0 ;
498502
499- function onChunkRead ( chunkAddress , chunkDataView , bytesCompressed ) {
500- // ... all recursive logic ...
503+ // Queue-based read function
504+ function readNextBlock ( ) {
505+ if ( saveCancelled || nextAddress >= maxBytes ) {
506+ mark_saving_dialog_done ( startTime , nextAddress , totalBytesCompressed ) ;
507+ FileSystem . closeFile ( openedFile ) ;
508+ return ;
509+ }
510+
511+ let simpleRetryCount = 0 ;
512+
513+ function attemptRead ( ) {
514+ mspHelper . dataflashRead (
515+ nextAddress ,
516+ self . blockSize ,
517+ ( chunkAddress , chunkDataView , bytesCompressed ) => {
518+ if ( chunkDataView && chunkDataView . byteLength > 0 ) {
519+ simpleRetryCount = 0 ;
520+
521+ const blob = new Blob ( [ chunkDataView ] ) ;
522+ FileSystem . writeChunk ( openedFile , blob ) ;
523+
524+ nextAddress += chunkDataView . byteLength ;
525+
526+ if ( typeof bytesCompressed === "number" ) {
527+ if ( totalBytesCompressed == null ) totalBytesCompressed = 0 ;
528+ totalBytesCompressed += bytesCompressed ;
529+ }
530+
531+ $ ( ".dataflash-saving progress" ) . attr ( "value" , ( nextAddress / maxBytes ) * 100 ) ;
532+
533+ // Continue with next block
534+ readNextBlock ( ) ;
535+ } else if ( chunkDataView && chunkDataView . byteLength === 0 ) {
536+ // Zero-length block → EOF
537+ mark_saving_dialog_done ( startTime , nextAddress , totalBytesCompressed ) ;
538+ FileSystem . closeFile ( openedFile ) ;
539+ } else {
540+ // Null/missing block
541+ if ( simpleRetryCount < MAX_SIMPLE_RETRIES ) {
542+ simpleRetryCount ++ ;
543+ if ( simpleRetryCount % 2 === 1 ) {
544+ console . warn (
545+ `Null/missing block at ${ nextAddress } , retry ${ simpleRetryCount } ` ,
546+ ) ;
547+ }
548+ setTimeout ( attemptRead , RETRY_BACKOFF_MS ) ;
549+ } else {
550+ console . error (
551+ `Skipping null block at ${ nextAddress } after ${ MAX_SIMPLE_RETRIES } retries` ,
552+ ) ;
553+ nextAddress += self . blockSize ;
554+ readNextBlock ( ) ;
555+ }
556+ }
557+ } ,
558+ ) ;
559+ }
560+
561+ attemptRead ( ) ;
501562 }
502563
503- // Fetch the initial block
504- FileSystem . openFile ( fileWriter ) . then ( ( file ) => {
505- openedFile = file ;
506- mspHelper . dataflashRead ( nextAddress , self . blockSize , onChunkRead ) ;
507- } ) ;
564+ // Start reading the first block
565+ readNextBlock ( ) ;
508566 } ) ;
509567 } ) ;
510- }
511- } // <-- this is the exact end of flash_save_begin
568+ } ) ;
569+ } // end of flash_save_begin
512570
513571 function prepare_file ( onComplete ) {
514572 const prefix = "BLACKBOX_LOG" ;
0 commit comments