feat(core): 优化在线版下ampy和kflash的进度显示

This commit is contained in:
王立帮
2025-11-02 19:18:11 +08:00
parent e82a2bd791
commit 535abbc8e1
5 changed files with 131 additions and 34 deletions

View File

@@ -98,6 +98,44 @@ class EditorAce extends EditorBase {
}
}
replaceLine(lineNumber, newContent) {
const session = this.#editor_.getSession();
const totalLines = session.getLength();
let targetLine = lineNumber < 0 ? totalLines + lineNumber : lineNumber;
if (targetLine < 0) {
targetLine = 0;
} else if (targetLine >= totalLines) {
targetLine = totalLines - 1;
}
const cursorPos = this.#editor_.getCursorPosition();
const oldLine = session.getLine(targetLine);
const range = new ace.Range(targetLine, 0, targetLine, oldLine.length);
session.replace(range, newContent);
if (cursorPos.row > targetLine) {
this.#editor_.moveCursorTo(cursorPos.row, cursorPos.column);
} else if (cursorPos.row === targetLine) {
const newCol = Math.min(cursorPos.column, newContent.length);
this.#editor_.moveCursorTo(targetLine, newCol);
} else {
this.#editor_.moveCursorTo(cursorPos.row, cursorPos.column);
}
this.#editor_.renderer.scrollCursorIntoView(null, 0.5);
}
appendLine(text) {
const session = this.#editor_.getSession();
const totalLines = session.getLength();
const lastLineIndex = totalLines - 1;
const lastLineText = session.getLine(lastLineIndex);
if (lastLineText.trim()) {
session.insert({ row: totalLines, column: 0 }, `\n${text}`);
} else {
const range = new ace.Range(lastLineIndex, 0, lastLineIndex, lastLineText.length);
session.replace(range, text);
}
this.scrollToBottom();
}
getValue() {
return this.#destroyed_ ? '' : this.#editor_.getValue();
}

View File

@@ -1560,6 +1560,7 @@
"path",
"Mustache",
"Mixly.Env",
"Mixly.Events",
"Mixly.Msg",
"Mixly.Ampy",
"Mixly.Web"

View File

@@ -3,6 +3,7 @@ goog.loadJs('web', () => {
goog.require('path');
goog.require('Mustache');
goog.require('Mixly.Env');
goog.require('Mixly.Events');
goog.require('Mixly.Msg');
goog.require('Mixly.Ampy');
goog.require('Mixly.Web');
@@ -10,6 +11,7 @@ goog.provide('Mixly.Web.Ampy');
const {
Env,
Events,
Msg,
Ampy,
Web
@@ -37,6 +39,7 @@ class AmpyExt extends Ampy {
#writeBuffer_ = true;
#active_ = false;
#dataLength_ = 256;
#events_ = new Events(['message', 'replaceMessage'])
constructor(device, writeBuffer = true, dataLength = 256) {
super();
this.#device_ = device;
@@ -56,6 +59,25 @@ class AmpyExt extends Ampy {
});
}
bind(...args) {
return this.#events_.bind(...args);
}
message(message) {
this.#events_.run('message', message);
}
replaceMessage(lineNumber, message) {
this.#events_.run('replaceMessage', lineNumber, message);
}
getProgressMessage(name, percent) {
const sended = parseInt(percent * 45);
const left = percent === 0 ? '' : Array(sended).fill('=').join('');
const right = percent === 100 ? '' : Array(45 - sended).fill('-').join('');
return `${name} → |${left}${right}| ${(percent * 100).toFixed(1)}%`;
}
isActive() {
return this.#active_;
}
@@ -235,6 +257,8 @@ class AmpyExt extends Ampy {
if (!this.isActive()) {
throw new Error(Msg.Lang['ampy.portIsNotOpen']);
}
this.message(`Writing ${filename}...\n`);
this.message(this.getProgressMessage('', 0));
await this.exec(`file = open('${filename}', 'wb')`, timeout);
let buffer = null;
if (data.constructor === String) {
@@ -245,8 +269,15 @@ class AmpyExt extends Ampy {
buffer = data;
}
const len = Math.ceil(buffer.length / 64);
if (!len) {
this.replaceMessage(-1, this.getProgressMessage('', 1));
}
let sendedLength = 0;
for (let i = 0; i < len; i++) {
const writeBuffer = buffer.slice(i * 64, Math.min((i + 1) * 64, buffer.length));
sendedLength += writeBuffer.length;
const percent = sendedLength / buffer.length;
this.replaceMessage(-1, this.getProgressMessage('', percent));
let writeStr = '';
for (let num of writeBuffer) {
let numStr = num.toString(16);
@@ -258,6 +289,7 @@ class AmpyExt extends Ampy {
await this.exec(`file.write(b'${writeStr}')`, timeout);
}
await this.exec('file.close()', timeout);
this.message('\n');
}
async ls(directory = '/', longFormat = true, recursive = false, timeout = 5000) {
@@ -387,6 +419,8 @@ class AmpyExt extends Ampy {
this.#active_ = false;
await this.#device_.dispose();
this.#device_ = null;
this.#events_.reset();
this.#events_ = null;
}
}

View File

@@ -418,9 +418,7 @@ BU.burnWithKFlash = async (binFile, erase) => {
mainStatusBarTabs.changeTo('output');
BU.progressLayer.title(`${Msg.Lang['shell.burning']}...`);
BU.progressLayer.show();
statusBarTerminal.addValue(Msg.Lang['shell.bin.reading'] + "...");
statusBarTerminal.addValue("\n");
let data = [];
try {
for (let i of binFile) {
@@ -437,30 +435,32 @@ BU.burnWithKFlash = async (binFile, erase) => {
}
}
} catch (error) {
statusBarTerminal.addValue("Failed!\n" + Msg.Lang['shell.bin.readFailed'] + "\n");
statusBarTerminal.addValue("\n" + error + "\n", true);
statusBarTerminal.addValue(`\n[ERROR] ${error}\n`, true);
BU.progressLayer.hide();
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
return;
}
statusBarTerminal.addValue("Done!\n");
let serial = null;
try {
serial = new Serial(portName);
const kflash = new KFlash(serial, (message) => {
const kflash = new KFlash(serial);
kflash.bind('message', (message) => {
statusBarTerminal.addValue(message);
});
kflash.bind('replaceMessage', (lineNumber, message) => {
statusBarTerminal.replaceLine(lineNumber, message);
});
await kflash.enter();
for (let item of data) {
await kflash.write(item.data, item.address, item.sha256Prefix ?? true, item?.filename ?? 'main.bin');
}
BU.progressLayer.hide();
layer.msg(Msg.Lang['shell.burnSucc'], { time: 1000 });
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnSucc']}==\n`);
statusBarTerminal.appendLine(`==${Msg.Lang['shell.burnSucc']}==\n`);
} catch (error) {
statusBarTerminal.addValue(`[ERROR] ${error.message}\n`);
statusBarTerminal.appendLine(`[ERROR] ${error.message}\n`);
BU.progressLayer.hide();
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
statusBarTerminal.appendLine(`==${Msg.Lang['shell.burnFailed']}==\n`);
} finally {
try {
serial && await serial.close();
@@ -640,7 +640,7 @@ BU.uploadWithAmpy = async (portName) => {
let statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
BU.burning = false;
BU.uploading = true;
statusBarTerminal.setValue(Msg.Lang['shell.uploading'] + '...\n');
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
mainStatusBarTabs.show();
mainStatusBarTabs.changeTo('output');
const mainWorkspace = Workspace.getMain();
@@ -650,12 +650,21 @@ BU.uploadWithAmpy = async (portName) => {
BU.progressLayer.show();
const serial = new Serial(portName);
const ampy = new Ampy(serial);
ampy.bind('message', (message) => {
statusBarTerminal.addValue(message);
});
ampy.bind('replaceMessage', (lineNumber, message) => {
statusBarTerminal.replaceLine(lineNumber, message);
});
const code = editor.getCode();
let closePromise = Promise.resolve();
if (statusBarSerial) {
closePromise = statusBarSerial.close();
}
try {
await closePromise;
await ampy.enter();
await ampy.put('main.py', code);
/*const importsMap = BU.getImportModules(code);
let libraries = {};
for (let key in importsMap) {
@@ -665,13 +674,8 @@ BU.uploadWithAmpy = async (portName) => {
data,
size: importsMap[key]['__size__']
};
}*/
await closePromise;
await ampy.enter();
statusBarTerminal.addValue('Writing main.py ');
await ampy.put('main.py', code);
statusBarTerminal.addValue('Done!\n');
/*const cwd = await ampy.cwd();
}
let cwd = await ampy.cwd();
const rootInfo = await ampy.ls(cwd);
let rootMap = {};
for (let item of rootInfo) {
@@ -683,19 +687,17 @@ BU.uploadWithAmpy = async (portName) => {
if (libraries && libraries instanceof Object) {
for (let key in libraries) {
if (rootMap[`${cwd}/${key}`] !== undefined && rootMap[`${cwd}/${key}`] === libraries[key].size) {
statusBarTerminal.addValue(`Skip ${key}\n`);
statusBarTerminal.addValue(`Writing ${key} (Skipped)\n`);
continue;
}
statusBarTerminal.addValue(`Writing ${key} `);
await ampy.put(key, libraries[key].data);
statusBarTerminal.addValue('Done!\n');
}
}*/
await ampy.exit();
await ampy.dispose();
BU.progressLayer.hide();
layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadSucc']}==\n`);
statusBarTerminal.appendLine(`==${Msg.Lang['shell.uploadSucc']}==\n`);
if (!statusBarSerial) {
mainStatusBarTabs.add('serial', portName);
statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
@@ -707,8 +709,8 @@ BU.uploadWithAmpy = async (portName) => {
ampy.dispose();
BU.progressLayer.hide();
Debug.error(error);
statusBarTerminal.addValue(`${error}\n`);
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadFailed']}==\n`);
statusBarTerminal.appendLine(`[ERROR] ${error.message}\n`);
statusBarTerminal.appendLine(`==${Msg.Lang['shell.uploadFailed']}==\n`);
}
}

View File

@@ -100,7 +100,7 @@ class MAIXLoader {
#port_ = null;
#readedBuffer_ = [];
#events_ = new Events('message');
#events_ = new Events(['message', 'replaceMessage']);
constructor(port) {
this.#port_ = port;
@@ -117,6 +117,10 @@ class MAIXLoader {
this.#events_.run('message', message);
}
replaceMessage(lineNumber, message) {
this.#events_.run('replaceMessage', lineNumber, message);
}
async write(packet) {
let handlePacket = [];
@@ -251,9 +255,9 @@ class MAIXLoader {
getProgressMessage(name, percent) {
const sended = parseInt(percent * 45);
const left = Array(sended).fill('=').join('');
const right = Array(45 - sended).fill('-').join('');
return `${name}: |${left}${right}| ${(percent * 100).toFixed(1)}%\n\n`;
const left = percent === 0 ? '' : Array(sended).fill('=').join('');
const right = percent === 100 ? '' : Array(45 - sended).fill('-').join('');
return `${name}: |${left}${right}| ${(percent * 100).toFixed(1)}%`;
}
async flashDataFrame(data, address = 0x80000000) {
@@ -261,6 +265,7 @@ class MAIXLoader {
const dataLength = data.length;
let sendedLength = 0;
const len = Math.ceil(dataLength / this.DATAFRAME_SIZE);
this.message('\n');
for (let i = 0; i < len; i++) {
let start = i * this.DATAFRAME_SIZE;
let end = Math.min(start + this.DATAFRAME_SIZE, dataLength);
@@ -286,12 +291,18 @@ class MAIXLoader {
await this.write(packet);
if (await this.recvDebug()) {
sendedLength += chunk.length;
this.message(this.getProgressMessage('Downloading ISP', sendedLength / dataLength));
const message = this.getProgressMessage('Downloading ISP', sendedLength / dataLength);
if (i > 0) {
this.replaceMessage(-1, message);
} else {
this.message(message);
}
address += this.DATAFRAME_SIZE;
break;
}
}
}
this.message('\n\n');
}
async installFlashBootloader(data) {
@@ -390,7 +401,7 @@ class MAIXLoader {
}
async flashFirmware(firmware, aesKey = null, address = 0, sha256Prefix = true, filename = '') {
this.message(`[INFO] Writing ${filename} into ${this.formatHex(address)}\n\n`);
this.message(`[INFO] Writing ${filename} into ${this.formatHex(address)}\n`);
let firmwareBin = Array.from(new Uint8Array(firmware));
if (sha256Prefix) {
const aesCipherFlag = 0x00;
@@ -432,18 +443,25 @@ class MAIXLoader {
await this.write(packet);
if (await this.recvDebug()) {
sendedLength += chunkLength;
this.message(this.getProgressMessage('Programming BIN', sendedLength / dataLength));
const message = this.getProgressMessage('Programming BIN', sendedLength / dataLength);
if (i > 0) {
this.replaceMessage(-1, message);
} else {
this.message(`\n${message}`);
}
address += this.ISP_FLASH_DATA_FRAME_SIZE;
break;
}
}
}
this.message('\n\n');
}
dispose() {
this.#port_ = null;
this.#events_.reset();
this.#events_ = null;
this.#readedBuffer_ = null;
}
}
@@ -451,10 +469,13 @@ class KFlash {
#port_ = null;
#loader_ = null;
constructor(port, messageCallback) {
constructor(port) {
this.#port_ = port;
this.#loader_ = new MAIXLoader(port);
this.#loader_.bind('message', messageCallback);
}
bind(...args) {
return this.#loader_.bind(...args);
}
async enter() {
@@ -462,7 +483,7 @@ class KFlash {
}
async write(firmware, address = 0x0, sha256Prefix = true, filename = '') {
this.#loader_.message('[INFO] Trying to Enter the ISP Mode...\n');
this.#loader_.message('[INFO] Trying to Enter the ISP Mode...');
let retryCount = 0;
while (true) {
try {
@@ -500,6 +521,7 @@ class KFlash {
dispose() {
this.#loader_.dispose();
this.#loader_ = null;
this.#port_ = null;
}
}