feat(core): 在线版添加对web compiler的支持

This commit is contained in:
王立帮
2025-05-04 14:38:32 +08:00
parent b9c0574780
commit e3f5caf0ce
21 changed files with 641 additions and 498 deletions

View File

@@ -104,6 +104,10 @@
}
},
"web": {
"com": "serial"
"devices": {
"serial": true,
"hid": false,
"usb": false
}
}
}

View File

@@ -32,26 +32,10 @@
}
},
"web": {
"com": "serial",
"upload": {
"reset": [
{
"dtr": false,
"rts": false
}, {
"sleep": 500
}, {
"dtr": false,
"rts": true
}, {
"sleep": 500
}, {
"dtr": false,
"rts": false
}, {
"sleep": 500
}
]
"devices": {
"serial": true,
"hid": false,
"usb": false
}
}
}

View File

@@ -1076,26 +1076,10 @@
}
},
"web": {
"com": "serial",
"upload": {
"reset": [
{
"dtr": true,
"rts": true
}, {
"sleep": 500
}, {
"dtr": false,
"rts": true
}, {
"sleep": 500
}, {
"dtr": true,
"rts": true
}, {
"sleep": 500
}
]
"devices": {
"serial": true,
"hid": false,
"usb": false
}
}
}

View File

@@ -104,6 +104,10 @@
}
},
"web": {
"com": "serial"
"devices": {
"serial": true,
"hid": false,
"usb": false
}
}
}

View File

@@ -32,26 +32,10 @@
}
},
"web": {
"com": "serial",
"upload": {
"reset": [
{
"dtr": false,
"rts": false
}, {
"sleep": 500
}, {
"dtr": false,
"rts": true
}, {
"sleep": 500
}, {
"dtr": false,
"rts": false
}, {
"sleep": 500
}
]
"devices": {
"serial": true,
"hid": false,
"usb": false
}
}
}

View File

@@ -1076,26 +1076,10 @@
}
},
"web": {
"com": "serial",
"upload": {
"reset": [
{
"dtr": true,
"rts": true
}, {
"sleep": 500
}, {
"dtr": false,
"rts": true
}, {
"sleep": 500
}, {
"dtr": true,
"rts": true
}, {
"sleep": 500
}
]
"devices": {
"serial": true,
"hid": false,
"usb": false
}
}
}

View File

@@ -128,8 +128,8 @@
"provide": ["PouchDB"],
"require": []
}, {
"path": "modules/web-modules/avr-uploader.min.js",
"provide": ["AvrUploader"],
"path": "modules/web-modules/avrbro.min.js",
"provide": ["avrbro"],
"require": []
}, {
"path": "modules/web-modules/microbit/microbit-fs.umd.min.js",

View File

@@ -27,6 +27,7 @@ goog.require('Mixly.Web.BU');
goog.require('Mixly.Web.FS');
goog.require('Mixly.Web.File');
goog.require('Mixly.Web.Serial');
goog.require('Mixly.WebCompiler.ArduShell');
goog.require('Mixly.WebSocket.File');
goog.require('Mixly.WebSocket.Serial');
goog.require('Mixly.WebSocket.ArduShell');
@@ -49,6 +50,7 @@ const {
EditorMix,
Electron = {},
Web = {},
WebCompiler = {},
WebSocket = {}
} = Mixly;
@@ -70,12 +72,18 @@ const {
FS,
File,
LibManager,
ArduShell,
BU,
PythonShell,
Serial
} = currentObj;
let ArduShell = null;
if (!goog.isElectron && Env.hasCompiler) {
ArduShell = WebCompiler.ArduShell;
} else {
ArduShell = currentObj.ArduShell;
}
const { BOARD, SELECTED_BOARD } = Config;
const { layer } = layui;

View File

@@ -16,7 +16,8 @@ goog.require('Mixly.Debug');
goog.require('Mixly.API2');
goog.require('Mixly.Electron.LibManager');
goog.require('Mixly.Electron.File');
goog.require('Mixly.WebSocket.Socket');
goog.require('Mixly.WebCompiler.Loader');
goog.require('Mixly.WebSocket.Loader');
goog.provide('Mixly.Loader');
const {
@@ -35,16 +36,20 @@ const {
API2,
Electron = {},
Web = {},
WebCompiler = {},
WebSocket = {}
} = Mixly;
const { LibManager, File } = goog.isElectron? Electron : Web;
const { Socket } = WebSocket;
window.addEventListener('load', () => {
if (!goog.isElectron && Env.hasSocketServer) {
Socket.init();
if (!goog.isElectron) {
if (Env.hasSocketServer) {
WebSocket.Loader.init();
} else if (Env.hasCompiler) {
WebCompiler.Loader.init();
}
}
const app = new App($('body')[0]);
Mixly.app = app;

View File

@@ -0,0 +1,91 @@
goog.loadJs('common', () => {
goog.require('io');
goog.require('Mixly');
goog.provide('Mixly.Socket');
class Socket {
#socket_ = null;
constructor(path, option) {
this.#socket_ = io(path, option);
}
#detectStatus_(status, callback) {
window.setTimeout(() => {
if (status.finished) {
return;
}
if (this.isConnected()) {
this.#detectStatus_(status, callback);
} else {
callback({
error: 'socket is not connected'
});
status.finished = true;
}
}, 1000);
}
emit(eventName, ...args) {
const callback = args.pop();
if (this.isConnected()) {
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
return status;
} else {
callback({
error: 'socket is not connected'
});
return false;
}
}
async emitAsync(eventName, ...args) {
return new Promise((resolve, reject) => {
if (this.isConnected()) {
const callback = (...callbackArgs) => {
if (callbackArgs[0].error) {
reject(callbackArgs[0].error);
return;
}
resolve(...callbackArgs);
}
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
} else {
reject('socket is not connected');
}
})
}
getSocket() {
return this.#socket_;
}
isConnected() {
return this.#socket_?.connected;
}
}
Mixly.Socket = Socket;
});

View File

@@ -49,6 +49,7 @@
"Mixly.Web.FS",
"Mixly.Web.File",
"Mixly.Web.Serial",
"Mixly.WebCompiler.ArduShell",
"Mixly.WebSocket.File",
"Mixly.WebSocket.Serial",
"Mixly.WebSocket.ArduShell",
@@ -653,7 +654,8 @@
"Mixly.API2",
"Mixly.Electron.LibManager",
"Mixly.Electron.File",
"Mixly.WebSocket.Socket"
"Mixly.WebCompiler.Loader",
"Mixly.WebSocket.Loader"
],
"provide": [
"Mixly.Loader"
@@ -919,6 +921,16 @@
"Mixly.RightSideBarsManager"
]
},
{
"path": "/common/socket.js",
"require": [
"io",
"Mixly"
],
"provide": [
"Mixly.Socket"
]
},
{
"path": "/common/statusbar-ampy.js",
"require": [
@@ -1556,7 +1568,6 @@
"ESPTool",
"AdafruitESPTool",
"CryptoJS",
"AvrUploader",
"Mixly.Env",
"Mixly.LayerExt",
"Mixly.Config",
@@ -1713,19 +1724,37 @@
]
},
{
"path": "/web-compiler/compiler.js",
"path": "/web-compiler/arduino-shell.js",
"require": [
"Mixly.Url",
"Mixly.Config",
"Mixly.LayerExt",
"layui",
"avrbro",
"ESPTool",
"AdafruitESPTool",
"CryptoJS",
"dayjs.duration",
"Mixly.Boards",
"Mixly.MFile",
"Mixly.Debug",
"Mixly.LayerExt",
"Mixly.Msg",
"Mixly.Web.BU",
"Mixly.Web.Serial"
"Mixly.Workspace",
"Mixly.LayerProgress",
"Mixly.Web.Serial",
"Mixly.WebCompiler"
],
"provide": [
"Mixly.WebCompiler.Compiler"
"Mixly.WebCompiler.ArduShell"
]
},
{
"path": "/web-compiler/loader.js",
"require": [
"Mixly.Debug",
"Mixly.StatusBarsManager",
"Mixly.Socket",
"Mixly.WebCompiler.ArduShell"
],
"provide": [
"Mixly.WebCompiler.Loader"
]
},
{
@@ -1811,6 +1840,21 @@
"Mixly.WebSocket.File"
]
},
{
"path": "/web-socket/loader.js",
"require": [
"Mixly.Debug",
"Mixly.StatusBarsManager",
"Mixly.Socket",
"Mixly.WebSocket.Serial",
"Mixly.WebSocket.ArduShell",
"Mixly.WebSocket.BU",
"Mixly.WebSocket.Ampy"
],
"provide": [
"Mixly.WebSocket.Loader"
]
},
{
"path": "/web-socket/serial.js",
"require": [
@@ -1825,25 +1869,9 @@
"Mixly.WebSocket.Serial"
]
},
{
"path": "/web-socket/socket.js",
"require": [
"Mixly.Debug",
"Mixly.StatusBarsManager",
"Mixly.WebSocket",
"Mixly.WebSocket.Serial",
"Mixly.WebSocket.ArduShell",
"Mixly.WebSocket.BU",
"Mixly.WebSocket.Ampy"
],
"provide": [
"Mixly.WebSocket.Socket"
]
},
{
"path": "/web-socket/web-socket.js",
"require": [
"io",
"Mixly"
],
"provide": [

View File

@@ -0,0 +1,401 @@
goog.loadJs('web', () => {
goog.require('layui');
goog.require('avrbro');
goog.require('ESPTool');
goog.require('AdafruitESPTool');
goog.require('CryptoJS');
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.Web.Serial');
goog.require('Mixly.WebCompiler');
goog.provide('Mixly.WebCompiler.ArduShell');
const {
Boards,
Debug,
LayerExt,
Msg,
Workspace,
LayerProgress,
Web,
WebCompiler
} = Mixly;
const { Serial } = Web;
const { layer } = layui;
const { ESPLoader, Transport } = ESPTool;
function hexToBinaryString (hex) {
let binaryString = '';
for (let i = 0; i < hex.length; i += 2) {
const byte = parseInt(hex.substr(i, 2), 16);
binaryString += String.fromCharCode(byte);
}
return binaryString;
}
class WebCompilerArduShell {
static {
this.mixlySocket = null;
this.socket = null;
this.shell = null;
this.getSocket = function () {
return this.socket;
}
this.getMixlySocket = function () {
return this.mixlySocket;
}
this.init = function (mixlySocket) {
this.mixlySocket = mixlySocket;
this.socket = mixlySocket.getSocket();
this.shell = new WebCompilerArduShell();
const socket = this.socket;
socket.on('arduino.dataEvent', (data) => {
if (data.length > 1000) {
return;
}
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) => {
console.log(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)) {
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 = WebCompilerArduShell.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 };
const mixlySocket = WebCompilerArduShell.getMixlySocket();
mixlySocket.emit('arduino.upload', config, async (response) => {
if (response.error) {
this.hideProgress();
reject(response.error);
return;
}
const [error, result] = response;
if (error) {
this.hideProgress();
reject(error);
return;
}
if (result.code !== 0) {
this.hideProgress();
resolve(result);
return;
}
const { files } = result;
try {
const keys = Boards.getSelectedBoardKey().split(':');
if (`${keys[0]}:${keys[1]}` === 'arduino:avr') {
await this.uploadWithAvrbro(port, files);
} else {
await this.uploadWithEsptool(port, files);
}
} catch (error) {
this.hideProgress();
reject(error);
return;
}
this.hideProgress();
result.files = null;
resolve(result);
});
});
}
async uploadWithEsptool(port, files) {
console.log(port, files)
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let esploader = null;
let transport = null;
let baudrate = 115200;
let eraseAll = true;
try {
const keys = Boards.getSelectedBoardKey().split(':');
if (`${keys[0]}:${keys[1]}` === 'esp32:esp32') {
baudrate = Boards.getSelectedBoardConfigParam('UploadSpeed');
eraseAll = Boards.getSelectedBoardConfigParam('EraseFlash') === 'all';
} else {
baudrate = Boards.getSelectedBoardConfigParam('baud');
eraseAll = Boards.getSelectedBoardConfigParam('wipe') === 'all';
}
transport = new Transport(Serial.getPort(port), false);
esploader = new ESPLoader({
transport,
baudrate,
terminal: {
clean() {},
writeLine(data) {
statusBarTerminal.addValue(data + '\n');
},
write(data) {
statusBarTerminal.addValue(data);
}
}
});
let chip = await esploader.main();
} catch (error) {
await transport.disconnect();
throw new Error(error);
}
let data = [];
statusBarTerminal.addValue("\n");
for (let file of files) {
if (file.data && file.offset) {
data.push({
address: parseInt(file.offset, 16),
data: hexToBinaryString(file.data)
});
}
}
const flashOptions = {
fileArray: data,
flashSize: 'keep',
eraseAll,
compress: true,
calculateMD5Hash: (image) => CryptoJS.MD5(CryptoJS.enc.Latin1.parse(image))
};
try {
await esploader.writeFlash(flashOptions);
await transport.setDTR(false);
await new Promise((resolve) => setTimeout(resolve, 100));
await transport.setDTR(true);
await transport.disconnect();
} catch (error) {
await transport.disconnect();
throw new Error(error);
}
}
async uploadWithAvrbro(port, files) {
const key = Boards.getSelectedBoardKey();
const boardId = key.split(':')[2];
let boardName = '';
if (boardId === 'uno') {
boardName = 'uno';
} else if (boardId === 'nano') {
const cpu = Boards.getSelectedBoardConfigParam('cpu');
if (cpu === 'atmega328old') {
boardName = 'nano';
} else {
boardName = 'nano (new bootloader)';
}
} else if (boardId === 'pro') {
boardName = 'pro-mini';
} else if (boardId === 'mega') {
boardName = 'mega';
} else if (boardId === 'leonardo') {
boardName = 'leonardo';
}
const buffer = avrbro.parseHex(files[0].data);
await avrbro.flash(Serial.getPort(port), buffer, { boardName });
}
async kill() {
return new Promise(async (resolve, reject) => {
const mixlySocket = WebCompilerArduShell.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_;
}
}
WebCompiler.ArduShell = WebCompilerArduShell;
});

View File

@@ -1,184 +0,0 @@
goog.loadJs('web', () => {
goog.require('Mixly.Url');
goog.require('Mixly.Config');
goog.require('Mixly.LayerExt');
goog.require('Mixly.Boards');
goog.require('Mixly.MFile');
goog.require('Mixly.Msg');
goog.require('Mixly.Web.BU');
goog.require('Mixly.Web.Serial');
goog.provide('Mixly.WebCompiler.Compiler');
const {
WebCompiler,
Url,
Boards,
MFile,
Config,
LayerExt,
Msg,
Web
} = Mixly;
const { SOFTWARE, BOARD } = Config;
const { Compiler } = WebCompiler;
const { BU, Serial } = Web;
const DEFAULT_CONFIG = {
"enabled": true,
"protocol": "http:",
"ip": "localhost",
"domain": null
};
Compiler.CONFIG = { ...DEFAULT_CONFIG, ...(SOFTWARE?.webCompiler ?? {}) };
const { CONFIG } = Compiler;
let { hostname, protocol, port } = window.location;
if (port) {
port = ':' + port;
}
Compiler.protocol = protocol;
Compiler.URL = Compiler.protocol + '//' + hostname + port + '/compile';
Compiler.compile = () => {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.show();
statusBarTerminal.setValue('');
Compiler.generateCommand('compile', (error, obj, layerNum) => {
layer.close(layerNum);
let message = Msg.Lang['shell.compileSucc'];
if (error) {
message = Msg.Lang['shell.compileFailed'];
}
layer.msg(message, { time: 1000 });
statusBarTerminal.addValue("==" + message + "(" + Msg.Lang['shell.timeCost'] + " " + obj.timeCost + ")==\n");
});
}
Compiler.upload = async () => {
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
mainStatusBarTabs.show();
statusBarTerminal.setValue('');
BU.burning = false;
BU.uploading = true;
const board = Boards.getSelectedBoardCommandParam();
const boardParam = board.split(':');
const portName = 'web-serial';
if (boardParam[1] === 'avr') {
let boardUpload;
switch (boardParam[2]) {
case 'uno':
boardUpload = 'uno';
break;
case 'nano':
if (boardParam.length > 3 && boardParam[3] === 'cpu=atmega328old') {
boardUpload = 'nanoOldBootloader';
} else {
boardUpload = 'nano';
}
break;
case 'pro':
boardUpload = 'proMini';
break;
}
Serial.portClose(portName, async () => {
mainStatusBarTabs.changeTo('output');
try {
await AvrUploader.connect(boardUpload, {});
Compiler.generateCommand('upload', BU.uploadWithAvrUploader);
} catch (error) {
statusBarTerminal.addValue(error.toString() + '\n');
}
});
} else {
Serial.connect(portName, 115200, async (port) => {
if (!port) {
layer.msg(Msg.Lang['已取消连接'], { time: 1000 });
return;
}
mainStatusBarTabs.changeTo('output');
Compiler.generateCommand('upload', BU.uploadWithEsptool);
});
}
}
Compiler.generateCommand = (operate, endFunc = (errorMessage, data, layerNum) => {}) => {
const code = MFile.getCode();
let type;
const boardType = Boards.getSelectedBoardCommandParam();
let command = {
board: encodeURIComponent(boardType),
code: encodeURIComponent(code),
visitorId: BOARD.visitorId.str32CRC32b,
operate
};
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let commandStr = Compiler.URL + '?' + Url.jsonToUrl(command);
statusBarTerminal.setValue(Msg.Lang['shell.compiling'] + '...\n');
console.log('send -> ', commandStr);
const compileLayer = layer.open({
type: 1,
title: Msg.Lang['shell.compiling'] + "...",
content: $('#mixly-loader-div'),
shade: LayerExt.SHADE_NAV,
closeBtn: 0,
success: function () {
$(".layui-layer-page").css("z-index", "198910151");
$("#mixly-loader-btn").off("click").click(() => {
layer.close(compileLayer);
});
},
end: function () {
$('#mixly-loader-div').css('display', 'none');
$(".layui-layer-shade").remove();
}
});
Compiler.sendCommand(compileLayer, commandStr, endFunc);
}
Compiler.sendCommand = (layerType, command, endFunc = (errorMessage, data, layerNum) => {}) => {
/*
fetch(command).then(function(response) {
console.log(response);
if(response.ok) {
return response.blob();
}
throw new Error('Network response was not ok.');
}).then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
console.log(objectURL);
}).catch(function(error) {
console.log('There has been a problem with your fetch operation: ', error.message);
});
*/
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let req = new Request(command);
fetch(req, {
credentials: 'omit', // 设置不传递cookie
mode: 'cors', // 设置请求不允许跨域
}).then(res => {
return res.text();
}).then((data) => {
const dataObj = JSON.parse(data);
console.log(dataObj);
if (dataObj.error) {
statusBarTerminal.addValue(decodeURIComponent(dataObj.error));
endFunc(true, null, layerType);
} else {
statusBarTerminal.addValue(decodeURIComponent(dataObj.compileMessage));
endFunc(false, {
data: dataObj.data,
timeCost: decodeURIComponent(dataObj.timeCost)
}, layerType);
}
})
.catch((error) => {
endFunc(true, error.toString(), layerType);
});
}
});

View File

@@ -0,0 +1,37 @@
goog.loadJs('web', () => {
goog.require('Mixly.Debug');
goog.require('Mixly.StatusBarsManager');
goog.require('Mixly.Socket');
goog.require('Mixly.WebCompiler.ArduShell');
goog.provide('Mixly.WebCompiler.Loader');
const {
Debug,
StatusBarsManager,
Socket,
WebCompiler
} = Mixly;
const {
Loader,
ArduShell
} = WebCompiler;
Loader.init = function () {
const mixlySocket = new Socket(`wss://${location.hostname}:4000/compile`, {
path: '/mixly-socket/',
reconnection: true,
reconnectionDelayMax: 10000,
transports: ['websocket'],
protocols: ['my-protocol-v1']
});
const socket = mixlySocket.getSocket();
socket.on('connect', () => {});
socket.on('disconnect', () => {});
ArduShell.init(mixlySocket);
}
});

View File

@@ -128,7 +128,7 @@ class WebSocketArduShell {
statusBarSerial.open()
.then(() => {
const baudRates = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
if (!baudRates.length) {
if (!baudRates?.length) {
return statusBarSerial.setBaudRate(9600);
} else {
return statusBarSerial.setBaudRate(baudRates[0] - 0);

View File

@@ -2,21 +2,22 @@ goog.loadJs('web', () => {
goog.require('Mixly.Debug');
goog.require('Mixly.StatusBarsManager');
goog.require('Mixly.WebSocket');
goog.require('Mixly.Socket');
goog.require('Mixly.WebSocket.Serial');
goog.require('Mixly.WebSocket.ArduShell');
goog.require('Mixly.WebSocket.BU');
goog.require('Mixly.WebSocket.Ampy');
goog.provide('Mixly.WebSocket.Socket');
goog.provide('Mixly.WebSocket.Loader');
const {
Debug,
StatusBarsManager,
Socket,
WebSocket
} = Mixly;
const {
Socket,
Loader,
Serial,
ArduShell,
BU,
@@ -24,8 +25,8 @@ const {
} = WebSocket;
Socket.init = function () {
const mixlySocket = new WebSocket('wss://127.0.0.1:4000', {
Loader.init = function () {
const mixlySocket = new Socket(`wss://${location.hostname}:4000/all`, {
path: '/mixly-socket/',
reconnection: true,
reconnectionDelayMax: 10000,

View File

@@ -1,91 +1,6 @@
goog.loadJs('web', () => {
goog.require('io');
goog.require('Mixly');
goog.provide('Mixly.WebSocket');
class WebSocket {
#socket_ = null;
constructor(path, option) {
this.#socket_ = io(path, option);
}
#detectStatus_(status, callback) {
window.setTimeout(() => {
if (status.finished) {
return;
}
if (this.isConnected()) {
this.#detectStatus_(status, callback);
} else {
callback({
error: 'socket is not connected'
});
status.finished = true;
}
}, 1000);
}
emit(eventName, ...args) {
const callback = args.pop();
if (this.isConnected()) {
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
return status;
} else {
callback({
error: 'socket is not connected'
});
return false;
}
}
async emitAsync(eventName, ...args) {
return new Promise((resolve, reject) => {
if (this.isConnected()) {
const callback = (...callbackArgs) => {
if (callbackArgs[0].error) {
reject(callbackArgs[0].error);
return;
}
resolve(...callbackArgs);
}
let emitStatus = {
finished: false
};
let status = this.#socket_.emit(eventName, ...args, (...callbackArgs) => {
if (emitStatus.finished) {
return;
}
emitStatus.finished = true;
callback(...callbackArgs);
});
this.#detectStatus_(emitStatus, callback);
} else {
reject('socket is not connected');
}
})
}
getSocket() {
return this.#socket_;
}
isConnected() {
return this.#socket_?.connected;
}
}
Mixly.WebSocket = WebSocket;
});

View File

@@ -8,7 +8,6 @@ goog.require('PartialFlashing');
goog.require('ESPTool');
goog.require('AdafruitESPTool');
goog.require('CryptoJS');
goog.require('AvrUploader');
goog.require('Mixly.Env');
goog.require('Mixly.LayerExt');
goog.require('Mixly.Config');
@@ -660,6 +659,9 @@ BU.uploadWithAmpy = async (portName) => {
for (let item of rootInfo) {
rootMap[item[0]] = item[1];
}
if (cwd === '/') {
cwd = '';
}
if (libraries && libraries instanceof Object) {
for (let key in libraries) {
if (rootMap[`${cwd}/${key}`] !== undefined && rootMap[`${cwd}/${key}`] === libraries[key].size) {
@@ -692,110 +694,6 @@ BU.uploadWithAmpy = async (portName) => {
}
}
function hexToBuf (hex) {
var typedArray = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
}));
return typedArray.buffer;
}
BU.uploadWithEsptool = async (endType, obj, layerType) => {
const portName = 'web-serial';
const portObj = Serial.portsOperator[portName];
const { serialport, toolConfig } = portObj;
let prevBaud = toolConfig.baudRates;
if (prevBaud !== 115200) {
toolConfig.baudRates = 115200;
await serialport.setBaudRate(toolConfig.baudRates);
}
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
let firmwareData = obj.data;
if (endType || typeof firmwareData !== 'object') {
statusBarTerminal.addValue(Msg.Lang['shell.bin.readFailed'] + "\n");
layer.close(layerType);
return;
}
layer.title(Msg.Lang['shell.uploading'] + '...', layerType);
statusBarTerminal.addValue(Msg.Lang['shell.bin.reading'] + "... ");
let firmwareList = [];
for (let i of firmwareData) {
if (!i.offset || !i.data) {
continue;
}
const firmware = {
offset: i.offset,
binBuf: hexToBuf(i.data)
};
firmwareList.push(firmware);
}
statusBarTerminal.addValue("Done!\n");
BU.burning = true;
BU.uploading = false;
statusBarTerminal.addValue(Msg.Lang['shell.uploading'] + '...\n');
mainStatusBarTabs.show();
mainStatusBarTabs.changeTo('output');
try {
SerialPort.refreshOutputBuffer = false;
SerialPort.refreshInputBuffer = true;
await espTool.reset();
if (await clickSync()) {
// await clickErase();
for (let i of firmwareList) {
await clickProgram(i.offset, i.binBuf);
}
}
layer.close(layerType);
layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadSucc']}==\n`);
Serial.reset(portName, 'upload');
mainStatusBarTabs.changeTo(portName);
} catch (error) {
Debug.error(error);
layer.close(layerType);
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadFailed']}==\n`);
} finally {
SerialPort.refreshOutputBuffer = true;
SerialPort.refreshInputBuffer = false;
const code = MFile.getCode();
const baudRateList = code.match(/(?<=Serial.begin[\s]*\([\s]*)[0-9]*(?=[\s]*\))/g);
if (baudRateList && Serial.BAUDRATES.includes(baudRateList[0]-0)) {
prevBaud = baudRateList[0]-0;
}
if (toolConfig.baudRates !== prevBaud) {
toolConfig.baudRates = prevBaud;
await serialport.setBaudRate(prevBaud);
}
}
}
BU.uploadWithAvrUploader = async (endType, obj, layerType) => {
let firmwareData = obj.data;
const { mainStatusBarTabs } = Mixly;
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
if (endType || typeof firmwareData !== 'object') {
statusBarTerminal.addValue(Msg.Lang['shell.bin.readFailed'] + "\n");
layer.close(layerType);
return;
}
statusBarTerminal.addValue(Msg.Lang['shell.uploading'] + '...\n');
layer.title(Msg.Lang['shell.uploading'] + '...', layerType);
let uploadSucMessageShow = true;
AvrUploader.upload(firmwareData[0].data, (progress) => {
if (progress >= 100 && uploadSucMessageShow) {
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadSucc']}==\n`);
layer.msg(Msg.Lang['shell.uploadSucc'], { time: 1000 });
layer.close(layerType);
uploadSucMessageShow = false;
}
}, true)
.catch((error) => {
layer.close(layerType);
statusBarTerminal.addValue(`==${Msg.Lang['shell.uploadFailed']}==\n`);
});
}
/**
* @function 特殊固件的烧录
* @return {void}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
<div class="menu-line">
<label>{{d.name}}</label>
<div class="sep"></div>
{{# if (d.hotKey) { }}
<div class="sep"></div>
<label>{{d.hotKey}}</label>
{{# } }}
</div>