Compare commits
11 Commits
9bcc49059e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
708bdc6bf0 | ||
|
|
44b02f4781 | ||
|
|
48ce7f35e1 | ||
|
|
9b80cf73d3 | ||
|
|
67a3a3bd78 | ||
|
|
e8de8f19c2 | ||
|
|
7d2076f165 | ||
|
|
10d5254a7e | ||
|
|
a67284afe5 | ||
|
|
4ac522ffc6 | ||
|
|
5456419bb3 |
@@ -1,275 +1,302 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('layui');
|
||||
goog.require('dayjs.duration');
|
||||
goog.require('Mixly.Boards');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Workspace');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.WebSocket.Serial');
|
||||
goog.provide('Mixly.WebSocket.ArduShell');
|
||||
goog.require('layui');
|
||||
goog.require('dayjs.duration');
|
||||
goog.require('Mixly.Boards');
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Msg');
|
||||
goog.require('Mixly.Workspace');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.WebSocket.Serial');
|
||||
goog.require('Mixly.WebCompiler.ArduShell');
|
||||
goog.provide('Mixly.WebSocket.ArduShell');
|
||||
|
||||
const {
|
||||
Boards,
|
||||
Debug,
|
||||
LayerExt,
|
||||
Msg,
|
||||
Workspace,
|
||||
LayerProgress,
|
||||
WebSocket
|
||||
} = Mixly;
|
||||
const {
|
||||
Boards,
|
||||
Debug,
|
||||
LayerExt,
|
||||
Msg,
|
||||
Workspace,
|
||||
LayerProgress,
|
||||
WebSocket,
|
||||
WebCompiler = {}
|
||||
} = Mixly;
|
||||
|
||||
const { Serial } = WebSocket;
|
||||
// 动态获取 WebCompiler.ArduShell,用于本地上传
|
||||
const getWebCompilerArduShell = () => Mixly.WebCompiler?.ArduShell;
|
||||
|
||||
const { layer } = layui;
|
||||
const { Serial } = WebSocket;
|
||||
|
||||
const { layer } = layui;
|
||||
|
||||
|
||||
class WebSocketArduShell {
|
||||
static {
|
||||
this.mixlySocket = null;
|
||||
this.socket = null;
|
||||
this.shell = null;
|
||||
class WebSocketArduShell {
|
||||
static {
|
||||
this.mixlySocket = null;
|
||||
this.socket = null;
|
||||
this.shell = null;
|
||||
|
||||
this.getSocket = function () {
|
||||
return this.socket;
|
||||
}
|
||||
this.getSocket = function () {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
this.getMixlySocket = function () {
|
||||
return this.mixlySocket;
|
||||
}
|
||||
this.getMixlySocket = function () {
|
||||
return this.mixlySocket;
|
||||
}
|
||||
|
||||
this.init = function (mixlySocket) {
|
||||
this.mixlySocket = mixlySocket;
|
||||
this.socket = mixlySocket.getSocket();
|
||||
this.shell = new WebSocketArduShell();
|
||||
const socket = this.socket;
|
||||
this.init = function (mixlySocket) {
|
||||
this.mixlySocket = mixlySocket;
|
||||
this.socket = mixlySocket.getSocket();
|
||||
this.shell = new WebSocketArduShell();
|
||||
const socket = this.socket;
|
||||
|
||||
socket.on('arduino.dataEvent', (data) => {
|
||||
if (data.length > 1000) {
|
||||
return;
|
||||
// 同时初始化 WebCompiler.ArduShell,用于本地上传
|
||||
const WebCompilerArduShell = getWebCompilerArduShell();
|
||||
if (WebCompilerArduShell && !WebCompilerArduShell.getMixlySocket()) {
|
||||
WebCompilerArduShell.init(mixlySocket);
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
|
||||
socket.on('arduino.errorEvent', (data) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
try {
|
||||
data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
|
||||
data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
|
||||
this.initCompile = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.compiling']}...\n`);
|
||||
this.shell.compile(code)
|
||||
.then((info) => {
|
||||
this.endCallback(info.code, info.time);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.initUpload = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
|
||||
closePromise
|
||||
.then(() => {
|
||||
return this.shell.upload(port, code)
|
||||
})
|
||||
.then((info) => {
|
||||
this.endCallback(info.code, info.time);
|
||||
if (info.code || !Serial.portIsLegal(port)) {
|
||||
socket.on('arduino.dataEvent', (data) => {
|
||||
if (data.length > 1000) {
|
||||
return;
|
||||
}
|
||||
mainStatusBarTabs.add('serial', port);
|
||||
mainStatusBarTabs.changeTo(port);
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
statusBarSerial.open()
|
||||
.then(() => {
|
||||
const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
|
||||
if (!baudRates?.length) {
|
||||
return statusBarSerial.setBaudRate(9600);
|
||||
} else {
|
||||
return statusBarSerial.setBaudRate(baudRates[0] - 0);
|
||||
}
|
||||
})
|
||||
.catch(Debug.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
|
||||
this.endCallback = function (code, time) {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
let message = '';
|
||||
if (code) {
|
||||
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']);
|
||||
statusBarTerminal.addValue(`\n==${message}==\n`);
|
||||
} else {
|
||||
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
|
||||
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${
|
||||
dayjs.duration(time).format('HH:mm:ss.SSS')
|
||||
})==\n`);
|
||||
socket.on('arduino.errorEvent', (data) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
try {
|
||||
data = unescape(data.replace(/(_E[0-9A-F]{1}_[0-9A-F]{2}_[0-9A-F]{2})+/gm, '%$1'));
|
||||
data = unescape(data.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
|
||||
// 过滤掉无关紧要的端口错误(因为已经在本地完成上传了)
|
||||
if (typeof data === 'string' && (
|
||||
data.includes('cannot open serial1') ||
|
||||
data.includes('cannot open device "undefined"') ||
|
||||
data.includes('No such file or directory, cannot open')
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
statusBarTerminal.addValue(data);
|
||||
});
|
||||
}
|
||||
layer.msg(message, { time: 1000 });
|
||||
|
||||
this.initCompile = function () {
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.compiling']}...\n`);
|
||||
this.shell.compile(code)
|
||||
.then((info) => {
|
||||
this.endCallback(info.code, info.time);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.compileFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.initUpload = function () {
|
||||
// 委托给 WebCompiler.ArduShell 处理本地上传(使用 AVRUploader 或 esptool-js)
|
||||
// 服务器无法访问用户本地的串口设备,必须在浏览器端完成上传
|
||||
const WebCompilerArduShell = getWebCompilerArduShell();
|
||||
if (WebCompilerArduShell) {
|
||||
return WebCompilerArduShell.initUpload();
|
||||
}
|
||||
|
||||
if (!this.mixlySocket.isConnected()) {
|
||||
layer.msg(Msg.Lang['websocket.offline'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const port = Serial.getSelectedPortName();
|
||||
if (!port) {
|
||||
layer.msg(Msg.Lang['statusbar.serial.noDevice'], {
|
||||
time: 1000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
mainStatusBarTabs.show();
|
||||
statusBarTerminal.setValue(`${Msg.Lang['shell.uploading']}...\n`);
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
const closePromise = statusBarSerial ? statusBarSerial.close() : Promise.resolve();
|
||||
closePromise
|
||||
.then(() => {
|
||||
return this.shell.upload(port, code)
|
||||
})
|
||||
.then((info) => {
|
||||
this.endCallback(info.code, info.time);
|
||||
if (info.code || !Serial.portIsLegal(port)) {
|
||||
return;
|
||||
}
|
||||
mainStatusBarTabs.add('serial', port);
|
||||
mainStatusBarTabs.changeTo(port);
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(port);
|
||||
statusBarSerial.open()
|
||||
.then(() => {
|
||||
const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
|
||||
if (!baudRates?.length) {
|
||||
return statusBarSerial.setBaudRate(9600);
|
||||
} else {
|
||||
return statusBarSerial.setBaudRate(baudRates[0] - 0);
|
||||
}
|
||||
})
|
||||
.catch(Debug.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
statusBarTerminal.addValue(`\n==${Msg.Lang['shell.uploadFailed']}==\n`);
|
||||
});
|
||||
}
|
||||
|
||||
this.endCallback = function (code, time) {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
let message = '';
|
||||
if (code) {
|
||||
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileFailed'] : Msg.Lang['shell.uploadFailed']);
|
||||
statusBarTerminal.addValue(`\n==${message}==\n`);
|
||||
} else {
|
||||
message = (this.shell.isCompiling() ? Msg.Lang['shell.compileSucc'] : Msg.Lang['shell.uploadSucc']);
|
||||
statusBarTerminal.addValue(`\n==${message}(${Msg.Lang['shell.timeCost']} ${dayjs.duration(time).format('HH:mm:ss.SSS')
|
||||
})==\n`);
|
||||
}
|
||||
layer.msg(message, { time: 1000 });
|
||||
}
|
||||
}
|
||||
|
||||
#running_ = false;
|
||||
#upload_ = false;
|
||||
#killing_ = false;
|
||||
#layer_ = null;
|
||||
|
||||
constructor() {
|
||||
this.#layer_ = new LayerProgress({
|
||||
width: 200,
|
||||
cancelValue: Msg.Lang['nav.btn.stop'],
|
||||
skin: 'layui-anim layui-anim-scale',
|
||||
cancel: () => {
|
||||
if (this.#killing_) {
|
||||
return false;
|
||||
}
|
||||
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
|
||||
this.#killing_ = true;
|
||||
this.kill().catch(Debug.error);
|
||||
return false;
|
||||
},
|
||||
cancelDisplay: false
|
||||
});
|
||||
}
|
||||
|
||||
async compile(code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = false;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.compile', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async upload(port, code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = true;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code, port };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.upload', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async kill() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.kill', (response) => {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showProgress() {
|
||||
const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
|
||||
this.#layer_.title(`${message}...`);
|
||||
this.#layer_.show();
|
||||
}
|
||||
|
||||
hideProgress() {
|
||||
this.#layer_.hide();
|
||||
}
|
||||
|
||||
isUploading() {
|
||||
return this.#running_ && this.#upload_;
|
||||
}
|
||||
|
||||
isCompiling() {
|
||||
return this.#running_ && !this.#upload_;
|
||||
}
|
||||
}
|
||||
|
||||
#running_ = false;
|
||||
#upload_ = false;
|
||||
#killing_ = false;
|
||||
#layer_ = null;
|
||||
|
||||
constructor() {
|
||||
this.#layer_ = new LayerProgress({
|
||||
width: 200,
|
||||
cancelValue: Msg.Lang['nav.btn.stop'],
|
||||
skin: 'layui-anim layui-anim-scale',
|
||||
cancel: () => {
|
||||
if (this.#killing_) {
|
||||
return false;
|
||||
}
|
||||
this.#layer_.title(`${Msg.Lang['shell.aborting']}...`);
|
||||
this.#killing_ = true;
|
||||
this.kill().catch(Debug.error);
|
||||
return false;
|
||||
},
|
||||
cancelDisplay: false
|
||||
});
|
||||
}
|
||||
|
||||
async compile(code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = false;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.compile', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async upload(port, code) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.#running_ = true;
|
||||
this.#upload_ = true;
|
||||
this.#killing_ = false;
|
||||
this.showProgress();
|
||||
const key = Boards.getSelectedBoardCommandParam();
|
||||
const config = { key, code, port };
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.upload', config, (response) => {
|
||||
this.hideProgress();
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async kill() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const mixlySocket = WebSocketArduShell.getMixlySocket();
|
||||
mixlySocket.emit('arduino.kill', (response) => {
|
||||
if (response.error) {
|
||||
reject(response.error);
|
||||
return;
|
||||
}
|
||||
const [error, result] = response;
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showProgress() {
|
||||
const message = this.isCompiling() ? Msg.Lang['shell.compiling'] : Msg.Lang['shell.uploading'];
|
||||
this.#layer_.title(`${message}...`);
|
||||
this.#layer_.show();
|
||||
}
|
||||
|
||||
hideProgress() {
|
||||
this.#layer_.hide();
|
||||
}
|
||||
|
||||
isUploading() {
|
||||
return this.#running_ && this.#upload_;
|
||||
}
|
||||
|
||||
isCompiling() {
|
||||
return this.#running_ && !this.#upload_;
|
||||
}
|
||||
}
|
||||
|
||||
WebSocket.ArduShell = WebSocketArduShell;
|
||||
WebSocket.ArduShell = WebSocketArduShell;
|
||||
|
||||
});
|
||||
@@ -31,8 +31,8 @@ goog.loadJs('web', () => {
|
||||
|
||||
const { SELECTED_BOARD, BOARD } = Config;
|
||||
|
||||
// 引入本地 Web.BU 模块,用于 MicroPython 本地上传
|
||||
const WebBU = Mixly.Web?.BU;
|
||||
// WebBU 需要在使用时动态获取,避免模块加载顺序问题
|
||||
const getWebBU = () => Mixly.Web?.BU;
|
||||
|
||||
const { Serial } = WebSocket;
|
||||
|
||||
@@ -74,6 +74,7 @@ goog.loadJs('web', () => {
|
||||
|
||||
this.initBurn = function () {
|
||||
// MicroPython 板卡使用本地 Web Serial API 烧录,而非通过服务器
|
||||
const WebBU = getWebBU();
|
||||
if (SELECTED_BOARD?.language === 'MicroPython' && WebBU) {
|
||||
return WebBU.initBurn();
|
||||
}
|
||||
@@ -111,6 +112,7 @@ goog.loadJs('web', () => {
|
||||
|
||||
this.initUpload = function () {
|
||||
// MicroPython 板卡使用本地 Web Serial API 上传,而非通过服务器
|
||||
const WebBU = getWebBU();
|
||||
if (SELECTED_BOARD?.language === 'MicroPython' && WebBU) {
|
||||
return WebBU.initUpload();
|
||||
}
|
||||
|
||||
@@ -11,38 +11,60 @@ export default class ShellArduino extends Shell {
|
||||
async compile(config) {
|
||||
let arduino = _.merge({}, ARDUINO);
|
||||
arduino = _.merge(arduino, config);
|
||||
const command = [
|
||||
`"${arduino.path.cli}"`,
|
||||
|
||||
// 构建参数数组,避免 shell 解析引号问题
|
||||
const args = [
|
||||
'compile',
|
||||
'-b', arduino.key,
|
||||
'--config-file', `"${arduino.path.config}"`,
|
||||
'--verbose',
|
||||
'--libraries', `"${arduino.path.libraries.join('","')}"`,
|
||||
'--build-path', `"${arduino.path.build}"`,
|
||||
'--output-dir', `"${arduino.path.output}"`,
|
||||
`"${arduino.path.code}"`,
|
||||
'--config-file', arduino.path.config,
|
||||
'--verbose'
|
||||
];
|
||||
|
||||
// 为每个 library 路径添加参数
|
||||
// 注意:config.json 中的路径是指向包含多个库的集合目录,所以必须用 --libraries
|
||||
// 如果用 --library,arduino-cli 会尝试把该目录当作单个库处理,导致找不到头文件报错
|
||||
for (const lib of arduino.path.libraries) {
|
||||
args.push('--libraries', lib);
|
||||
}
|
||||
|
||||
args.push(
|
||||
'--build-path', arduino.path.build,
|
||||
'--output-dir', arduino.path.output,
|
||||
arduino.path.code,
|
||||
'--no-color'
|
||||
].join(' ');
|
||||
return this.execUntilClosed(command, { maxBuffer: 4096 * 1000000 });
|
||||
);
|
||||
|
||||
return this.execFileUntilClosed(arduino.path.cli, args, { maxBuffer: 4096 * 1000000 });
|
||||
}
|
||||
|
||||
async upload(config) {
|
||||
let arduino = _.merge({}, ARDUINO);
|
||||
arduino = _.merge(arduino, config);
|
||||
const command = [
|
||||
`"${arduino.path.cli}"`,
|
||||
|
||||
// 构建参数数组,避免 shell 解析引号问题
|
||||
const args = [
|
||||
'compile',
|
||||
'--upload',
|
||||
'-p', arduino.port,
|
||||
'-b', arduino.key,
|
||||
'--config-file', `"${arduino.path.config}"`,
|
||||
'--verbose',
|
||||
'--libraries', `"${arduino.path.libraries.join('","')}"`,
|
||||
'--build-path', `"${arduino.path.build}"`,
|
||||
'--output-dir', `"${arduino.path.output}"`,
|
||||
`"${arduino.path.code}"`,
|
||||
'--config-file', arduino.path.config,
|
||||
'--verbose'
|
||||
];
|
||||
|
||||
// 为每个 library 路径添加参数
|
||||
// 注意:config.json 中的路径是指向包含多个库的集合目录,所以必须用 --libraries
|
||||
// 如果用 --library,arduino-cli 会尝试把该目录当作单个库处理,导致找不到头文件报错
|
||||
for (const lib of arduino.path.libraries) {
|
||||
args.push('--libraries', lib);
|
||||
}
|
||||
|
||||
args.push(
|
||||
'--build-path', arduino.path.build,
|
||||
'--output-dir', arduino.path.output,
|
||||
arduino.path.code,
|
||||
'--no-color'
|
||||
].join(' ');
|
||||
return this.execUntilClosed(command, { maxBuffer: 4096 * 1000000 });
|
||||
);
|
||||
|
||||
return this.execFileUntilClosed(arduino.path.cli, args, { maxBuffer: 4096 * 1000000 });
|
||||
}
|
||||
}
|
||||
@@ -173,6 +173,12 @@ export default class Socket {
|
||||
});
|
||||
|
||||
socket.on('micropython.burn', async (config, callback) => {
|
||||
// 检查端口是否是有效的系统设备路径(而非前端内部标识符如 serial1)
|
||||
if (config.port && !config.port.startsWith('/dev/') && !config.port.includes(':')) {
|
||||
console.log(`[MicroPython] 忽略无效端口请求: ${config.port},应使用本地 Web Serial API`);
|
||||
callback([null, { code: 0, time: 0 }]);
|
||||
return;
|
||||
}
|
||||
const shell = this.#shellMicroPython_.getItem(socket.id);
|
||||
const [error, result] = await to(shell.burn(config));
|
||||
error && Debug.error(error);
|
||||
@@ -180,6 +186,12 @@ export default class Socket {
|
||||
});
|
||||
|
||||
socket.on('micropython.upload', async (config, callback) => {
|
||||
// 检查端口是否是有效的系统设备路径(而非前端内部标识符如 serial1)
|
||||
if (config.port && !config.port.startsWith('/dev/') && !config.port.includes(':')) {
|
||||
console.log(`[MicroPython] 忽略无效端口请求: ${config.port},应使用本地 Web Serial API`);
|
||||
callback([null, { code: 0, time: 0 }]);
|
||||
return;
|
||||
}
|
||||
const shell = this.#shellMicroPython_.getItem(socket.id);
|
||||
let { filePath = '', libraries = {} } = config;
|
||||
filePath = MString.tpl(filePath, {
|
||||
@@ -250,8 +262,16 @@ export default class Socket {
|
||||
error2 && Debug.error(error2);
|
||||
let [error3,] = await to(fsExtra.outputFile(config.path.code, config.code));
|
||||
error3 && Debug.error(error3);
|
||||
const [error, result] = await to(shell.upload(config));
|
||||
error && Debug.error(error);
|
||||
const [error, result] = await to(shell.compile(config));
|
||||
if (error) {
|
||||
Debug.error(error);
|
||||
callback([error, result]);
|
||||
return;
|
||||
}
|
||||
if (result.code === 0) {
|
||||
const buildPath = config.path.build;
|
||||
result.files = await Boards.getFiles(config.key, buildPath);
|
||||
}
|
||||
callback([error, result]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user