@govincaresone/g-link
Version:
Porvide local hardware function to govin
247 lines (202 loc) • 7.87 kB
JavaScript
const fs = require('fs');
const { spawn } = require('child_process');
const path = require('path');
const ansi = require('ansi-string');
const os = require('os');
const ABORT_STATE_CHECK_INTERVAL = 100;
class MicroPython {
constructor(peripheralPath, config, userDataPath, toolsPath, sendstd) {
this._peripheralPath = peripheralPath;
this._config = config;
this._userDataPath = userDataPath;
this._projectPath = path.join(userDataPath, 'micropython/project');
this._pythonPath = path.join(toolsPath, 'Python'); //2.0.2
this._toolsPath = toolsPath;
this._sendstd = sendstd;
this._abort = false;
//2.0.2
if (os.platform() === 'darwin') {
this._pyPath = path.join(this._pythonPath, 'python3');
this._ampyPath = path.join(toolsPath, 'ampy');
} else if (os.platform() === 'linux') {
this._pyPath = path.join(this._pythonPath, 'bin/python3');
this._ampyPath = path.join(toolsPath, 'ampy');
this._espPath = path.join(this._pythonPath, 'bin/esptool.py');
} else {
this._pyPath = path.join(this._pythonPath, 'python');
this._ampyPath = path.join(toolsPath, 'ampy.bat');
}
this._codefilePath = path.join(this._projectPath, 'main.py');
this._firmwareDir = path.join(toolsPath, '../firmwares/microPython');
}
abortUpload() {
this._abort = true;
}
async flashMicroPythonCode(code, library = []) {
const filesToPut = [];
if (!fs.existsSync(this._projectPath)) {
fs.mkdirSync(this._projectPath, { recursive: true });
}
try {
fs.writeFileSync(this._codefilePath, code);
} catch (err) {
return Promise.reject(err);
}
filesToPut.push(this._codefilePath);
library.forEach(lib => {
if (fs.existsSync(lib)) {
const libraries = fs.readdirSync(lib);
libraries.forEach(file => {
filesToPut.push(path.join(lib, file));
});
}
});
this._sendstd('Writing files...\n');
for (const file of filesToPut) {
const result = await this.putFile(file);
if (result !== 'Success') {
return Promise.reject(result);
}
if (this._abort) {
return Promise.resolve('Aborted');
}
}
this._sendstd(`${ansi.green_dark}Success\n`);
return Promise.resolve('Success');
}
putFile(file) {
return new Promise((resolve, reject) => {
const args = ['--port', this._peripheralPath, 'put', file];
const ampy = spawn(this._ampyPath, args);
ampy.stdout.on('data', buf => {
this._sendstd(buf.toString());
});
ampy.stderr.on('data', buf => {
this._sendstd(ansi.red + buf.toString());
});
const listenAbortSignal = setInterval(() => {
if (this._abort) {
ampy.kill();
}
}, ABORT_STATE_CHECK_INTERVAL);
ampy.on('exit', code => {
clearInterval(listenAbortSignal);
if (code === null) {
return resolve('Aborted');
} else if (code === 0) {
this._sendstd(`${file} write finished\n`);
return resolve('Success');
} else {
return reject('Failed to write file');
}
});
});
}
async flash(code, library = []) {
const filesToPut = [];
if (!fs.existsSync(this._projectPath)) {
fs.mkdirSync(this._projectPath, { recursive: true });
}
try {
fs.writeFileSync(this._codefilePath, code);
} catch (err) {
return Promise.reject(err);
}
filesToPut.push(this._codefilePath);
library.forEach(lib => {
if (fs.existsSync(lib)) {
const libraries = fs.readdirSync(lib);
libraries.forEach(file => {
filesToPut.push(path.join(lib, file));
});
}
});
this._sendstd('Writing files...\n');
for (const file of filesToPut) {
const result = await this.putFile(file);
if (result !== 'Success') {
return Promise.reject(result);
}
if (this._abort) {
return Promise.resolve('Aborted');
}
}
this._sendstd(`${ansi.green_dark}Success\n`);
return Promise.resolve('Success');
}
async flashBinaryFile(filePath) {
if (!fs.existsSync(filePath)) {
return Promise.reject('File does not exist');
}
this._sendstd(`Erasing flash memory...\n`);
// Step to erase flash memory before flashing
await new Promise((resolve, reject) => {
const eraseArgs = [
'--chip', 'esp32',
'--port', this._peripheralPath,
'--baud', '115200',
'erase_flash'
];
// const eraseProcess = spawn('esptool.py', eraseArgs);
const eraseProcess = spawn(this._pyPath, [this._espPath, ...eraseArgs]);
eraseProcess.stdout.on('data', buf => {
this._sendstd(buf.toString());
});
eraseProcess.stderr.on('data', buf => {
this._sendstd(ansi.red + buf.toString());
});
eraseProcess.on('exit', code => {
if (code === 0) {
this._sendstd(`${ansi.green_dark}Flash memory erased successfully.\n`);
resolve();
} else {
reject('Failed to erase flash memory');
}
});
});
this._sendstd(`Flashing binary file ${filePath}...\n`);
return new Promise((resolve, reject) => {
const args = [
'--chip', 'esp32',
'--port', this._peripheralPath,
'--baud', '460800',
'--before', 'default_reset',
'--after', 'hard_reset',
'write_flash', '-z',
'--flash_mode', 'keep',
'--flash_freq', '40m',
'--flash_size', 'detect',
'0x1000', filePath
];
const esptool = spawn(this._pyPath, [this._espPath, ...args]);
// const esptool = spawn('esptool.py', args);
esptool.stdout.on('data', buf => {
this._sendstd(buf.toString());
});
esptool.stderr.on('data', buf => {
this._sendstd(ansi.red + buf.toString());
});
const listenAbortSignal = setInterval(() => {
if (this._abort) {
esptool.kill();
}
}, ABORT_STATE_CHECK_INTERVAL);
esptool.on('exit', code => {
clearInterval(listenAbortSignal);
if (code === null) {
return resolve('Aborted');
} else if (code === 0) {
this._sendstd(`${ansi.green_dark}Success\n`);
return resolve('Success');
} else {
return reject('Failed to flash binary file');
}
});
});
}
flashRealFirmware() {
const firmwarePath = path.join(this._firmwareDir, this._config.firmware);
return this.flashBinaryFile(firmwarePath);
}
}
module.exports = MicroPython;