From add1c656c5484e1139fa041e62792289768979f2 Mon Sep 17 00:00:00 2001 From: keeramis Date: Wed, 15 Jan 2025 13:50:49 -0800 Subject: [PATCH] Adds following changes: - Show progress bar while flashing - Show location of output log file at the start of the flashing process. - Throw error is device is not in EDL mode or no device is detected. - Propagate any errors --- src/cmd/flash.js | 40 ++++++++++---------- src/lib/qdl.js | 95 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/src/cmd/flash.js b/src/cmd/flash.js index 8b8ad7cda..5194a91e5 100644 --- a/src/cmd/flash.js +++ b/src/cmd/flash.js @@ -71,7 +71,7 @@ module.exports = class FlashCommand extends CLICommandBase { } async flashTachyon({ verbose, files }) { - this.ui.write(`${os.EOL}Ensure only one device is connected to the computer${os.EOL}`); + this.ui.write(`${os.EOL}Ensure that only one device is connected to the computer before proceeding.${os.EOL}`); let zipFile; let includeDir = ''; @@ -101,27 +101,25 @@ module.exports = class FlashCommand extends CLICommandBase { filesToProgram = files; } - this.ui.write(`Starting download. The download may take several minutes...${os.EOL}`); - - const res = await qdl.run({ - files: filesToProgram, - includeDir, - updateFolder, - zip: zipFile, - verbose, - ui: this.ui - }); - // put the output in a log file if not verbose - if (!verbose) { - const outputLog = path.join(process.cwd(), `qdl-output-${Date.now()}.log`); - if (res?.stdout) { - await fs.writeFile(outputLog, res.stdout); - } - this.ui.write(`Download complete. Output log available at ${outputLog}${os.EOL}`); - } else { - this.ui.write(`Download complete${os.EOL}`); + this.ui.write(`Starting download. This may take several minutes...${os.EOL}`); + const outputLog = path.join(process.cwd(), `qdl-output-${Date.now()}.log`); + try { + // put the output in a log file if not verbose + this.ui.write(`Logs are being written to: ${outputLog}${os.EOL}`); + await qdl.run({ + files: filesToProgram, + includeDir, + updateFolder, + zip: zipFile, + verbose, + ui: this.ui, + outputLogFile: outputLog + }); + fs.appendFileSync(outputLog, 'Download complete.'); + } catch (error) { + this.ui.write('Download failed'); + fs.appendFileSync(outputLog, 'Download failed.'); } - // TODO: Handle errors } async _extractFlashFilesFromDir(dirPath) { diff --git a/src/lib/qdl.js b/src/lib/qdl.js index 079e23ed5..387ed2fc2 100644 --- a/src/lib/qdl.js +++ b/src/lib/qdl.js @@ -24,31 +24,94 @@ async function getExecutable() { return path.join(tmpDir, 'qdl' + (archName === 'win32' ? '.exe' : '')); } -/** - */ -async function run({ files, includeDir, updateFolder, zip, verbose, ui }) { - const qdl = await getExecutable(); - - const qdlArgs = [ - '--storage', - TACHYON_STORAGE_TYPE, +async function run({ files, includeDir, updateFolder, zip, ui, outputLogFile }) { + const qdlPath = '/Users/keerthyamisagadda/code/tachyon-qdl/qdl'; + await fs.chmod(qdlPath, 0o755); + + const qdlArguments = [ + '--storage', TACHYON_STORAGE_TYPE, ...(zip ? ['--zip', zip] : []), ...(includeDir ? ['--include', includeDir] : []), ...files ]; - if (verbose) { - ui.write(`Command: ${qdl} ${qdlArgs.join(' ')}${os.EOL}`); - } + const progressBar = ui.createProgressBar(); + let currentModuleName = '', currentModuleSectors = 0; + let totalSectorsInAllFiles = 0, totalSectorsFlashed = 0, progressBarInitialized = false; + + const handleError = (process, message) => { + progressBar.stop(); + ui.stdout.write(message); + process.kill(); + }; + + const processLogLine = (line, process) => { + fs.appendFileSync(outputLogFile, `${line}\n`); + + if (line.includes('Waiting for EDL device')) { + handleError(process, `Device is not in EDL mode${os.EOL}`); + } else if (line.includes('[ERROR]')) { + handleError(process, `${os.EOL}Error detected: ${line}${os.EOL}`); + } else if (line.includes('status=getProgramInfo')) { + const match = line.match(/sectors_total=(\d+)/); + if (match) { + totalSectorsInAllFiles += parseInt(match[1], 10); + } + } else if (line.includes('status=Start flashing module')) { + const moduleNameMatch = line.match(/module=(.*?),/); + const sectorsTotalMatch = line.match(/sectors_total=(\d+)/); + if (moduleNameMatch && sectorsTotalMatch) { + currentModuleName = moduleNameMatch[1]; + currentModuleSectors = parseInt(sectorsTotalMatch[1], 10); + + if (!progressBarInitialized) { + progressBarInitialized = true; + progressBar.start(totalSectorsInAllFiles, totalSectorsFlashed, { description: `Flashing ${currentModuleName}` }); + } else { + progressBar.update(totalSectorsFlashed, { description: `Flashing ${currentModuleName}` }); + } + } + } else if (line.includes('status=Flashing module')) { + const sectorsFlashedMatch = line.match(/sectors_done=(\d+)/); + if (sectorsFlashedMatch) { + const sectorsFlashed = parseInt(sectorsFlashedMatch[1], 10); + progressBar.update(totalSectorsFlashed + sectorsFlashed, { description: `Flashing module: ${currentModuleName} (${sectorsFlashed}/${currentModuleSectors} sectors)` }); - const res = await execa(qdl, qdlArgs, { - cwd: updateFolder || process.cwd(), - stdio: verbose ? 'inherit' : 'pipe' - }); + if (sectorsFlashed === currentModuleSectors) { + totalSectorsFlashed += currentModuleSectors; + progressBar.update({ description: `Flashed ${currentModuleName}` }); + } - return res; + if (totalSectorsFlashed === totalSectorsInAllFiles) { + progressBar.update({ description: 'Flashing complete' }); + progressBar.stop(); + } + } + } + }; + + try { + const qdlProcess = execa(qdlPath, qdlArguments, { cwd: updateFolder || process.cwd(), stdio: 'pipe' }); + + const handleStream = (stream) => { + stream.on('data', chunk => { + chunk.toString().split('\n').map(line => line.trim()).filter(Boolean).forEach(line => { + processLogLine(line, qdlProcess); + }); + }); + }; + + handleStream(qdlProcess.stdout, 'stdout'); + handleStream(qdlProcess.stderr, 'stderr'); + + await qdlProcess; + return; + } finally { + progressBar.stop(); + } } + module.exports = { run };