2018-12-30 22:17:58 -08:00
const { app , BrowserWindow , ipcMain } = require ( 'electron' )
const { download } = require ( 'electron-dl' )
2019-01-01 15:47:54 -08:00
const { spawn } = require ( 'child_process' ) ;
2019-01-02 15:50:05 -08:00
const { execFile } = require ( 'child_process' ) ;
2019-01-13 22:17:32 -08:00
const fs = require ( 'fs' ) ;
2020-10-20 19:05:17 -07:00
const path = require ( 'path' ) ;
2018-12-30 22:17:58 -08:00
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
2019-01-02 18:44:21 -08:00
var avrdudeErr = "" ;
2019-01-04 04:19:17 -08:00
var avrdudeIsRunning = false ;
2020-10-20 19:05:17 -07:00
var teensyLoaderIsRunning = false ;
var teensyLoaderErr = ""
2019-01-02 18:44:21 -08:00
2018-12-30 22:17:58 -08:00
function createWindow ( ) {
// Create the browser window.
2020-08-26 00:13:45 -07:00
win = new BrowserWindow ( {
width : 800 , height : 600 , backgroundColor : '#312450' ,
webPreferences : { nodeIntegration : true }
} )
2018-12-30 22:17:58 -08:00
// and load the index.html of the app.
win . loadFile ( 'index.html' )
// Open the DevTools.
2019-01-04 02:47:00 -08:00
//win.webContents.openDevTools()
2018-12-30 22:17:58 -08:00
// Emitted when the window is closed.
win . on ( 'closed' , ( ) => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null
} )
2020-10-20 23:00:15 -07:00
//This forces any links that have a target of _blank to open in a browser rather than Electron window
win . webContents . on ( 'new-window' , function ( e , url ) {
e . preventDefault ( ) ;
require ( 'electron' ) . shell . openExternal ( url ) ;
} ) ;
2018-12-30 22:17:58 -08:00
}
2020-10-20 23:00:15 -07:00
2020-08-26 00:13:45 -07:00
//Required for newer versions of Electron to work with serialport
app . allowRendererProcessReuse = false
2018-12-30 22:17:58 -08:00
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app . on ( 'ready' , createWindow )
// Quit when all windows are closed.
app . on ( 'window-all-closed' , ( ) => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
2019-01-14 14:34:00 -08:00
//if (process.platform !== 'darwin')
{
2018-12-30 22:17:58 -08:00
app . quit ( )
}
} )
app . on ( 'activate' , ( ) => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if ( win === null ) {
createWindow ( )
}
} )
2018-12-31 05:59:18 -08:00
ipcMain . on ( 'download' , ( e , args ) => {
2019-01-13 22:17:32 -08:00
filename = args . url . substring ( args . url . lastIndexOf ( '/' ) + 1 ) ;
dlDir = app . getPath ( 'downloads' ) ;
fullFile = dlDir + "/" + filename ;
2019-02-11 15:10:32 -08:00
//Special case for handling the build that is from master. This is ALWAYS downloaded as there's no way of telling when it was last updated.
if ( filename == "master.hex" || filename == "master.ini" )
{
if ( fs . existsSync ( fullFile ) )
{
fs . unlinkSync ( fullFile )
console . log ( 'Master version selected, removing local file forcing re-download: ' + filename ) ;
}
}
2019-01-13 22:17:32 -08:00
//console.log("Filename: " + fullFile );
2019-07-08 21:56:05 -07:00
options = { } ;
2019-07-16 23:51:27 -07:00
if ( filename . split ( '.' ) . pop ( ) == "msq" )
2019-07-08 21:56:05 -07:00
{
options = { saveAs : true } ;
}
2019-01-13 22:17:32 -08:00
fs . exists ( fullFile , ( exists ) => {
if ( exists ) {
console . log ( "File " + fullFile + " already exists in Downloads directory. Skipping download" ) ;
e . sender . send ( "download complete" , fullFile , "exists" ) ;
}
else {
2019-07-08 21:56:05 -07:00
download ( BrowserWindow . getFocusedWindow ( ) , args . url , options )
2019-01-13 22:17:32 -08:00
. then ( dl => e . sender . send ( "download complete" , dl . getSavePath ( ) , dl . getState ( ) ) )
. catch ( console . error ) ;
}
} ) ;
2018-12-30 22:17:58 -08:00
} ) ;
2019-07-08 23:15:31 -07:00
ipcMain . on ( 'installWinDrivers' , ( e , args ) => {
var infName = _ _dirname + "/bin/drivers-win/arduino.inf" ;
infName = infName . replace ( 'app.asar' , '' ) ;
console . log ( "INF File " + infName ) ;
//syssetup,SetupInfObjectInstallAction DefaultInstall 128 .\<file>.inf
var execArgs = [ 'syssetup,SetupInfObjectInstallAction' , 'DefaultInstall 128' , infName ] ;
const child = execFile ( "rundll32" , execArgs ) ;
} ) ;
2019-01-01 15:47:54 -08:00
ipcMain . on ( 'uploadFW' , ( e , args ) => {
2019-01-04 04:19:17 -08:00
if ( avrdudeIsRunning == true ) { return ; }
avrdudeIsRunning = true ; //Indicate that an avrdude process has started
2019-01-01 15:47:54 -08:00
var platform ;
2019-01-04 04:10:03 -08:00
var burnStarted = false ;
var burnPercent = 0 ;
2019-07-02 00:08:06 -07:00
//All Windows builds use the 32-bit binary
if ( process . platform == "win32" )
{
platform = "avrdude-windows" ;
}
//All Mac builds use the 64-bit binary
else if ( process . platform == "darwin" )
{
platform = "avrdude-darwin-x86_64" ;
}
else if ( process . platform == "linux" )
{
if ( process . arch == "x32" ) { platform = "avrdude-linux_i686" ; }
else if ( process . arch == "x64" ) { platform = "avrdude-linux_x86_64" ; }
else if ( process . arch == "arm" ) { platform = "avrdude-armhf" ; }
else if ( process . arch == "arm64" ) { platform = "avrdude-aarch64" ; }
}
2019-01-01 15:47:54 -08:00
2019-01-02 15:50:05 -08:00
var executableName = _ _dirname + "/bin/" + platform + "/avrdude" ;
executableName = executableName . replace ( 'app.asar' , '' ) ; //This is important for allowing the binary to be found once the app is packaed into an asar
2019-01-01 15:47:54 -08:00
var configName = executableName + ".conf" ;
2019-01-01 19:47:18 -08:00
if ( process . platform == "win32" ) { executableName = executableName + '.exe' ; } //This must come after the configName line above
2019-01-01 16:31:28 -08:00
2019-01-01 15:47:54 -08:00
var hexFile = 'flash:w:' + args . firmwareFile + ':i' ;
var execArgs = [ '-v' , '-patmega2560' , '-C' , configName , '-cwiring' , '-b 115200' , '-P' , args . port , '-D' , '-U' , hexFile ] ;
2019-01-02 15:50:05 -08:00
console . log ( executableName ) ;
//const child = spawn(executableName, execArgs);
const child = execFile ( executableName , execArgs ) ;
2019-01-01 15:47:54 -08:00
child . stdout . on ( 'data' , ( data ) => {
2019-01-04 04:10:03 -08:00
console . log ( ` avrdude stdout: \n ${ data } ` ) ;
2019-01-01 15:47:54 -08:00
} ) ;
child . stderr . on ( 'data' , ( data ) => {
console . log ( ` avrdude stderr: ${ data } ` ) ;
2019-01-02 18:44:21 -08:00
avrdudeErr = avrdudeErr + data ;
2019-01-04 04:10:03 -08:00
//Check if avrdude has started the actual burn yet, and if so, track the '#' characters that it prints. Each '#' represents 1% of the total burn process (50 for write and 50 for read)
if ( burnStarted == true )
{
if ( data == "#" ) { burnPercent += 1 ; }
e . sender . send ( "upload percent" , burnPercent ) ;
}
else
{
//This is a hack, but basically watch the output from avrdude for the term 'Writing | ', everything after that is the #s indicating 1% of burn.
if ( avrdudeErr . substr ( avrdudeErr . length - 10 ) == "Writing | " )
{
burnStarted = true ;
}
}
2019-01-01 15:47:54 -08:00
} ) ;
2020-10-20 23:00:15 -07:00
child . on ( 'error' , ( err ) => {
console . log ( 'Failed to start subprocess.' ) ;
console . log ( err ) ;
avrDudeIsRunning = false ;
} ) ;
child . on ( 'close' , ( code ) => {
avrdudeIsRunning = false ;
if ( code !== 0 )
{
console . log ( ` avrdude process exited with code ${ code } ` ) ;
e . sender . send ( "upload error" , avrdudeErr )
avrdudeErr = "" ;
}
else
{
e . sender . send ( "upload completed" , code )
}
} ) ;
2020-10-20 19:05:17 -07:00
} ) ;
2021-01-12 15:47:17 -08:00
ipcMain . on ( 'uploadFW_teensy' , ( e , args ) => {
2020-10-20 19:05:17 -07:00
if ( teensyLoaderIsRunning == true ) { return ; }
teensyLoaderIsRunning = true ; //Indicate that an avrdude process has started
var platform ;
var burnStarted = false ;
var burnPercent = 0 ;
//All Windows builds use the 32-bit binary
if ( process . platform == "win32" )
{
platform = "teensy_loader_cli-windows" ;
}
//All Mac builds use the 64-bit binary
else if ( process . platform == "darwin" )
{
platform = "teensy_loader_cli-darwin-x86_64" ;
}
else if ( process . platform == "linux" )
{
if ( process . arch == "x32" ) { platform = "teensy_loader_cli-linux_i686" ; }
else if ( process . arch == "x64" ) { platform = "teensy_loader_cli-linux_x86_64" ; }
else if ( process . arch == "arm" ) { platform = "teensy_loader_cli-armhf" ; }
else if ( process . arch == "arm64" ) { platform = "teensy_loader_cli-aarch64" ; }
}
var executableName = _ _dirname + "/bin/" + platform + "/teensy_post_compile" ;
executableName = executableName . replace ( 'app.asar' , '' ) ; //This is important for allowing the binary to be found once the app is packaed into an asar
var configName = executableName + ".conf" ;
2020-10-21 03:01:53 -07:00
2020-10-20 19:05:17 -07:00
2021-01-12 15:47:17 -08:00
var execArgs = [ '-board=' + args . board , '-reboot' , '-file=' + path . basename ( args . firmwareFile , '.hex' ) , '-path=' + path . dirname ( args . firmwareFile ) , '-tools=' + executableName . replace ( '/teensy_post_compile' , "" ) ] ;
2020-10-20 23:00:15 -07:00
//console.log(execArgs);
2020-10-20 19:05:17 -07:00
2020-10-21 03:01:53 -07:00
if ( process . platform == "win32" ) { executableName = executableName + '.exe' ; } //This must come after the configName line above
2020-10-20 19:05:17 -07:00
console . log ( executableName ) ;
const child = execFile ( executableName , execArgs ) ;
child . stdout . on ( 'data' , ( data ) => {
console . log ( ` teensy_loader_cli stdout: \n ${ data } ` ) ;
} ) ;
child . stderr . on ( 'data' , ( data ) => {
console . log ( ` teensy_loader_cli stderr: ${ data } ` ) ;
teensyLoaderErr = teensyLoaderErr + data ;
//Check if avrdude has started the actual burn yet, and if so, track the '#' characters that it prints. Each '#' represents 1% of the total burn process (50 for write and 50 for read)
if ( burnStarted == true )
{
if ( data == "#" ) { burnPercent += 1 ; }
e . sender . send ( "upload percent" , burnPercent ) ;
}
else
{
2021-01-12 15:47:17 -08:00
//This is a hack, but basically watch the output from teensy loader for the term 'Writing | ', everything after that is the #s indicating 1% of burn.
2020-10-20 19:05:17 -07:00
if ( teensyLoaderErr . substr ( teensyLoaderErr . length - 10 ) == "Writing | " )
{
burnStarted = true ;
}
}
} ) ;
2019-01-01 15:47:54 -08:00
child . on ( 'error' , ( err ) => {
console . log ( 'Failed to start subprocess.' ) ;
2019-01-02 15:50:05 -08:00
console . log ( err ) ;
2020-10-20 19:05:17 -07:00
teensyLoaderIsRunning = false ;
2019-01-01 15:47:54 -08:00
} ) ;
child . on ( 'close' , ( code ) => {
2020-10-20 19:05:17 -07:00
teensyLoaderIsRunning = false ;
2019-01-01 21:18:31 -08:00
if ( code !== 0 )
{
2020-10-20 19:05:17 -07:00
console . log ( ` teensyLoader process exited with code ${ code } ` ) ;
e . sender . send ( "upload error" , teensyLoaderErr )
teensyLoaderErr = "" ;
2019-01-01 21:18:31 -08:00
}
else
{
e . sender . send ( "upload completed" , code )
2019-01-01 15:47:54 -08:00
}
} ) ;
2020-10-21 00:52:11 -07:00
} ) ;