feat: sync mixly root files and common folder
This commit is contained in:
399
mixly/common/modules/mixly-modules/web-compiler/arduino-shell.js
Normal file
399
mixly/common/modules/mixly-modules/web-compiler/arduino-shell.js
Normal file
@@ -0,0 +1,399 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('layui');
|
||||
goog.require('AVRUploader');
|
||||
goog.require('esptooljs');
|
||||
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.Web.SerialTransport');
|
||||
goog.require('Mixly.WebCompiler');
|
||||
goog.provide('Mixly.WebCompiler.ArduShell');
|
||||
|
||||
const {
|
||||
Boards,
|
||||
Debug,
|
||||
LayerExt,
|
||||
Msg,
|
||||
Workspace,
|
||||
LayerProgress,
|
||||
Web,
|
||||
WebCompiler
|
||||
} = Mixly;
|
||||
|
||||
const { Serial, SerialTransport } = Web;
|
||||
const { layer } = layui;
|
||||
const { ESPLoader, Transport } = esptooljs;
|
||||
|
||||
|
||||
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) => {
|
||||
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.uploadWithAVRUploader(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) {
|
||||
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') || 115200;
|
||||
eraseAll = Boards.getSelectedBoardConfigParam('EraseFlash') === 'all';
|
||||
} else {
|
||||
baudrate = Boards.getSelectedBoardConfigParam('baud') || 115200;
|
||||
eraseAll = Boards.getSelectedBoardConfigParam('wipe') === 'all';
|
||||
}
|
||||
const serial = new Serial(port);
|
||||
transport = new SerialTransport(serial, 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 uploadWithAVRUploader(port, files) {
|
||||
const key = Boards.getSelectedBoardKey();
|
||||
const boardId = key.split(':')[2];
|
||||
let boardName = 'uno';
|
||||
if (boardId === 'nano') {
|
||||
const cpu = Boards.getSelectedBoardConfigParam('cpu');
|
||||
if (cpu === 'atmega328old') {
|
||||
boardName = 'nanoOldBootloader';
|
||||
} else {
|
||||
boardName = 'nano';
|
||||
}
|
||||
} else if (boardId === 'pro') {
|
||||
boardName = 'proMini';
|
||||
} else if (boardId === 'mega') {
|
||||
boardName = 'mega';
|
||||
} else if (boardId === 'leonardo') {
|
||||
boardName = 'leonardo';
|
||||
}
|
||||
const serial = Serial.getPort(port);
|
||||
const text = files[0].data;
|
||||
await AVRUploader.upload(serial, boardName, text);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
});
|
||||
54
mixly/common/modules/mixly-modules/web-compiler/loader.js
Normal file
54
mixly/common/modules/mixly-modules/web-compiler/loader.js
Normal file
@@ -0,0 +1,54 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('Mixly.Debug');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.StatusBarsManager');
|
||||
goog.require('Mixly.Socket');
|
||||
goog.require('Mixly.WebCompiler.ArduShell');
|
||||
goog.provide('Mixly.WebCompiler.Loader');
|
||||
|
||||
const {
|
||||
Debug,
|
||||
Config,
|
||||
StatusBarsManager,
|
||||
Socket,
|
||||
WebCompiler
|
||||
} = Mixly;
|
||||
|
||||
const {
|
||||
Loader,
|
||||
ArduShell
|
||||
} = WebCompiler;
|
||||
|
||||
const { SOFTWARE } = Config;
|
||||
|
||||
|
||||
Loader.init = function () {
|
||||
let url = '';
|
||||
if (SOFTWARE.webCompiler?.url) {
|
||||
const info = new window.URL(SOFTWARE.webCompiler.url);
|
||||
if (window.location.protocol === 'http:') {
|
||||
info.protocol = 'ws:';
|
||||
}
|
||||
if (info.hostname === 'default') {
|
||||
info.hostname = window.location.hostname;
|
||||
}
|
||||
url = info.origin;
|
||||
} else {
|
||||
url = `wss://${window.location.host}`;
|
||||
}
|
||||
const mixlySocket = new Socket(`${url}/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);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('Mixly');
|
||||
goog.provide('Mixly.WebCompiler');
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user