From 53c62b70280b7a7b35ee0dc4416ab9aeeaa8d00a Mon Sep 17 00:00:00 2001 From: Robert Blackhart Date: Wed, 30 Oct 2024 17:52:09 -0400 Subject: [PATCH 1/2] update the connection code to be flexible on the prompt characters --- src/rawmode.js | 24 +++++++++++++++++++++--- src/transports.js | 21 ++++++++++++++------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/rawmode.js b/src/rawmode.js index 5fec9ce..ac4873f 100644 --- a/src/rawmode.js +++ b/src/rawmode.js @@ -8,6 +8,10 @@ import { report } from "./utils" +// >>> is the standard MicroPython prompt +// --> is the aiorepl prompt +let replPrompts = ['>>> ', '--> '] + export class MpRawMode { constructor(port) { this.port = port @@ -25,13 +29,26 @@ export class MpRawMode { return res } + async detectPrompt() { + await this.port.write('\r\x01') // Ctrl-A: enter raw REPL + await this.port.readUntil('raw REPL; CTRL-B to exit\r\n') + // detect what the system prompt is and add it to our list of prompts to look for + const userPrompt = `${(await this.exec('print(sys.ps1)')).trim()} ` + if (!replPrompts.includes(userPrompt)) { + console.log(`Detected user prompt as ${userPrompt}`) + replPrompts.push(userPrompt) + } + await this.port.write('\x02') // Ctrl-B: exit raw REPL + } + async interruptProgram(timeout=20000) { const endTime = Date.now() + timeout while (timeout <= 0 || (Date.now() < endTime)) { await this.port.write('\x03') // Ctrl-C: interrupt any running program try { - let banner = await this.port.readUntil('>>> ', 500) - if (this.port.prevRecvCbk && banner != '\r\n>>> ') { + let banner = await this.port.readUntil(replPrompts, 500) + let promptRegex = RegExp(`\r\n(?:${replPrompts.join('|')})`) + if (this.port.prevRecvCbk && !promptRegex.test(banner)) { this.port.prevRecvCbk(banner) } await this.port.flushInput() @@ -46,6 +63,7 @@ export class MpRawMode { async enterRawRepl(soft_reboot=false) { const release = await this.port.startTransaction() try { + await this.detectPrompt() await this.interruptProgram() await this.port.write('\r\x01') // Ctrl-A: enter raw REPL @@ -60,7 +78,7 @@ export class MpRawMode { try { await this.port.write('\x02') // Ctrl-B: exit raw REPL await this.port.readUntil('>\r\n') - await this.port.readUntil('>>> ') + await this.port.readUntil(replPrompts) } finally { release() } diff --git a/src/transports.js b/src/transports.js index 7a8da4e..72bd3e3 100644 --- a/src/transports.js +++ b/src/transports.js @@ -127,17 +127,24 @@ export class Transport { throw new Error('Timeout') } - async readUntil(ending, timeout=5000) { + async readUntil(endings, timeout=5000) { + let err_message = 'any of the ending sequences' + if (!Array.isArray(endings)) { + endings = [endings] + err_message = 'the ending sequence' + } if (!this.inTransaction) { throw new Error('Not in transaction') } let endTime = Date.now() + timeout while (timeout <= 0 || (Date.now() < endTime)) { - const idx = this.receivedData.indexOf(ending) + ending.length - if (idx >= ending.length) { - const res = this.receivedData.substring(0, idx) - this.receivedData = this.receivedData.substring(idx) - return res + for (let ending of endings) { + const idx = this.receivedData.indexOf(ending) + ending.length + if (idx >= ending.length) { + const res = this.receivedData.substring(0, idx) + this.receivedData = this.receivedData.substring(idx) + return res + } } const prev_avail = this.receivedData.length await sleep(10) @@ -145,7 +152,7 @@ export class Transport { endTime = Date.now() + timeout } } - throw new Error('Timeout reached before finding the ending sequence') + throw new Error(`Timeout reached before finding ${err_message}`) } } From 0eb17f61febcccdcbb322fb57858077511c3d593 Mon Sep 17 00:00:00 2001 From: Robert Blackhart Date: Fri, 1 Nov 2024 20:08:20 -0400 Subject: [PATCH 2/2] little reorganization of when the prompt detection happens --- src/rawmode.js | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/rawmode.js b/src/rawmode.js index ac4873f..6dd711a 100644 --- a/src/rawmode.js +++ b/src/rawmode.js @@ -8,9 +8,7 @@ import { report } from "./utils" -// >>> is the standard MicroPython prompt -// --> is the aiorepl prompt -let replPrompts = ['>>> ', '--> '] +let replPrompts = [] export class MpRawMode { constructor(port) { @@ -19,6 +17,7 @@ export class MpRawMode { static async begin(port, soft_reboot=false) { const res = new MpRawMode(port) + await res.detectPrompt() await res.enterRawRepl(soft_reboot) try { await res.exec(`import sys,os`) @@ -30,15 +29,32 @@ export class MpRawMode { } async detectPrompt() { - await this.port.write('\r\x01') // Ctrl-A: enter raw REPL - await this.port.readUntil('raw REPL; CTRL-B to exit\r\n') - // detect what the system prompt is and add it to our list of prompts to look for - const userPrompt = `${(await this.exec('print(sys.ps1)')).trim()} ` - if (!replPrompts.includes(userPrompt)) { - console.log(`Detected user prompt as ${userPrompt}`) - replPrompts.push(userPrompt) + const release = await this.port.startTransaction() + + // >>> is the standard MicroPython prompt + // --> is the aiorepl prompt + replPrompts = ['>>> ', '--> '] + + try { + await this.port.write('\r\x01') // Ctrl-A: enter raw REPL + await this.port.readUntil('raw REPL; CTRL-B to exit\r\n') + // detect what the system prompt is and add it to our list of prompts to look for + const userPrompt = `${(await this.exec('import sys; print(sys.ps1)')).trim()} ` + if (!replPrompts.includes(userPrompt)) { + console.log(`Detected user prompt as ${userPrompt}`) + replPrompts.push(userPrompt) + } + await this.port.write('\x02') // Ctrl-B: exit raw REPL + await this.port.readUntil('>\r\n') + let activePrompt = await this.port.readUntil(replPrompts) + // this is a hack because in the other cases, the prompt gets printed agian, but in the + // aiorepl case, it doesn't. Not sure why at the moment so I'm doing this + if (activePrompt == '--> ') { + await this.port.write(activePrompt) + } + } finally { + release() } - await this.port.write('\x02') // Ctrl-B: exit raw REPL } async interruptProgram(timeout=20000) { @@ -63,7 +79,6 @@ export class MpRawMode { async enterRawRepl(soft_reboot=false) { const release = await this.port.startTransaction() try { - await this.detectPrompt() await this.interruptProgram() await this.port.write('\r\x01') // Ctrl-A: enter raw REPL