OpenBuilds-CONTROL/index.js

3707 wiersze
115 KiB
JavaScript

process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = '1';
process.on('uncaughtException', function(err) {
//showErrorDialog(err, attempts = 2) // make two attempts to show an uncaughtException in a dialog
if (DEBUG) {
debug_log(err)
} else {
console.log(err);
}
})
function showErrorDialog(err, attempts) {
console.error('Attempting to show an error dialog.')
if (!attempts) return;
try {
let options = {
type: 'error',
buttons: ['OK'],
title: 'Error',
message: `An error occured.`,
detail: `${err.message}\r\r\rIf you feel this shouldn't be happening, please report it at:\r\rhttps://github.com/OpenBuilds/OpenBuilds-CONTROL/issues`,
};
let window = BrowserWindow.getFocusedWindow()
dialog.showMessageBoxSync(window, options)
} catch (e) {
console.error(`An error occurred trying show an error, ho-boy. ${e}. We'll try again ${attempts} more time(s).`)
setTimeout(() => {
showErrorDialog(err, --attempts)
}, millisecondDelay = 2000);
}
}
// To see console.log output run with `DEBUGCONTROL=true electron .` or set environment variable for DEBUGCONTROL=true
// debug_log debug overhead
DEBUG = false;
if (process.env.DEBUGCONTROL) {
DEBUG = true;
console.log("Console Debugging Enabled")
}
function debug_log() {
if (DEBUG) {
console.log.apply(this, arguments);
}
} // end Debug Logger
debug_log("Starting OpenBuilds CONTROL v" + require('./package').version)
var config = {};
config.webPorts = [3000, 3020, 3200, 3220]
config.webPortIdx = 0;
config.nextWebPort = function() {
config.webPort = config.webPorts[config.webPortIdx]
config.webPortIdx++
if (config.webPortIdx == config.webPorts.length) {
throw new Error(`No ports were available to start the http server.\r\rWe tried ports ${config.webPorts.join(",")}.`);
}
return config.webPort;
}
config.webPort = process.env.WEB_PORT || config.nextWebPort();
config.posDecimals = process.env.DRO_DECIMALS || 3;
config.grblWaitTime = 0.5;
config.firmwareWaitTime = 4;
var express = require("express");
var app = express();
var http = require("http").Server(app);
var https = require('https');
//var ioServer = require('socket.io');
const {
Server: ioServer
} = require('socket.io');
var io = new ioServer();
var fs = require('fs');
var path = require("path");
const join = require('path').join;
const {
mkdirp
} = require('mkdirp')
//const drivelist = require('drivelist'); // removed in 1.0.350 due to Drivelist stability issues
// FluidNC test
var fluidncConfig = "";
// FluidNC end test
app.use(express.static(path.join(__dirname, "app")));
//app.use(express.limit('200M'));
app.use(function setCommonHeaders(req, res, next) {
res.set("Access-Control-Allow-Private-Network", "true");
next();
});
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Private-Network", "true");
next();
});
// Interface firmware flash
app.post('/uploadCustomFirmware', (req, res) => {
// 'firmwareBin' is the name of our file input field in the HTML form
let upload = multer({
storage: storage
}).single('firmwareBin');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields, if there were any
if (err instanceof multer.MulterError) {
return res.send(err);
} else if (err) {
return res.send(err);
}
// Display uploaded image for user validation
firmwareImagePath = req.file.path;
res.send(req.file.path);
});
});
// end Interface Firmware flash
//Note when renewing Convert zerossl cert first `openssl.exe rsa -in domain-key.key -out domain-key.key`
// fix error: App threw an error during load
// Error: error:06000066:public key routines:OPENSSL_internal:DECODE_ERROR
var httpsOptions = {
key: fs.readFileSync(path.join(__dirname, 'privkey1.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fullchain1.pem'))
};
const httpsserver = https.createServer(httpsOptions, app).listen(3001, function() {
debug_log('https: listening on:' + ip.address() + ":3001");
});
const httpserver = http.listen(config.webPort, '0.0.0.0', httpServerSuccess).on('error', httpServerError);
function httpServerSuccess() {
debug_log('http: listening on:' + ip.address() + ":" + config.webPort);
if (jogWindow) {
jogWindow.loadURL(`http://localhost:${config.webPort}/`);
}
}
function httpServerError(error) {
// If unable to start (port in use) - try next port in array from config.nextWebPort()
console.error(error.message);
httpserver.listen(config.nextWebPort());
}
io.attach(httpserver);
io.attach(httpsserver);
const grblStrings = require("./grblStrings.js");
// Serial
const {
SerialPort
} = require('serialport')
const {
ReadlineParser
} = require('@serialport/parser-readline')
// telnet
const net = require('net');
var ip = require("ip");
const Evilscan = require('evilscan');
var md5 = require('md5');
var _ = require('lodash');
var formidable = require('formidable')
var lastsentuploadprogress = 0;
// Electron app
const electron = require('electron');
const electronApp = electron.app;
electronApp.setAppUserModelId("openbuilds.control")
const {
dialog
} = require('electron')
electronApp.commandLine.appendSwitch('ignore-gpu-blacklist')
electronApp.commandLine.appendSwitch('enable-gpu-rasterization')
electronApp.commandLine.appendSwitch('enable-zero-copy')
if (isElectron()) {
debug_log("Local User Data: " + electronApp.getPath('userData'))
}
const BrowserWindow = electron.BrowserWindow;
const Tray = electron.Tray;
const nativeImage = require('electron').nativeImage
const Menu = require('electron').Menu
var forceQuit
var appIcon = null,
jogWindow = null,
mainWindow = null
var autoUpdater
var updateIsDownloading = false;
if (isElectron()) {
autoUpdater = require("electron-updater").autoUpdater
var availversion = '0.0.0'
autoUpdater.on('checking-for-update', () => {
var string = 'Starting update... Please wait';
var output = {
'command': 'autoupdate',
'response': string
}
io.sockets.emit('updatedata', output);
})
autoUpdater.on('update-available', (ev, info) => {
updateIsDownloading = true;
var string = "Starting Download: v" + ev.version;
availversion = ev.version
var output = {
'command': 'autoupdate',
'response': string
}
io.sockets.emit('updatedata', output);
debug_log(JSON.stringify(ev))
})
autoUpdater.on('update-not-available', (ev, info) => {
var string = 'Update not available. Installed version: ' + require('./package').version + " / Available version: " + ev.version + ".\n";
if (require('./package').version === ev.version) {
string += "You are already running the latest version!"
}
var output = {
'command': 'autoupdate',
'response': string
}
io.sockets.emit('updatedata', output);
debug_log(JSON.stringify(ev))
})
autoUpdater.on('error', (ev, err) => {
if (err) {
var string = 'Error in auto-updater: \n' + err.split('SyntaxError')[0];
} else {
var string = 'Error in auto-updater';
}
var output = {
'command': 'autoupdate',
'response': string
}
io.sockets.emit('updatedata', output);
})
autoUpdater.on('download-progress', (ev, progressObj) => {
updateIsDownloading = true;
var string = 'Download update ... ' + ev.percent.toFixed(1) + '%';
debug_log(string)
var output = {
'command': 'autoupdate',
'response': string
}
io.sockets.emit('updatedata', output);
io.sockets.emit('updateprogress', ev.percent.toFixed(0));
})
autoUpdater.on('update-downloaded', (info) => {
var string = "New update ready";
var output = {
'command': 'autoupdate',
'response': string
}
io.sockets.emit('updatedata', output);
io.sockets.emit('updateready', availversion);
// repeat every minute
setTimeout(function() {
io.sockets.emit('updateready', availversion);
}, 1000 * 60 * 60 * 8) // 8hrs before alerting again if it was snoozed
updateIsDownloading = false;
});
} else {
debug_log("Running outside Electron: Disabled AutoUpdater")
}
if (isElectron()) {
var uploadsDir = electronApp.getPath('userData') + '/upload/';
} else {
var uploadsDir = process.env.APPDATA || (process.platform == 'darwin' ? process.env.HOME + 'Library/Preferences' : '/var/local')
}
var jobStartTime = false;
var jobCompletedMsg = ""; // message sent when job is done
var uploadedgcode = ""; // var to store uploaded gcode
var uploadedworkspace = ""; // var to store uploaded OpenBuildsCAM Workspace
mkdirp(uploadsDir).then(made =>
debug_log('Created Uploads Temp Directory'))
// Check USB Selective Suspend Settings
function checkPowerSettings() {
if (process.platform == 'win32') {
debug_log("Checking Power Settings")
var powerplan = "",
usbselectiveAC = false,
usbselectiveDC = false;
const {
exec
} = require('child_process');
const cfg = exec('powercfg /GETACTIVESCHEME', function(error, stdout, stderr) {
if (error) {
debug_log(error.stack);
debug_log('Error code: ' + error.code);
debug_log('Signal received: ' + error.signal);
}
// console.log('Child Process STDOUT: ' + stdout);
// console.log('Child Process STDERR: ' + stderr);
powerplan = stdout.split(":")[1].split("()")[0].trim()
});
cfg.on('exit', function(code) {
debug_log('powercfg /GETACTIVESCHEME exited with exit code ' + code);
if (code == 0) {
const usbsetting = exec('powercfg /q ' + powerplan, function(error, stdout, stderr) {
if (error) {
debug_log(error.stack);
debug_log('Error code: ' + error.code);
debug_log('Signal received: ' + error.signal);
}
// console.log('Child Process STDOUT: ' + stdout);
// console.log('Child Process STDERR: ' + stderr);
usbselective = (stdout.slice(stdout.search("USB selective suspend setting") - 1)).split("\n")
usbselective.length = 7;
if (usbselective[5].indexOf("0x00000000") != -1) {
debug_log("USB Selective Suspend DISABLED on AC power ")
status.driver.powersettings.usbselectiveAC = false;
} else if (usbselective[5].indexOf("0x00000001") != -1) {
debug_log("USB Selective Suspend ENABLED on AC power ")
status.driver.powersettings.usbselectiveAC = true;
}
if (usbselective[6].indexOf("0x00000000") != -1) {
debug_log("USB Selective Suspend DISABLED on DC power ")
status.driver.powersettings.usbselectiveDC = false;
} else if (usbselective[6].indexOf("0x00000001") != -1) {
debug_log("USB Selective Suspend ENABLED on DC power ")
status.driver.powersettings.usbselectiveDC = true;
}
});
usbsetting.on('exit', function(code) {
debug_log('powercfg /q exited with exit code ' + code);
setTimeout(function() {
debug_log(status.driver.powersettings.usbselectiveDC, status.driver.powersettings.usbselectiveAC)
}, 100);
})
}
});
// end USB Selective Suspend
}
}
var oldportslist, oldiplist;
var oldpinslist;
const iconPath = path.join(__dirname, 'app/icon.png');
const iconNoComm = path.join(__dirname, 'app/icon-notconnected.png');
const iconPlay = path.join(__dirname, 'app/icon-play.png');
const iconStop = path.join(__dirname, 'app/icon-stop.png');
const iconPause = path.join(__dirname, 'app/icon-pause.png');
const iconAlarm = path.join(__dirname, 'app/icon-bell.png');
var iosocket;
var lastCommand = false
var gcodeQueue = [];
var queuePointer = 0;
var statusLoop;
var frontEndUpdateLoop
var queueCounter;
var listPortsLoop;
var GRBL_RX_BUFFER_SIZE = 127; // 128 characters
var GRBLHAL_RX_BUFFER_SIZE = 1023; // 128 characters
var sentBuffer = [];
var xPos = 0.00;
var yPos = 0.00;
var zPos = 0.00;
var aPos = 0.00;
var xOffset = 0.00;
var yOffset = 0.00;
var zOffset = 0.00;
var aOffset = 0.00;
var feedOverride = 100,
spindleOverride = 100;
//regex to identify MD5hash on sdupload later
var re = new RegExp("^[a-f0-9]{32}");
var status = {
login: false,
driver: {
version: require('./package').version,
ipaddress: ip.address(),
operatingsystem: false,
powersettings: {
usbselectiveAC: null,
usbselectiveDC: null
},
},
machine: {
name: '',
has4thAxis: false,
inputs: [],
overrides: {
feedOverride: 100, //
spindleOverride: 100, //
realFeed: 0, //
realSpindle: 0 //
},
//
tool: {
nexttool: {
number: 0,
line: ""
}
},
modals: {
//motionmode: "G0", // G0, G1, G2, G3, G38.2, G38.3, G38.4, G38.5, G80
coordinatesys: "G54", // G54, G55, G56, G57, G58, G59
plane: "G17", // G17, G18, G19
distancemode: "G90", // G90, G91
arcdistmode: "G91.1", // G91.1
feedratemode: "G94", // G93, G94
unitsmode: "G21", // G20, G21
radiuscomp: "G40", // G40
tlomode: "G49", // G43.1, G49
// programmode: "M0", // M0, M1, M2, M30
spindlestate: "M5", // M3, M4, M5
coolantstate: "M9", // M7, M8, M9
homedRecently: false
// tool: "0",
// spindle: "0",
// feedrate: "0"
},
probe: {
x: 0.00,
y: 0.00,
z: 0.00,
state: -1
},
position: {
work: {
x: 0,
y: 0,
z: 0,
a: 0,
e: 0
},
offset: {
x: 0,
y: 0,
z: 0,
a: 0,
e: 0
}
},
firmware: {
type: "",
platform: "",
version: "",
date: "",
buffer: [],
features: [],
blockBufferSize: 0,
rxBufferSize: 0,
},
},
comms: {
connectionStatus: 0, //0 = not connected, 1 = opening, 2 = connected, 3 = playing, 4 = paused, 5 = alarm, 6 = firmware upgrade
runStatus: "Pending", // 0 = init, 1 = idle, 2 = alarm, 3 = stop, 4 = run, etc?
queue: 0,
blocked: false,
paused: false,
controllerBuffer: 0, // Seems like you are tracking available buffer? Maybe nice to have in frontend?
interfaces: {
type: "",
ports: "",
networkDevices: [],
activePort: "" // or activeIP in the case of wifi/telnet?
},
alarm: ""
},
interface: {
diskdrive: false,
firmware: {
availVersion: "",
installedVersion: "",
},
connected: false
}
};
async function findPorts() {
const ports = await SerialPort.list()
// console.log(ports)
oldportslist = ports;
status.comms.interfaces.ports = ports;
}
findPorts()
async function findChangedPorts() {
const ports = await SerialPort.list()
// console.log(ports)
status.comms.interfaces.ports = ports;
if (!_.isEqual(ports, oldportslist)) {
var newPorts = _.differenceWith(ports, oldportslist, _.isEqual)
if (newPorts.length > 0) {
debug_log("Plugged " + newPorts[0].path);
}
var removedPorts = _.differenceWith(oldportslist, ports, _.isEqual)
if (removedPorts.length > 0) {
debug_log("Unplugged " + removedPorts[0].path);
}
}
oldportslist = ports;
// throw new Error('No ports found')
findPorts()
}
// async function findDisks() {
// const drives = await drivelist.list();
// status.interface.diskdrives = drives;
// } // removed in 1.0.350 due to Drivelist stability issues
var PortCheckinterval = setInterval(function() {
if (status.comms.connectionStatus == 0) {
findChangedPorts();
}
//findDisks(); // removed in 1.0.350 due to Drivelist stability issues
}, 1000);
// var telnetCheckinterval = setInterval(function() {
// if (status.comms.connectionStatus == 0) {
// scanForTelnetDevices();
// }
// }, 30000);
// scanForTelnetDevices();
checkPowerSettings()
// JSON API
app.get('/api/version', (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
data = {
"application": "OMD",
"version": require('./package').version,
"ipaddress": ip.address() + ":" + config.webPort
}
res.send(JSON.stringify(data), null, 2);
})
app.get('/activate', (req, res) => {
debug_log(req.hostname)
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.send('Host: ' + req.hostname + ' asked to activate OpenBuilds CONTROL v' + require('./package').version);
showJogWindow()
setTimeout(function() {
io.sockets.emit('activate', req.hostname);
}, 500);
})
// Upload
app.get('/upload', (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.sendFile(__dirname + '/app/upload.html');
})
app.get('/gcode', (req, res) => {
if (uploadedgcode.indexOf('$') != 0) { // Ignore grblSettings jobs
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.send(uploadedgcode);
}
})
app.get('/workspace', (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.send(uploadedworkspace);
})
// http-post version of runJob
app.post('/runjob', (req, res) => {
// 'firmwareBin' is the name of our file input field in the HTML form
let upload = multer({
storage: storage
}).single('file');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields, if there were any
if (err instanceof multer.MulterError) {
return res.send(err);
} else if (err) {
return res.send(err);
}
fs.readFile(req.file.path, 'utf8', function(err, data) {
if (err) {
return console.log(err);
}
var object = {
isJob: true,
//completedMsg: "",
data: data,
}
runJob(object)
});
res.send(`Running ` + req.file.path);
});
});
// File Post
app.post('/upload', function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
//debug_log(req)
uploadprogress = 0
var form = new formidable.IncomingForm();
form.maxFileSize = 300 * 1024 * 1024;
form.parse(req, function(err, fields, files) {
// debug_log(files);
});
form.on('fileBegin', function(name, file) {
debug_log(JSON.stringify(name));
debug_log(JSON.stringify(file));
debug_log('Uploading ' + file.filepath);
});
form.on('progress', function(bytesReceived, bytesExpected) {
uploadprogress = parseInt(((bytesReceived * 100) / bytesExpected).toFixed(0));
if (uploadprogress != lastsentuploadprogress) {
lastsentuploadprogress = uploadprogress;
}
debug_log('Progress ' + uploadprogress + "% / " + bytesReceived + "b");
});
form.on('file', function(name, file) {
debug_log('Uploaded ' + file.filepath);
showJogWindow()
readFile(file.filepath)
});
form.on('aborted', function() {
// Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. After this event is emitted, an error event will follow. In the future there will be a separate 'timeout' event (needs a change in the node core).
});
form.on('end', function() {
//Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.
res.end();
});
res.sendFile(__dirname + '/app/upload.html');
});
app.on('certificate-error', function(event, webContents, url, error,
certificate, callback) {
event.preventDefault();
callback(true);
});
io.on("connection", function(socket) {
debug_log("New IO Connection ");
iosocket = socket;
if (status.machine.firmware.type == 'grbl') {
debug_log("Is Grbl");
// // handle Grbl RESET external input
// if (status.machine.inputs.length > 0) {
// for (i = 0; i < status.machine.inputs.length; i++) {
// switch (status.machine.inputs[i]) {
// case 'R':
// // debug_log('PIN: SOFTRESET');
// safetosend = true;
// break;
// }
// }
// } else {
// setTimeout(function() {
debug_log("Emit Grbl: 1");
io.sockets.emit('grbl', status.machine.firmware)
// }, 10000);
// }
//
// if (safetosend != undefined && safetosend == true) {
// setTimeout(function() {
// debug_log("Emit Grbl: 2");
// io.sockets.emit('grbl', status.machine.firmware)
// }, 10000);
// }
}
// Global Update loop
clearInterval(frontEndUpdateLoop);
frontEndUpdateLoop = setInterval(function() {
io.sockets.emit("status", status);
}, 100);
socket.on("scannetwork", function(data) {
scanForTelnetDevices(data)
})
socket.on("openFile", function(data) {
dialog.showOpenDialog(jogWindow, {
properties: ['openFile']
}).then(result => {
console.log(result.canceled)
console.log(result.filePaths)
var openFilePath = result.filePaths[0];
if (openFilePath !== "") {
debug_log("path" + openFilePath);
readFile(openFilePath);
}
}).catch(err => {
console.log(err)
})
})
socket.on("openInterfaceDir", function(data) {
dialog.showOpenDialog(jogWindow, {
properties: ['openDirectory'],
title: "Select the USB Flashdrive you want to use with Interface"
}).then(result => {
console.log(result.canceled)
console.log(result.filePaths)
io.sockets.emit("interfaceDrive", result.filePaths[0]);
status.interface.diskdrive = result.filePaths[0]
}).catch(err => {
console.log(err)
})
})
socket.on("openbuilds", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://www.openbuilds.com')
});
socket.on("openbuildspartstore", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://www.openbuildspartstore.com')
});
socket.on("carveco", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://carveco.com/carveco-software-range/?ref=openbuilds')
});
socket.on("fabber", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://www.getfabber.com/openbuilds?ref=OpenBuilds')
});
socket.on("lightburn", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://openbuildspartstore.com/lightburn/')
});
socket.on("vectric", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://openbuildspartstore.com/vectric/')
});
socket.on("opencam", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://cam.openbuilds.com')
});
socket.on("opendocs", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://docs.openbuilds.com/')
});
socket.on("openforum", function(data) {
const {
shell
} = require('electron')
shell.openExternal('https://openbuilds.com/threads/openbuilds-control-software.13121/')
});
socket.on("gpuinfo", function(data) {
// GPU
var gpuInfoWindow = new BrowserWindow({
// 1366 * 768 == minimum to cater for
width: 800,
height: 800,
fullscreen: false,
center: true,
resizable: true,
maximizable: true,
title: "OpenBuilds CONTROL: Chromium's GPU Report",
frame: true,
autoHideMenuBar: true,
//icon: '/app/favicon.png',
icon: nativeImage.createFromPath(
path.join(__dirname, "/app/favicon.png")
),
webgl: true,
experimentalFeatures: true,
experimentalCanvasFeatures: true,
offscreen: true,
backgroundColor: "#fff"
});
gpuInfoWindow.loadURL("chrome://gpu");
gpuInfoWindow.once('ready-to-show', () => {
gpuInfoWindow.show()
gpuInfoWindow.setAlwaysOnTop(true);
gpuInfoWindow.focus();
gpuInfoWindow.setAlwaysOnTop(false);
})
});
socket.on("minimisetotray", function(data) {
jogWindow.hide();
});
socket.on("minimize", function(data) {
jogWindow.minimize();
});
socket.on("maximize", function(data) {
if (jogWindow.isFullScreen()) {
jogWindow.setFullScreen(false);
}
if (jogWindow.isMaximized()) {
jogWindow.unmaximize();
} else {
jogWindow.maximize();
}
});
socket.on("fullscreen", function(data) {
if (jogWindow.isFullScreen()) {
jogWindow.setFullScreen(false);
} else {
jogWindow.setFullScreen(true);
}
});
socket.on("quit", function(data) {
if (appIcon) {
appIcon.destroy();
}
electronApp.exit(0);
});
socket.on("applyUpdate", function(data) {
autoUpdater.quitAndInstall();
})
socket.on("downloadUpdate", function(data) {
if (!updateIsDownloading) {
if (typeof autoUpdater !== 'undefined') {
autoUpdater.checkForUpdates();
} else {
debug_log("autoUpdater not found")
}
}
})
socket.on("flashGrbl", function(data) {
var port = data.port;
var firmwareImagePath = data.file;
var board = data.board
var customImg = data.customImg
console.log(__dirname, file, data.file)
if (customImg) {
var firmwarePath = data.file
} else {
var firmwarePath = path.join(__dirname, data.file)
}
console.log("-------------------------------------------")
console.log(firmwarePath)
console.log("-------------------------------------------")
if (status.comms.connectionStatus > 0) {
debug_log('WARN: Closing Port ' + port);
stopPort();
} else {
debug_log('ERROR: Machine connection not open!');
}
function flashGrblCallback(debugString, port) {
debug_log(port, debugString);
var data = {
'port': port,
'string': debugString
}
io.sockets.emit("progStatus", data);
}
// setTimeout(function() {
// var avrgirl = new Avrgirl({
// board: board,
// port: port,
// debug: function(debugString) {
// var port = this.connection.options.port;
// flashGrblCallback(debugString, port)
// }
// });
//
// debug_log(JSON.stringify(avrgirl));
//
// status.comms.connectionStatus = 6;
// avrgirl.flash(firmwarePath, function(error) {
// if (error) {
// console.error(error);
// io.sockets.emit("progStatus", 'Flashing FAILED!');
// status.comms.connectionStatus = 0;
// } else {
// console.info('done.');
// io.sockets.emit("progStatus", 'Programmed Succesfully');
// io.sockets.emit("progStatus", 'Please Reconnect');
// status.comms.connectionStatus = 0;
// }
// status.comms.connectionStatus = 0;
// });
// }, 1000)
})
socket.on("flashGrblHal", function(data) {
if (status.comms.connectionStatus > 0) {
debug_log('WARN: Closing Port ' + port);
stopPort();
} else {
debug_log('ERROR: Machine connection not open!');
}
console.log(JSON.stringify(data), null, 4);
flashGrblHal(data)
})
socket.on("flashInterface", function(data) {
if (status.comms.connectionStatus > 0) {
debug_log('WARN: Closing Port ' + port);
stopPort();
} else {
debug_log('ERROR: Machine connection not open!');
}
flashInterface(data)
})
socket.on("writeInterfaceUsbDrive", function(data) {
debug_log(data)
//data.drive = mountpoint dest
//data.controller = type of controller
if (data.controller == "blackbox4x" || data.controller == "genericgrbl") {
var probesrc = path.join(__dirname, './app/wizards/interface/PROBE/');
var profilesrc = path.join(__dirname, './app/wizards/interface/PROFILESGRBL/');
} else if (data.controller == "blackboxx32" || data.controller == "genericgrblhal") {
var probesrc = path.join(__dirname, './app/wizards/interface/PROBE/');
var profilesrc = path.join(__dirname, './app/wizards/interface/PROFILESHAL/');
}
var probedest = path.join(data.drive, "/PROBE/");
var profiledest = path.join(data.drive, "/PROFILES/");
var ncp = require('ncp').ncp;
ncp.limit = 16;
var output = {
'command': 'Interface USB Drive',
'response': "Starting to copy data to " + data.drive,
'type': 'info'
}
io.sockets.emit('data', output);
var errorCount = 0;
if (data.ssid && data.psk) {
const folderPath = path.join(data.drive, "CONFIG");
// Create the subfolder if it doesn't exist and then write the file
fs.mkdir(folderPath, {
recursive: true
}, (err) => {
if (err) {
var output = {
'command': 'Interface USB Drive',
'response': `Failed to create folder ${folderPath}! Error: ${err}`,
'type': 'error'
};
io.sockets.emit('data', output);
} else {
var fileContent = `${data.ssid}\n${data.psk}`;
fs.writeFile(path.join(folderPath, "wifi.cfg"), fileContent, (err) => {
if (err) {
errorCount++;
var output = {
'command': 'Interface USB Drive',
'response': `Failed to create Wifi Configuration file in ${folderPath}! Error: ${err}`,
'type': 'error'
};
io.sockets.emit('data', output);
} else {
var output = {
'command': 'Interface USB Drive',
'response': `Created Wifi Configuration file in ${folderPath} successfully!`,
'type': 'success'
};
io.sockets.emit('data', output);
}
});
}
});
}
ncp(probesrc, probedest, function(err) {
if (err) {
var output = {
'command': 'Interface USB Drive',
'response': "Failed to copy PROBE macros to " + probedest + ": " + JSON.stringify(err),
'type': 'error'
}
io.sockets.emit('data', output);
errorCount++
} else {
var output = {
'command': 'Interface USB Drive',
'response': "Copied PROBE macros to " + probedest + " succesfully!",
'type': 'success'
}
io.sockets.emit('data', output);
}
});
ncp(profilesrc, profiledest, function(err) {
if (err) {
var output = {
'command': 'Interface USB Drive',
'response': "Failed to copy MACHINE PROFILES to " + profiledest + ": " + JSON.stringify(err),
'type': 'error'
}
io.sockets.emit('data', output);
errorCount++
} else {
var output = {
'command': 'Interface USB Drive',
'response': "Copied MACHINE PROFILES to " + profiledest + " succesfully!",
'type': 'success'
}
io.sockets.emit('data', output);
}
});
setTimeout(function() {
if (errorCount == 0) {
var output = {
'command': 'Interface USB Drive',
'response': "Finished copying supporting files to Drive " + data.drive,
'type': 'success'
}
io.sockets.emit('data', output);
var output = {
'command': 'Interface USB Drive',
'response': "Please Eject the drive (Safely Remove) and insert it into your Interface's USB port",
'type': 'info'
}
io.sockets.emit('data', output);
}
}, 500);
});
socket.on("connectTo", function(data) { // If a user picks a port to connect to, open a Node SerialPort Instance to it
if (status.comms.connectionStatus < 1) {
if (data.type == "usb") {
console.log("connect", "Connecting to " + data.port + " via " + data.type);
// Fix for autoreset getting stuck on MacOS with Silabs Chip
var allowRtsCts = false
var allowHupcl = false
if (process.platform == 'darwin') {
allowRtsCts = true
allowHupcl = true
}
port = new SerialPort({
path: data.port,
baudRate: parseInt(data.baud),
rtscts: allowRtsCts,
hupcl: allowHupcl // Don't set DTR - useful for X32 Reset
});
} else if (data.type == "telnet") {
console.log("connect", "Connecting to " + data.ip + " via " + data.type);
port = net.connect(23, data.ip);
port.isOpen = true;
}
const parser = port.pipe(new ReadlineParser({
delimiter: '\r\n'
}))
// port.on("data", function(data) {
// console.log(data)
// })
port.on("error", function(err) {
if (err.message != "Port is not open") {
debug_log("Error: ", err.message);
var output = {
'command': '',
'response': "PORT ERROR: " + err.message,
'type': 'error'
}
io.sockets.emit('data', output);
if (status.comms.connectionStatus > 0) {
debug_log('WARN: Closing Port ' + port.path);
status.comms.connectionStatus = 0;
stopPort();
} else {
debug_log('ERROR: Machine connection not open!');
}
}
});
port.on("ready", function(e) {
portOpened(port, data)
});
port.on("open", function(e) {
portOpened(port, data)
});
port.on("close", function() { // open errors will be emitted as an error event
debug_log("PORT INFO: Port closed");
var output = {
'command': 'disconnect',
'response': "PORT INFO: Port closed",
'type': 'info'
}
io.sockets.emit('data', output);
status.comms.connectionStatus = 0;
}); // end port.onclose
function portOpened(port, data) {
debug_log("PORT INFO: Connected to " + port.path + " at " + port.baudRate);
var output = {
'command': 'connect',
'response': "PORT INFO: Port is now open: " + port.path + " - Attempting to detect Controller...",
'type': 'info'
}
io.sockets.emit('data', output);
status.comms.connectionStatus = 1;
addQRealtime("\n"); // this causes smoothie and grblHAL to send the welcome string
var output = {
'command': 'connect',
'response': "Attempting to detect Controller (1): (Autoreset)",
'type': 'info'
}
io.sockets.emit('data', output);
setTimeout(function() { //wait for controller to be ready
if (status.machine.firmware.type.length < 1) {
debug_log("Didnt detect firmware after AutoReset. Lets see if we have Grbl instance with a board that doesnt have AutoReset");
var output = {
'command': 'connect',
'response': "Attempting to detect Controller (2): (Ctrl+X)",
'type': 'info'
}
io.sockets.emit('data', output);
addQRealtime(String.fromCharCode(0x18)); // ctrl-x (needed for rx/tx connection)
debug_log("Sent: Ctrl+x");
}
}, config.grblWaitTime * 1000);
setTimeout(function() { //wait for controller to be ready
if (status.machine.firmware.type.length < 1) {
debug_log("No firmware yet, probably not Grbl then. lets see if we have Smoothie?");
var output = {
'command': 'connect',
'response': "Attempting to detect Controller (3): (others)",
'type': 'info'
}
io.sockets.emit('data', output);
addQRealtime("version\n"); // Check if it's Smoothieware?
debug_log("Sent: version");
}
}, config.grblWaitTime * 2000);
setTimeout(function() {
if (status.machine.firmware.type.length > 1) {
if (status.machine.firmware.type === "grbl") {
debug_log("GRBL detected");
var output = {
'command': 'connect',
'response': "Detecting Firmware: Detected Grbl Succesfully",
'type': 'info'
}
setTimeout(function() {
io.sockets.emit('grbl', status.machine.firmware)
//v1.0.318 - commented out as a test - too many normal alarms clear prematurely
//io.sockets.emit('errorsCleared', true);
}, 600)
// Start interval for status queries
clearInterval(statusLoop);
statusLoop = setInterval(function() {
if (status.comms.connectionStatus > 0) {
addQRealtime("?");
}
}, 200);
status.machine.modals.homedRecently = false;
}
}
}, config.grblWaitTime * 3000)
if (config.firmwareWaitTime > 0) {
setTimeout(function() {
// Close port if we don't detect supported firmware after 2s.
if (status.machine.firmware.type.length < 1) {
debug_log("No supported firmware detected. Closing port " + port.path);
if (status.interface.connected) {
var output = {
'command': 'connect',
'response': `ERROR!: Connection established to INTERFACE, but no response from Grbl on the upstream controller. See https://docs.openbuilds.com/interface for more details. Closing port ` + port.path,
'type': 'error'
}
} else {
var output = {
'command': 'connect',
'response': `ERROR!: No Response from Controller - See https://docs.openbuilds.com/doku.php?id=docs:blackbox:faq-usb-connection-failed for troubleshooting information. Closing port ` + port.path,
'type': 'error'
}
}
io.sockets.emit('data', output);
stopPort();
} else {
if (data.type == "usb") {
var output = {
'command': 'connect',
'response': "Firmware Detected: " + status.machine.firmware.platform + " version " + status.machine.firmware.version + " dated " + status.machine.firmware.date + " on " + port.path,
'type': 'success'
}
} else if (data.type = "telnet") {
var output = {
'command': 'connect',
'response': "Firmware Detected: " + status.machine.firmware.platform + " version " + status.machine.firmware.version + " dated " + status.machine.firmware.date + " on " + data.ip,
'type': 'success'
}
}
io.sockets.emit('data', output);
}
}, config.firmwareWaitTime * 1000);
}
status.comms.connectionStatus = 2;
if (data.type == "usb") {
status.comms.interfaces.activePort = port.path;
status.comms.interfaces.type = data.type
status.comms.interfaces.activeBaud = port.baudRate;
} else if (data.type = "telnet") {
status.comms.interfaces.activePort = data.ip;
status.comms.interfaces.type = data.type
status.comms.interfaces.activeBaud = "net";
}
}
parser.on("data", function(data) {
//console.log(data)
var command = sentBuffer[0];
if (command == "$CD" && data != "ok") {
fluidncConfig = fluidncConfig += data + "\n"
}
if (data.indexOf("<") != 0) {
debug_log('data:', data)
}
// Grbl $I parser
if (data.indexOf("[VER:") === 0) {
status.machine.name = data.split(':')[2].split(']')[0].toLowerCase()
io.sockets.emit("status", status);
io.sockets.emit("machinename", data.split(':')[2].split(']')[0].toLowerCase());
status.machine.firmware.date = data.split(':')[1].split(".")[2];
}
if (data.indexOf("[OPT:") === 0) {
var startOpt = data.search(/opt:/i) + 4;
var grblOpt;
if (startOpt > 4) {
var grblOptLen = data.substr(startOpt).search(/]/);
grblOpts = data.substr(startOpt, grblOptLen).split(/,/);
status.machine.firmware.blockBufferSize = grblOpts[1];
status.machine.firmware.rxBufferSize = grblOpts[2];
var features = []
var i = grblOpts[0].length;
while (i--) {
features.push(grblOpts[0].charAt(i))
switch (grblOpts[0].charAt(i)) {
case 'Q':
debug_log('SPINDLE_IS_SERVO Enabled')
//
break;
case 'V': // Variable spindle enabled
debug_log('Variable spindle enabled')
//
break;
case 'N': // Line numbers enabled
debug_log('Line numbers enabled')
//
break;
case 'M': // Mist coolant enabled
debug_log('Mist coolant enabled')
//
break;
case 'C': // CoreXY enabled
debug_log('CoreXY enabled')
//
break;
case 'P': // Parking motion enabled
debug_log('Parking motion enabled')
//
break;
case 'Z': // Homing force origin enabled
debug_log('Homing force origin enabled')
//
break;
case 'H': // Homing single axis enabled
debug_log('Homing single axis enabled')
//
break;
case 'T': // Two limit switches on axis enabled
debug_log('Two limit switches on axis enabled')
//
break;
case 'A': // Allow feed rate overrides in probe cycles
debug_log('Allow feed rate overrides in probe cycles')
//
break;
case '$': // Restore EEPROM $ settings disabled
debug_log('Restore EEPROM $ settings disabled')
//
break;
case '#': // Restore EEPROM parameter data disabled
debug_log('Restore EEPROM parameter data disabled')
//
break;
case 'I': // Build info write user string disabled
debug_log('Build info write user string disabled')
//
break;
case 'E': // Force sync upon EEPROM write disabled
debug_log('Force sync upon EEPROM write disabled')
//
break;
case 'W': // Force sync upon work coordinate offset change disabled
debug_log('Force sync upon work coordinate offset change disabled')
//
break;
case 'L': // Homing init lock sets Grbl into an alarm state upon power up
debug_log('Homing init lock sets Grbl into an alarm state upon power up')
//
break;
}
}
status.machine.firmware.features = features;
io.sockets.emit("features", features);
}
}
// [PRB:0.000,0.000,0.000:0]
//if (data.indexOf("[PRB:") === 0 && command != "$#" && command != undefined) {
if (data.indexOf("[PRB:") === 0) {
debug_log(data)
var prbLen = data.substr(5).search(/\]/);
var prbData = data.substr(5, prbLen).split(/,/);
var success = data.split(':')[2].split(']')[0];
status.machine.probe.x = prbData[0];
status.machine.probe.y = prbData[1];
status.machine.probe.z = prbData[2].split(':')[0];
status.machine.probe.state = success;
if (success > 0) {
var output = {
'command': '[ PROBE ]',
'response': "Probe Completed.",
'type': 'success'
}
io.sockets.emit('data', output);
} else {
var output = {
'command': '[ PROBE ]',
'response': "Probe move ERROR - probe did not make contact within specified distance",
'type': 'error'
}
io.sockets.emit('data', output);
}
io.sockets.emit('prbResult', status.machine.probe);
};
if (data.indexOf("[GC:") === 0) {
gotModals(data)
}
if (data.indexOf("[INTF:") === 0) {
var output = {
'command': 'connect',
'response': "Detected an OpenBuilds Interface on port " + port.path,
'type': 'success'
}
io.sockets.emit('data', output);
status.interface.connected = true;
if (data.split(":")[1].indexOf("ver") == 0) {
var installedVersion = parseFloat(data.split(":")[1].split("]")[0].split("-")[1])
status.interface.firmware.installedVersion = installedVersion
var output = {
'command': 'connect',
'response': "OpenBuilds Interface Firmware Version: v" + installedVersion,
'type': 'info'
}
io.sockets.emit('data', output);
if (installedVersion < status.interface.firmware.availVersion) {
var output = {
'command': 'connect',
'response': "OpenBuilds Interface Firmware OUTDATED: v" + installedVersion + " can be upgraded to v" + status.interface.firmware.availVersion,
'type': 'error'
}
io.sockets.emit('data', output);
io.sockets.emit('interfaceOutdated', status);
}
}
io.sockets.emit("status", status);
}
// Machine Identification
if (data.indexOf("Grbl") === 0 || data.indexOf("[FIRMWARE:grblHAL]") === 0) { // Check if it's Grbl
debug_log(data)
status.comms.blocked = false;
if (data.indexOf("GrblHAL") === 0) {
status.machine.firmware.type = "grbl";
status.machine.firmware.platform = "grblHAL"
status.machine.firmware.version = data.substr(8, 4); // get version
} else if (data.indexOf("[FIRMWARE:grblHAL]") === 0) {
status.machine.firmware.type = "grbl";
status.machine.firmware.platform = "grblHAL"
// Parse version from seperate [VER:...] line not here for this response
} else if (data.indexOf("FluidNC") != -1) { // Grbl 3.6 [FluidNC v3.6.5 (wifi) '$' for help]
status.machine.firmware.type = "grbl";
status.machine.firmware.platform = "FluidNC"
status.machine.firmware.version = data.substr(19, 5); // get version
} else {
status.machine.firmware.type = "grbl";
status.machine.firmware.platform = "gnea"
status.machine.firmware.version = data.substr(5, 4); // get version
}
if (parseFloat(status.machine.firmware.version) < 1.1) { // If version is too old
if (status.machine.firmware.version.length < 3) {
debug_log('invalid version string, stay connected')
} else {
if (status.comms.connectionStatus > 0) {
debug_log('WARN: Closing Port ' + port.path + " / v" + parseFloat(status.machine.firmware.version));
// stopPort();
} else {
debug_log('ERROR: Machine connection not open!');
}
var output = {
'command': command,
'response': "Detected an unsupported version: Grbl " + status.machine.firmware.version + ". This is sadly outdated. Please upgrade to Grbl 1.1 or newer to use this software. Go to http://github.com/gnea/grbl",
'type': 'error'
}
io.sockets.emit('data', output);
}
}
status.machine.firmware.date = "";
// debug_log("GRBL detected");
// setTimeout(function() {
// io.sockets.emit('grbl', status.machine.firmware)
// //v1.0.318 - commented out as a test - too many normal alarms clear prematurely
// //io.sockets.emit('errorsCleared', true);
// }, 600)
// // Start interval for status queries
// clearInterval(statusLoop);
// statusLoop = setInterval(function() {
// if (status.comms.connectionStatus > 0) {
// addQRealtime("?");
// }
// }, 200);
status.machine.modals.homedRecently = false;
} else if (data.indexOf("LPC176") >= 0) { // LPC1768 or LPC1769 should be Smoothieware
status.comms.blocked = false;
debug_log("Smoothieware detected");
status.machine.firmware.type = "smoothie";
status.machine.firmware.version = data.substr(data.search(/version:/i) + 9).split(/,/);
status.machine.firmware.date = new Date(data.substr(data.search(/Build date:/i) + 12).split(/,/)).toDateString();
// Start interval for status queries
// statusLoop = setInterval(function() {
// if (status.comms.connectionStatus > 0) {
// addQRealtime("?");
// }
// }, 200);
var output = {
'command': "FIRMWARE ERROR",
'response': "Detected an unsupported version: Smoothieware " + status.machine.firmware.version + ". This software no longer support Smoothieware. \nLuckilly there is an alternative firmware you can install on your controller to make it work with this software. Check out Grbl-LPC at https://github.com/cprezzi/grbl-LPC - Grbl-LPC is a Grbl port for controllers using the NXP LPC176x chips, for example Smoothieboards",
'type': 'error'
}
io.sockets.emit('data', output);
stopPort();
} // end of machine identification
// Machine Feedback: Position
if (data.indexOf("<") === 0) {
// debug_log(' Got statusReport (Grbl & Smoothieware)')
// statusfeedback func
parseFeedback(data)
if (command == "?") {
var output = {
'command': command,
'response': data,
'type': 'info'
}
// debug_log(output.response)
io.sockets.emit('data', output);
}
// debug_log(data)
} else if (data.indexOf("ok") === 0) { // Got an OK so we are clear to send
io.sockets.emit('ok', command); // added per #325
// debug_log("OK FOUND")
if (status.machine.firmware.type === "grbl") {
// debug_log('got OK from ' + command)
command = sentBuffer.shift();
}
if (command == "$CD") {
io.sockets.emit('fluidncConfig', fluidncConfig);
}
status.comms.blocked = false;
send1Q();
} else if (data.indexOf('ALARM') === 0) { //} || data.indexOf('HALTED') === 0) {
debug_log("ALARM: " + data)
status.comms.connectionStatus = 5;
switch (status.machine.firmware.type) {
case 'grbl':
// sentBuffer.shift();
var alarmCode = parseInt(data.split(':')[1]);
debug_log('ALARM: ' + alarmCode + ' - ' + grblStrings.alarms(alarmCode));
status.comms.alarm = alarmCode + ' - ' + grblStrings.alarms(alarmCode)
if (alarmCode != 5) {
io.sockets.emit("toastErrorAlarm", 'ALARM: ' + alarmCode + ' - ' + grblStrings.alarms(alarmCode) + " [ " + command + " ]")
}
var output = {
'command': '',
'response': 'ALARM: ' + alarmCode + ' - ' + grblStrings.alarms(alarmCode) + " [ " + command + " ]",
'type': 'error'
}
io.sockets.emit('data', output);
break;
}
status.comms.connectionStatus = 5;
} else if (data.indexOf('WARNING: After HALT you should HOME as position is currently unknown') != -1) { //} || data.indexOf('HALTED') === 0) {
status.comms.connectionStatus = 2;
} else if (data.indexOf('Emergency Stop Requested') != -1) { //} || data.indexOf('HALTED') === 0) {
debug_log("Emergency Stop Requested")
status.comms.connectionStatus = 5;
} else if (data.indexOf('wait') === 0) { // Got wait from Repetier -> ignore
// do nothing
} else if (data.indexOf('error') === 0) { // Error received -> stay blocked stops queue
switch (status.machine.firmware.type) {
case 'grbl':
// sentBuffer.shift();
var errorCode = parseInt(data.split(':')[1]);
var lastAlarm = "";
if (errorCode == 9 && status.comms.connectionStatus == 5 && status.comms.alarm.length > 0) {
lastAlarm = "<hr>This error may just be a symptom of an earlier event:<br> ALARM: " + status.comms.alarm
}
debug_log('error: ' + errorCode + ' - ' + grblStrings.errors(errorCode) + " [ " + command + " ]");
var output = {
'command': '',
'response': 'error: ' + errorCode + ' - ' + grblStrings.errors(errorCode) + " [ " + command + " ]" + lastAlarm,
'type': 'error'
}
io.sockets.emit('data', output);
io.sockets.emit("toastError", 'error: ' + errorCode + ' - ' + grblStrings.errors(errorCode) + " [ " + command + " ]" + lastAlarm)
break;
}
debug_log("error;")
sentBuffer.shift();
status.comms.connectionStatus = 5;
} else if (data === ' ') {
// nothing
} else {
// do nothing with +data
}
if (data.indexOf("[MSG:Reset to continue]") === 0) {
switch (status.machine.firmware.type) {
case 'grbl':
debug_log("[MSG:Reset to continue] -> Sending Reset")
addQRealtime(String.fromCharCode(0x18)); // ctrl-x
break;
}
}
if (command) {
command = command.replace(/(\r\n|\n|\r)/gm, "");
// debug_log("CMD: " + command + " / DATA RECV: " + data.replace(/(\r\n|\n|\r)/gm, ""));
if (command != "?" && command != "M105" && data.length > 0 && data.indexOf('<') == -1) {
var string = "";
if (status.comms.sduploading) {
string += "SD: "
}
string += data //+ " [ " + command + " ]"
var output = {
'command': command,
'response': string,
'type': 'info'
}
// debug_log(output.response)
io.sockets.emit('data', output);
}
} else {
if (data.indexOf("<") != 0) {
var output = {
'command': "",
'response': data,
'type': 'info'
}
io.sockets.emit('data', output);
}
}
}); // end of parser.on(data)
}
});
socket.on('saveToSd', function(datapack) {
saveToSd(datapack);
});
socket.on('setqueuePointer', function(data) {
debug_log('Setting queuePointer to ' + data)
queuePointer = data
});
socket.on('runJob', function(object) {
// debug_log(data)
runJob(object);
});
socket.on('forceQueue', function(data) {
send1Q();
});
socket.on('serialInject', function(data) {
// Inject a live command into Serial stream in real-time (dev tool) even while a job is running, etc (straight Port.write from machineSend)
machineSend(data, true);
});
socket.on("dump", function(data) {
console.log(queuePointer);
console.log(gcodeQueue);
console.log(sentBuffer);
})
socket.on('runCommand', function(data) {
debug_log('Run Command (' + data.replace('\n', '|') + ')');
if (status.comms.connectionStatus > 0) {
if (data) {
data = data.split('\n');
for (var i = 0; i < data.length; i++) {
var line = data[i].split(';'); // Remove everything after ; = comment
var tosend = line[0].trim();
if (tosend.length > 0) {
addQToEnd(tosend);
}
}
status.comms.runStatus = 'Running'
// debug_log('sending ' + JSON.stringify(gcodeQueue))
send1Q();
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('jog', function(data) {
debug_log('Jog ' + data);
if (status.comms.connectionStatus > 0) {
data = data.split(',');
var dir = data[0];
var dist = parseFloat(data[1]);
var feed;
if (data.length > 2) {
feed = parseInt(data[2]);
if (feed) {
feed = 'F' + feed;
}
}
if (dir && dist && feed) {
debug_log('Adding jog commands to queue. Firmw=' + status.machine.firmware.type + ', blocked=' + status.comms.blocked + ', paused=' + status.comms.paused + ', Q=' + gcodeQueue.length);
switch (status.machine.firmware.type) {
case 'grbl':
addQToEnd('$J=G91G21' + dir + dist + feed);
send1Q();
break;
default:
debug_log('ERROR: Unknown firmware!');
break;
}
} else {
debug_log('ERROR: Invalid params!');
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('jogXY', function(data) {
debug_log('Jog XY' + data);
if (status.comms.connectionStatus > 0) {
// var data = {
// x: xincrement,
// y: yincrement,
// feed: feed
// }
var xincrement = parseFloat(data.x);
var yincrement = parseFloat(data.y);
var feed = parseFloat(data.feed)
if (feed) {
feed = 'F' + feed;
}
if (xincrement && yincrement && feed) {
debug_log('Adding jog commands to queue. blocked=' + status.comms.blocked + ', paused=' + status.comms.paused + ', Q=' + gcodeQueue.length);
switch (status.machine.firmware.type) {
case 'grbl':
addQToEnd('$J=G91G21X' + xincrement + " Y" + yincrement + " " + feed);
send1Q();
break;
default:
debug_log('ERROR: Unknown firmware!');
break;
}
} else {
debug_log('ERROR: Invalid params!');
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('jogTo', function(data) { // data = {x:xVal, y:yVal, z:zVal, mode:0(absulute)|1(relative), feed:fVal}
debug_log('JogTo ' + JSON.stringify(data));
if (status.comms.connectionStatus > 0) {
if (data.x !== undefined || data.y !== undefined || data.z !== undefined) {
var xVal = (data.x !== undefined ? 'X' + parseFloat(data.x) : '');
var yVal = (data.y !== undefined ? 'Y' + parseFloat(data.y) : '');
var zVal = (data.z !== undefined ? 'Z' + parseFloat(data.z) : '');
var mode = ((data.mode == 0) ? 0 : 1);
var feed = (data.feed !== undefined ? 'F' + parseInt(data.feed) : '');
debug_log('Adding jog commands to queue. blocked=' + status.comms.blocked + ', paused=' + status.comms.paused + ', Q=' + gcodeQueue.length);
switch (status.machine.firmware.type) {
case 'grbl':
addQToEnd('$J=G91G21' + mode + xVal + yVal + zVal + feed);
break;
default:
debug_log('ERROR: Unknown firmware!');
break;
}
} else {
debug_log('error Invalid params!');
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('setZero', function(data) {
debug_log('setZero(' + data + ')');
if (status.comms.connectionStatus > 0) {
switch (data) {
case 'x':
addQToEnd('G10 L20 P0 X0');
break;
case 'y':
addQToEnd('G10 L20 P0 Y0');
break;
case 'z':
addQToEnd('G10 L20 P0 Z0');
break;
case 'a':
addQToEnd('G10 L20 P0 A0');
break;
case 'all':
addQToEnd('G10 L20 P0 X0 Y0 Z0');
break;
case 'xyza':
addQToEnd('G10 L20 P0 X0 Y0 Z0 A0');
break;
}
send1Q();
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('gotoZero', function(data) {
debug_log('gotoZero(' + data + ')');
if (status.comms.connectionStatus > 0) {
switch (data) {
case 'x':
addQToEnd('G0 X0');
break;
case 'y':
addQToEnd('G0 Y0');
break;
case 'z':
addQToEnd('G0 Z0');
break;
case 'a':
addQToEnd('G0 A0');
break;
case 'all':
addQToEnd('G0 X0 Y0 Z0');
break;
case 'xyza':
addQToEnd('G0 X0 Y0 Z0 A0');
break;
}
send1Q();
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('setPosition', function(data) {
debug_log('setPosition(' + JSON.stringify(data) + ')');
if (status.comms.connectionStatus > 0) {
if (data.x !== undefined || data.y !== undefined || data.z !== undefined) {
var xVal = (data.x !== undefined ? 'X' + parseFloat(data.x) + ' ' : '');
var yVal = (data.y !== undefined ? 'Y' + parseFloat(data.y) + ' ' : '');
var zVal = (data.z !== undefined ? 'Z' + parseFloat(data.z) + ' ' : '');
var aVal = (data.a !== undefined ? 'A' + parseFloat(data.a) + ' ' : '');
addQToEnd('G10 L20 P0 ' + xVal + yVal + zVal + aVal);
send1Q();
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('probe', function(data) {
debug_log('probe(' + JSON.stringify(data) + ')');
if (status.comms.connectionStatus > 0) {
switch (status.machine.firmware.type) {
case 'grbl':
addQToEnd('G38.2 ' + data.direction + '-5 F1');
addQToEnd('G92 ' + data.direction + ' ' + data.probeOffset);
send1Q();
break;
default:
//not supported
debug_log('Command not supported by firmware!');
break;
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('feedOverride', function(data) {
debug_log(data)
if (status.comms.connectionStatus > 0) {
switch (status.machine.firmware.type) {
case 'grbl':
debug_log("current FRO = " + status.machine.overrides.feedOverride)
debug_log("requested FRO = " + data)
var curfro = parseInt(status.machine.overrides.feedOverride)
var reqfro = parseInt(data)
var delta;
if (reqfro == 100) {
addQRealtime(String.fromCharCode(0x90));
} else if (curfro < reqfro) {
// FRO Increase
delta = reqfro - curfro
debug_log("delta = " + delta)
var tens = Math.floor(delta / 10)
debug_log("need to send " + tens + " x10s increase")
// for (i = 0; i < tens; i++) {
// addQRealtime(String.fromCharCode(0x91));
// }
for (let i = 1; i < tens + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(0x91));
addQRealtime("?");
}, i * 50);
}
var ones = delta - (10 * tens);
debug_log("need to send " + ones + " x1s increase")
// for (i = 0; i < ones; i++) {
// addQRealtime(String.fromCharCode(0x93));
// }
for (let i = 1; i < ones + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(0x93));
addQRealtime("?");
}, i * 50);
}
} else if (curfro > reqfro) {
// FRO Decrease
delta = curfro - reqfro
debug_log("delta = " + delta)
var tens = Math.floor(delta / 10)
debug_log("need to send " + tens + " x10s decrease")
// for (i = 0; i < tens; i++) {
// addQRealtime(String.fromCharCode(0x92));
// }
for (let i = 1; i < tens + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(0x92));
addQRealtime("?");
}, i * 50);
}
var ones = delta - (10 * tens);
debug_log("need to send " + ones + " x1s decrease")
// for (i = 0; i < tens; i++) {
// addQRealtime(String.fromCharCode(0x94));
// }
for (let i = 1; i < ones + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(0x94));
addQRealtime("?");
}, i * 50);
}
}
addQRealtime("?");
status.machine.overrides.feedOverride = parseInt(reqfro); // Set now, but will be overriden from feedback from Grbl itself in next queryloop
break;
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('spindleOverride', function(data) {
if (status.comms.connectionStatus > 0) {
switch (status.machine.firmware.type) {
case 'grbl':
debug_log("current SRO = " + status.machine.overrides.spindleOverride)
debug_log("requested SRO = " + data)
var cursro = parseInt(status.machine.overrides.spindleOverride)
var reqsro = parseInt(data)
var delta;
if (reqsro == 100) {
addQRealtime(String.fromCharCode(153));
} else if (cursro < reqsro) {
// FRO Increase
delta = reqsro - cursro
debug_log("delta = " + delta)
var tens = Math.floor(delta / 10)
debug_log("need to send " + tens + " x10s increase")
// for (i = 0; i < tens; i++) {
// addQRealtime(String.fromCharCode(154));
// }
for (let i = 1; i < tens + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(154));
addQRealtime("?");
}, i * 50);
}
var ones = delta - (10 * tens);
debug_log("need to send " + ones + " x1s increase")
// for (i = 0; i < ones; i++) {
// addQRealtime(String.fromCharCode(156));
// }
for (let i = 1; i < ones + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(156));
addQRealtime("?");
}, i * 50);
}
} else if (cursro > reqsro) {
// FRO Decrease
delta = cursro - reqsro
debug_log("delta = " + delta)
var tens = Math.floor(delta / 10)
debug_log("need to send " + tens + " x10s decrease")
// for (i = 0; i < tens; i++) {
// addQRealtime(String.fromCharCode(155));
// }
for (let i = 1; i < tens + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(155));
addQRealtime("?");
}, i * 50);
}
var ones = delta - (10 * tens);
debug_log("need to send " + ones + " x1s decrease")
// for (i = 0; i < tens; i++) {
// addQRealtime(String.fromCharCode(157));
// }
for (let i = 1; i < ones + 1; i++) {
setTimeout(function timer() {
addQRealtime(String.fromCharCode(157));
addQRealtime("?");
}, i * 50);
}
}
addQRealtime("?");
status.machine.overrides.spindleOverride = parseInt(reqsro); // Set now, but will be overriden from feedback from Grbl itself in next queryloop
break;
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('laserTest', function(data) { // Laser Test Fire
laserTest(data);
});
socket.on('pause', function() {
pause();
});
socket.on('resume', function() {
unpause();
});
socket.on('stop', function(data) {
stop(data);
});
socket.on('clearAlarm', function(data) { // Clear Alarm
if (status.comms.connectionStatus > 0) {
data = parseInt(data);
debug_log('Clearing Queue: Method ' + data);
switch (data) {
case 1:
debug_log('Clearing Lockout');
switch (status.machine.firmware.type) {
case 'grbl':
addQRealtime('$X\n');
debug_log('Sent: $X');
break;
}
debug_log('Resuming Queue Lockout');
var output = {
'command': '[clear alarm]',
'response': "Operator clicked Clear Alarm: Cleared Lockout",
'type': 'info'
}
io.sockets.emit('data', output);
break;
case 2:
debug_log('Emptying Queue');
status.comms.queue = 0
queuePointer = 0;
gcodeQueue.length = 0; // Dump the queue
sentBuffer.length = 0; // Dump bufferSizes
queuePointer = 0;
debug_log('Clearing Lockout');
switch (status.machine.firmware.type) {
case 'grbl':
clearInterval(queueCounter);
if (jogWindow) {
jogWindow.setProgressBar(0);
}
addQRealtime(String.fromCharCode(0x18)); // ctrl-x
setTimeout(function() {
addQRealtime('$X\n');
debug_log('Sent: $X');
}, 500);
status.comms.blocked = false;
status.comms.paused = false;
break;
}
var output = {
'command': '[clear alarm]',
'response': "Operator clicked Clear Alarm: Cleared Lockout and Emptied Queue",
'type': 'info'
}
io.sockets.emit('data', output);
break;
}
status.comms.runStatus = 'Stopped'
status.comms.connectionStatus = 2;
status.comms.alarm = "";
io.sockets.emit('errorsCleared', true);
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('resetMachine', function() {
if (status.comms.connectionStatus > 0) {
debug_log('Reset Machine');
switch (status.machine.firmware.type) {
case 'grbl':
addQRealtime(String.fromCharCode(0x18)); // ctrl-x
debug_log('Sent: Code(0x18)');
break;
}
} else {
debug_log('ERROR: Machine connection not open!');
}
});
socket.on('closePort', function(data) { // Close machine port and dump queue
if (status.comms.connectionStatus > 0) {
debug_log('WARN: Closing Port ' + port.path);
stopPort();
} else {
debug_log('ERROR: Machine connection not open!');
}
});
});
function readFile(filePath) {
if (filePath) {
if (filePath.length > 1) {
var filename = path.parse(filePath)
filename = filename.name + filename.ext
debug_log('readfile: ' + filePath)
fs.readFile(filePath, 'utf8',
function(err, data) {
if (err) {
debug_log(err);
var output = {
'command': '',
'response': "ERROR: File Upload Failed"
}
uploadedgcode = "";
}
if (data) {
if (filePath.endsWith('.obc')) { // OpenBuildsCAM Workspace
uploadedworkspace = data;
const {
shell
} = require('electron')
shell.openExternal('https://cam.openbuilds.com')
} else { // GCODE
var payload = {
gcode: data,
filename: filename
}
io.sockets.emit('gcodeupload', payload);
uploadedgcode = data;
return data
}
}
});
}
}
}
function machineSend(gcode, realtime) {
debug_log("SENDING: " + gcode)
if (port.isOpen) {
if (realtime) {
// realtime commands doesnt count toward the queue, does not generate OK
port.write(gcode);
} else {
if (gcode.match(/T([\d.]+)/i)) {
var tool = parseFloat(RegExp.$1);
status.machine.tool.nexttool.number = tool
status.machine.tool.nexttool.line = gcode
}
var queueLeft = parseInt((gcodeQueue.length - queuePointer))
var queueTotal = parseInt(gcodeQueue.length)
// debug_log("Q: " + queueLeft)
var data = []
data.push(queueLeft);
data.push(queueTotal);
io.sockets.emit("queueCount", data);
// debug_log(gcode)
port.write(gcode);
debug_log("SENT: " + gcode)
}
} else {
debug_log("PORT NOT OPEN")
}
}
function runJob(object) {
// object = {
// isJob: true,
// completedMsg: "",
// data: "",
// }
jobStartTime = false;
var data = object.data
if (object.isJob) {
if (data.length < 20000) {
uploadedgcode = data;
}
jobStartTime = new Date().getTime();
}
if (object.completedMsg) {
jobCompletedMsg = object.completedMsg
}
// debug_log('Run Job (' + data.length + ')');
if (status.comms.connectionStatus > 0) {
if (data) {
data = data.split('\n');
for (var i = 0; i < data.length; i++) {
var line = data[i].replace("%", "").split(';'); // Remove everything after ; = comment
var tosend = line[0].trim();
if (tosend.length > 0) {
addQToEnd(tosend);
}
}
if (i > 0) {
// Start interval for qCount messages to socket clients
queueCounter = setInterval(function() {
status.comms.queue = gcodeQueue.length - queuePointer
if (jogWindow) {
jogWindow.setProgressBar(queuePointer / gcodeQueue.length)
}
}, 500);
send1Q(); // send first line
status.comms.connectionStatus = 3;
}
}
} else {
debug_log('ERROR: Machine connection not open!');
}
}
function stopPort() {
clearInterval(queueCounter);
clearInterval(statusLoop);
if (jogWindow) {
jogWindow.setProgressBar(0);
}
status.comms.interfaces.activePort = false;
status.comms.interfaces.activeBaud = false;
status.comms.connectionStatus = 0;
status.machine.firmware.type = "";
status.machine.firmware.version = ""; // get version
status.machine.firmware.date = "";
status.machine.firmware.buffer = "";
gcodeQueue.length = 0;
sentBuffer.length = 0; // dump bufferSizes
// port.drain(port.close());
if (status.comms.interfaces.type == "usb") {
port.drain(port.close());
} else if (status.comms.interfaces.type == "telnet") {
port.destroy();
}
}
function parseFeedback(data) {
//debug_log(data)
var state = data.substring(1, data.search(/(,|\|)/));
status.comms.runStatus = state
if (state == "Alarm") {
// debug_log("ALARM: " + data)
status.comms.connectionStatus = 5;
switch (status.machine.firmware.type) {
case 'grbl':
//var alarmCode = parseInt(data.split(':')[1]);
debug_log('ALARM: ' + data);
//status.comms.alarm = alarmCode + ' - ' + grblStrings.alarms(alarmCode)
break;
}
status.comms.connectionStatus = 5;
} else if (state == "Hold:0") {
pause();
}
if (status.machine.firmware.type == "grbl") {
// Extract work offset (for Grbl > 1.1 only!)
var startWCO = data.search(/wco:/i) + 4;
var wco;
if (startWCO > 4) {
wco = data.replace(">", "").substr(startWCO).split(/,|\|/, 4);
}
if (Array.isArray(wco)) {
xOffset = parseFloat(wco[0]).toFixed(config.posDecimals);
yOffset = parseFloat(wco[1]).toFixed(config.posDecimals);
zOffset = parseFloat(wco[2]).toFixed(config.posDecimals);
if (status.machine.has4thAxis) {
aOffset = parseFloat(wco[3]).toFixed(config.posDecimals);
status.machine.position.offset.x = parseFloat(xOffset);
status.machine.position.offset.y = parseFloat(yOffset);
status.machine.position.offset.z = parseFloat(zOffset);
status.machine.position.offset.a = parseFloat(aOffset);
} else {
status.machine.position.offset.x = parseFloat(xOffset);
status.machine.position.offset.y = parseFloat(yOffset);
status.machine.position.offset.z = parseFloat(zOffset);
}
}
// Extract wPos (for Grbl > 1.1 only!)
var startWPos = data.search(/wpos:/i) + 5;
var wPos;
if (startWPos > 5) {
var wPosLen = data.substr(startWPos).search(/>|\|/);
wPos = data.substr(startWPos, wPosLen).split(/,/);
}
var startMPos = data.search(/mpos:/i) + 5;
var mPos;
if (startMPos > 5) {
var mPosLen = data.substr(startMPos).search(/>|\|/);
mPos = data.substr(startMPos, mPosLen).split(/,/);
}
// If we got a WPOS
if (Array.isArray(wPos)) {
// debug_log('wpos')
if (xPos !== parseFloat(wPos[0]).toFixed(config.posDecimals)) {
xPos = parseFloat(wPos[0]).toFixed(config.posDecimals);
}
if (yPos !== parseFloat(wPos[1]).toFixed(config.posDecimals)) {
yPos = parseFloat(wPos[1]).toFixed(config.posDecimals);
}
if (zPos !== parseFloat(wPos[2]).toFixed(config.posDecimals)) {
zPos = parseFloat(wPos[2]).toFixed(config.posDecimals);
}
if (wPos.length > 3) {
if (aPos !== parseFloat(wPos[3]).toFixed(config.posDecimals)) {
aPos = parseFloat(wPos[3]).toFixed(config.posDecimals);
status.machine.has4thAxis = true;
}
}
if (status.machine.has4thAxis) {
status.machine.position.work.x = parseFloat(xPos);
status.machine.position.work.y = parseFloat(yPos);
status.machine.position.work.z = parseFloat(zPos);
status.machine.position.work.a = parseFloat(aPos);
} else {
status.machine.position.work.x = parseFloat(xPos);
status.machine.position.work.y = parseFloat(yPos);
status.machine.position.work.z = parseFloat(zPos);
}
// end is WPOS
} else if (Array.isArray(mPos)) {
// debug_log('mpos', mPos)
if (xPos !== parseFloat(mPos[0]).toFixed(config.posDecimals)) {
xPos = parseFloat(mPos[0]).toFixed(config.posDecimals);
}
if (yPos !== parseFloat(mPos[1]).toFixed(config.posDecimals)) {
yPos = parseFloat(mPos[1]).toFixed(config.posDecimals);
}
if (zPos !== parseFloat(mPos[2]).toFixed(config.posDecimals)) {
zPos = parseFloat(mPos[2]).toFixed(config.posDecimals);
}
if (mPos.length > 3) {
if (aPos !== parseFloat(mPos[3]).toFixed(config.posDecimals)) {
aPos = parseFloat(mPos[3]).toFixed(config.posDecimals);
status.machine.has4thAxis = true;
}
}
if (status.machine.has4thAxis) {
status.machine.position.work.x = parseFloat(parseFloat(xPos - status.machine.position.offset.x).toFixed(config.posDecimals));
status.machine.position.work.y = parseFloat(parseFloat(yPos - status.machine.position.offset.y).toFixed(config.posDecimals));
status.machine.position.work.z = parseFloat(parseFloat(zPos - status.machine.position.offset.z).toFixed(config.posDecimals));
status.machine.position.work.a = parseFloat(parseFloat(aPos - status.machine.position.offset.a).toFixed(config.posDecimals));
} else {
status.machine.position.work.x = parseFloat(parseFloat(xPos - status.machine.position.offset.x).toFixed(config.posDecimals));
status.machine.position.work.y = parseFloat(parseFloat(yPos - status.machine.position.offset.y).toFixed(config.posDecimals));
status.machine.position.work.z = parseFloat(parseFloat(zPos - status.machine.position.offset.z).toFixed(config.posDecimals));
}
// end if MPOS
}
}
// Extract override values (for Grbl > v1.1 only!)
var startOv = data.search(/ov:/i) + 3;
if (startOv > 3) {
var ov = data.replace(">", "").substr(startOv).split(/,|\|/, 3);
if (Array.isArray(ov)) {
if (ov[0]) {
status.machine.overrides.feedOverride = parseInt(ov[0]);
}
if (ov[1]) {
status.machine.overrides.rapidOverride = parseInt(ov[1]);
}
if (ov[2]) {
status.machine.overrides.spindleOverride = parseInt(ov[2]);
}
}
}
// Extract realtime Feed and Spindle (for Grbl > v1.1 only!)
var startFS = data.search(/\|FS:/i) + 4;
if (startFS > 4) {
var fs = data.replace(">", "").substr(startFS).split(/,|\|/);
if (Array.isArray(fs)) {
if (fs[0]) {
status.machine.overrides.realFeed = parseInt(fs[0]);
}
if (fs[1]) {
status.machine.overrides.realSpindle = parseInt(fs[1]);
}
}
}
// extras realtime feed (if variable spindle is disabled)
var startF = data.search(/\|F:/i) + 3;
if (startF > 3) {
var f = data.replace(">", "").substr(startF).split(/,|\|/);
console.log(JSON.stringify(f, null, 4))
if (Array.isArray(f)) {
if (f[0]) {
status.machine.overrides.realFeed = parseInt(f[0]);
}
}
}
// Extract Pin Data
var startPin = data.search(/Pn:/i) + 3;
if (startPin > 3) {
var pinsdata = data.replace(">", "").replace("\r", "").substr(startPin).split(/,|\|/, 1);
var pins = pinsdata[0].split('')
status.machine.inputs = pins;
if (!_.isEqual(pins, oldpinslist)) {
if (pins.includes('H') && !pins.includes('D')) {
// pause
pause();
var output = {
'command': '[external from hardware]',
'response': "OpenBuilds CONTROL received a FEEDHOLD notification from Grbl: This could be due to someone pressing the HOLD button (if connected)",
'type': 'info'
}
io.sockets.emit('data', output);
} // end if HOLD
if (pins.includes('D')) {
// pause
pause();
}
if (pins.includes('R')) {
// abort
stop(true);
var output = {
'command': '[external from hardware]',
'response': "OpenBuilds CONTROL received a RESET/ABORT notification from Grbl: This could be due to someone pressing the RESET/ABORT button (if connected)",
'type': 'info'
}
io.sockets.emit('data', output);
} // end if ABORT
if (pins.includes('S')) {
// abort
unpause();
var output = {
'command': '[external from hardware]',
'response': "OpenBuilds CONTROL received a CYCLESTART/RESUME notification from Grbl: This could be due to someone pressing the CYCLESTART/RESUME button (if connected)",
'type': 'info'
}
io.sockets.emit('data', output);
} // end if RESUME/START
}
} else {
status.machine.inputs = [];
}
oldpinslist = pins;
// Extract Buffer Data
var startBuf = data.search(/Bf:/i) + 3;
if (startBuf > 3) {
var buffer = data.replace(">", "").replace("\r", "").substr(startBuf).split(/,|\|/, 2);
// debug_log("BUF: " + JSON.stringify(buffer, null, 2));
status.machine.firmware.buffer = buffer;
} else {
status.machine.firmware.buffer = [];
}
// end statusreport
}
function gotModals(data) {
// as per https://github.com/gnea/grbl/wiki/Grbl-v1.1-Commands#g---view-gcode-parser-state
// The shown g-code are the current modal states of Grbl's g-code parser.
// This may not correlate to what is executing since there are usually
// several motions queued in the planner buffer.
// [GC:G0 G54 G17 G21 G90 G94 M5 M9 T0 F0.0 S0]
// defaults
data = data.split(/:|\[|\]/)[2].split(" ")
for (i = 0; i < data.length; i++) {
// if (data[i] == "G0") {
// status.machine.modals.motionmode = "G0";
// }
// if (data[i] == "G1") {
// status.machine.modals.motionmode = "G1";
// }
// if (data[i] == "G2") {
// status.machine.modals.motionmode = "G2";
// }
// if (data[i] == "G3") {
// status.machine.modals.motionmode = "G3";
// }
// if (data[i] == "G38.2") {
// status.machine.modals.motionmode = "G38.2";
// }
// if (data[i] == "G38.3") {
// status.machine.modals.motionmode = "G38.3";
// }
// if (data[i] == "G38.4") {
// status.machine.modals.motionmode = "G38.4";
// }
// if (data[i] == "G38.5") {
// status.machine.modals.motionmode = "G38.5";
// }
// if (data[i] == "G80") {
// status.machine.modals.motionmode = "G80";
// }
// status.machine.modals.coordinatesys = "G54"; // G54, G55, G56, G57, G58, G59
if (data[i] == "G54") {
status.machine.modals.coordinatesys = "G54";
}
if (data[i] == "G55") {
status.machine.modals.coordinatesys = "G55";
}
if (data[i] == "G56") {
status.machine.modals.coordinatesys = "G56";
}
if (data[i] == "G57") {
status.machine.modals.coordinatesys = "G57";
}
if (data[i] == "G58") {
status.machine.modals.coordinatesys = "G58";
}
if (data[i] == "G59") {
status.machine.modals.coordinatesys = "G59";
}
// status.machine.modals.plane = "G17"; // G17, G18, G19
if (data[i] == "G17") {
status.machine.modals.plane = "G17";
}
if (data[i] == "G18") {
status.machine.modals.plane = "G18";
}
if (data[i] == "G19") {
status.machine.modals.plane = "G19";
}
// status.machine.modals.distancemode = "G90"; // G90, G91
if (data[i] == "G90") {
status.machine.modals.distancemode = "G90";
}
if (data[i] == "G91") {
status.machine.modals.distancemode = "G91";
}
// status.machine.modals.arcdistmode = "G91.1"; // G91.1
if (data[i] == "G91.1") {
status.machine.modals.arcdistmode = "G91.1";
}
// status.machine.modals.feedratemode = "G94"; // G93, G94
if (data[i] == "G93") {
status.machine.modals.feedratemode = "G93";
}
if (data[i] == "G94") {
status.machine.modals.feedratemode = "G94";
}
// status.machine.modals.unitsmode = "G21"; // G20, G21
if (data[i] == "G20") {
status.machine.modals.unitsmode = "G20";
}
if (data[i] == "G21") {
status.machine.modals.unitsmode = "G21";
}
// status.machine.modals.radiuscomp = "G40"; // G40
if (data[i] == "G40") {
status.machine.modals.radiuscomp = "G40";
}
// status.machine.modals.tlomode = "G49"; // G43.1, G49
if (data[i] == "G49") {
status.machine.modals.tlomode = "G49";
}
if (data[i] == "G43.1") {
status.machine.modals.tlomode = "G43.1";
}
// status.machine.modals.programmode = "M0"; // M0, M1, M2, M30
// if (data[i] == "M0") {
// status.machine.modals.programmode = "M0";
// }
// if (data[i] == "M1") {
// status.machine.modals.programmode = "M1";
// }
// if (data[i] == "M2") {
// status.machine.modals.programmode = "M2";
// }
// if (data[i] == "M30") {
// status.machine.modals.programmode = "M30";
// }
// status.machine.modals.spindlestate = "M5"; // M3, M4, M5
if (data[i] == "M3") {
status.machine.modals.spindlestate = "M3";
}
if (data[i] == "M4") {
status.machine.modals.spindlestate = "M4";
}
if (data[i] == "M5") {
status.machine.modals.spindlestate = "M5";
}
// status.machine.modals.coolantstate = "M9"; // M7, M8, M9
if (data[i] == "M7") {
status.machine.modals.coolantstate = "M7";
}
if (data[i] == "M8") {
status.machine.modals.coolantstate = "M8";
}
if (data[i] == "M9") {
status.machine.modals.coolantstate = "M9";
}
// // status.machine.modals.tool = "0",
// if (data[i].indexOf("T") === 0) {
// status.machine.modals.tool = parseFloat(data[i].substr(1))
// }
//
// // status.machine.modals.spindle = "0"
// if (data[i].indexOf("S") === 0) {
// status.machine.modals.spindle = parseFloat(data[i].substr(1))
// }
//
// // status.machine.modals.feedrate = "0"
// if (data[i].indexOf("F") === 0) {
// status.machine.modals.feedrate = parseFloat(data[i].substr(1))
// }
}
} // end gotModals
function laserTest(data) {
if (status.comms.connectionStatus > 0) {
data = data.split(',');
var power = parseFloat(data[0]);
var duration = parseInt(data[1]);
var maxS = parseFloat(data[2]);
if (power > 0) {
if (!laserTestOn) {
// laserTest is off
// debug_log('laserTest: ' + 'Power ' + power + ', Duration ' + duration + ', maxS ' + maxS);
if (duration >= 0) {
switch (status.machine.firmware.type) {
case 'grbl':
addQToEnd('G1F1');
addQToEnd('M3S' + parseInt(power * maxS / 100));
laserTestOn = true;
io.sockets.emit('laserTest', power);
if (duration > 0) {
addQToEnd('G4 P' + duration / 1000);
addQToEnd('M5S0');
laserTestOn = false;
}
send1Q();
break;
}
}
} else {
// debug_log('laserTest: ' + 'Power off');
switch (status.machine.firmware.type) {
case 'grbl':
addQToEnd('M5S0');
send1Q();
break;
}
laserTestOn = false;
io.sockets.emit('laserTest', 0);
}
}
} else {
debug_log('ERROR: Machine connection not open!');
}
}
// queue
function BufferSpace(firmware) {
var total = 0;
var len = sentBuffer.length;
for (var i = 0; i < len; i++) {
total += sentBuffer[i].length;
}
if (firmware == "grbl") {
if (status.machine.firmware.rxBufferSize > 0) {
return (status.machine.firmware.rxBufferSize - 1) - total;
} else {
if (status.machine.firmware.platform == "grblHAL") {
return GRBLHAL_RX_BUFFER_SIZE - total;
} else {
return GRBL_RX_BUFFER_SIZE - total;
}
}
}
}
function send1Q() {
// console.time('send1Q');
var gcode;
var gcodeLen = 0;
var spaceLeft = 0;
if (status.comms.connectionStatus > 0) {
switch (status.machine.firmware.type) {
case 'grbl':
if ((gcodeQueue.length - queuePointer) > 0 && !status.comms.blocked && !status.comms.paused) {
spaceLeft = BufferSpace('grbl');
// Do we have enough space in the buffer?
if (gcodeQueue[queuePointer].length < spaceLeft) {
gcode = gcodeQueue[queuePointer];
queuePointer++;
sentBuffer.push(gcode);
machineSend(gcode + '\n', false);
// debug_log('Sent: ' + gcode + ' Q: ' + (gcodeQueue.length - queuePointer) + ' Bspace: ' + (spaceLeft - gcode.length - 1));
} else {
status.comms.blocked = true;
}
}
break;
}
if (queuePointer >= gcodeQueue.length) {
if (gcodeQueue.length > 1) {
var data = {
completed: true,
failed: false,
jobCompletedMsg: jobCompletedMsg,
jobStartTime: jobStartTime,
jobEndTime: new Date().getTime()
}
io.sockets.emit('jobComplete', data);
} else {
var data = {
completed: true,
failed: true,
jobCompletedMsg: jobCompletedMsg,
jobStartTime: jobStartTime,
jobEndTime: new Date().getTime()
}
io.sockets.emit('jobComplete', data);
}
status.comms.connectionStatus = 2; // finished
clearInterval(queueCounter);
if (jogWindow) {
jogWindow.setProgressBar(0);
}
gcodeQueue.length = 0; // Dump the Queye
queuePointer = 0;
status.comms.connectionStatus = 2; // finished
jobCompletedMsg = ""
jobStartTime = false;
}
} else {
debug_log('Not Connected')
}
// console.timeEnd('send1Q');
}
var modalCommands = ['G54', 'G55', 'G56', 'G57', 'G58', 'G59', 'G17', 'G18', 'G19', 'G90', 'G91', 'G91.1', 'G93', 'G94', 'G20', 'G21', 'G40', 'G43.1', 'G49', 'M0', 'M1', 'M2', 'M30', 'M3', 'M4', 'M5', 'M7', 'M8', 'M9']
var modalCommandsRegExp = new RegExp(modalCommands.join("|"));
function addQToEnd(gcode) {
// debug_log('added ' + gcode)
gcodeQueue.push(gcode);
// if (gcode.indexOf("G54") != -1 || gcode.indexOf("G55") != -1 || gcode.indexOf("G56") != -1 || gcode.indexOf("G57") != -1 || gcode.indexOf("G58") != -1 || gcode.indexOf("G59") != -1) {
// gcodeQueue.push("$G");
// }
var testGcode = gcode.toUpperCase()
if (testGcode.indexOf("$H") != -1) {
status.machine.modals.homedRecently = true;
}
if (testGcode == "$CD") {
fluidncConfig = ""; // empty string
}
if (!gcode.startsWith("$J=") && modalCommandsRegExp.test(testGcode)) {
gcodeQueue.push("$G");
}
if (gcode.match(/T([\d.]+)/i)) {
gcodeQueue.push("$G");
}
}
function addQToStart(gcode) {
gcodeQueue.unshift(gcode);
}
function addQRealtime(gcode) {
// realtime command skip the send1Q as it doesnt respond with an ok
machineSend(gcode, true);
}
function showJogWindow() {
if (jogWindow === null) {
createJogWindow();
}
jogWindow.show()
jogWindow.setAlwaysOnTop(true);
jogWindow.focus();
jogWindow.setAlwaysOnTop(false);
}
// Electron
function isElectron() {
if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
return true;
}
if (typeof process !== 'undefined' && process.versions && !!process.versions.electron) {
return true;
}
return false;
}
if (isElectron()) {
const gotTheLock = electronApp.requestSingleInstanceLock()
var lauchGUI = true;
if (!gotTheLock) {
debug_log("Already running! Check the System Tray")
electronApp.exit(0);
electronApp.quit();
} else {
electronApp.on('second-instance', (event, commandLine, workingDirectory) => {
//Someone tried to run a second instance, we should focus our window.
// debug_log('SingleInstance')
function checkFileType(fileName) {
var fileNameLC = fileName.toLowerCase();
if (fileNameLC.endsWith('.obc') || fileName.endsWith('.gcode') || fileName.endsWith('.gc') || fileName.endsWith('.tap') || fileName.endsWith('.nc') || fileName.endsWith('.cnc')) {
return fileName;
}
}
debug_log(commandLine)
lauchGUI = true;
var openFilePath = commandLine.find(checkFileType);
if (openFilePath !== "") {
readFile(openFilePath);
if (openFilePath !== undefined) {
if (openFilePath.endsWith('.obc')) {
lauchGUI = false;
} else {
lauchGUI = true;
}
}
}
if (lauchGUI) {
showJogWindow()
}
})
// Create myWindow, load the rest of the app, etc...
electronApp.on('ready', () => {
if (process.platform == 'win32') {
// Don't show window - sit in Tray
} else {
showJogWindow() // Macos and Linux - launch GUI
}
})
}
if (electronApp) {
// Module to create native browser window.
function createApp() {
createTrayIcon();
if (process.platform == 'darwin') {
debug_log("Creating MacOS Menu");
createMenu();
status.driver.operatingsystem = 'macos';
}
if (process.platform == 'win32' && process.argv.length >= 2) {
var openFilePath = process.argv[1];
if (openFilePath !== "") {
debug_log("path" + openFilePath);
readFile(openFilePath);
}
status.driver.operatingsystem = 'windows';
}
if (process.platform == 'darwin' || uploadedgcode.length > 1) {
showJogWindow()
}
}
function createMenu() {
var template = [{
label: "Application",
submenu: [{
label: "Quit",
accelerator: "Command+Q",
click: function() {
if (appIcon) {
appIcon.destroy();
}
electronApp.exit(0);
}
}]
}, {
label: "Edit",
submenu: [{
label: "Cut",
accelerator: "CmdOrCtrl+X",
selector: "cut:"
},
{
label: "Copy",
accelerator: "CmdOrCtrl+C",
selector: "copy:"
},
{
label: "Paste",
accelerator: "CmdOrCtrl+V",
selector: "paste:"
},
{
label: "Select All",
accelerator: "CmdOrCtrl+A",
selector: "selectAll:"
}
]
}, {
label: "View",
submenu: [{
label: "Reload",
accelerator: "F5",
click: (item, focusedWindow) => {
if (focusedWindow) {
// on reload, start fresh and close any old
// open secondary windows
if (focusedWindow.id === 1) {
BrowserWindow.getAllWindows().forEach(win => {
if (win.id > 1) win.close();
});
}
focusedWindow.reload();
}
}
},
{
label: "Toggle Dev Tools",
accelerator: "F12",
click: () => {
jogWindow.webContents.toggleDevTools();
}
}
]
}];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}
function createTrayIcon() {
if (process.platform !== 'darwin') {
appIcon = new Tray(
nativeImage.createFromPath(iconPath)
)
const contextMenu = Menu.buildFromTemplate([{
label: 'Open User Interface (GUI)',
click() {
// debug_log("Clicked Systray")
showJogWindow()
}
}, {
label: 'Quit OpenBuilds CONTROL (Disables all integration until started again)',
click() {
if (appIcon) {
appIcon.destroy();
}
electronApp.exit(0);
}
}])
if (appIcon) {
appIcon.on('click', function() {
// debug_log("Clicked Systray")
showJogWindow()
})
}
if (appIcon) {
appIcon.on('balloon-click', function() {
// debug_log("Clicked Systray")
showJogWindow()
})
}
// Call this again for Linux because we modified the context menu
if (appIcon) {
appIcon.setContextMenu(contextMenu)
}
if (appIcon) {
appIcon.displayBalloon({
icon: nativeImage.createFromPath(iconPath),
title: "OpenBuilds CONTROL Started",
// content: "OpenBuilds CONTROL has started successfully: Active on " + ip.address() + ":" + config.webPort
content: "OpenBuilds CONTROL has started successfully"
})
}
} else {
const dockMenu = Menu.buildFromTemplate([{
label: 'Quit OpenBuilds CONTROL (Disables all integration until started again)',
click() {
// appIcon.destroy();
electronApp.exit(0);
}
}])
electronApp.dock.setMenu(dockMenu)
};
}
function createJogWindow() {
// Create the browser window.
jogWindow = new BrowserWindow({
// 1366 * 768 == minimum to cater for
width: 1000,
height: 850,
fullscreen: false,
center: true,
resizable: true,
maximizable: true,
title: "OpenBuilds CONTROL ",
frame: false,
autoHideMenuBar: true,
//icon: '/app/favicon.png',
icon: nativeImage.createFromPath(
path.join(__dirname, "/app/favicon.png")
),
webgl: true,
experimentalFeatures: true,
experimentalCanvasFeatures: true,
offscreen: true,
backgroundColor: "#fff",
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
jogWindow.setOverlayIcon(nativeImage.createFromPath(iconPath), 'Icon');
var ipaddr = ip.address();
// jogWindow.loadURL(`//` + ipaddr + `:3000/`)
jogWindow.loadURL(`http://localhost:${config.webPort}/`);
//jogWindow.webContents.openDevTools()
jogWindow.on('close', function(event) {
if (!forceQuit) {
jogWindow.hide();
return false;
}
});
// Emitted when the window is closed.
jogWindow.on('closed', function() {
// 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.
jogWindow = null;
});
jogWindow.once('ready-to-show', () => {
showJogWindow()
})
}
// 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.
electronApp.on('ready', createApp);
electronApp.on('before-quit', function() {
forceQuit = true;
})
electronApp.on('will-quit', function(event) {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
// We don't take that route, we close it completely
if (appIcon) {
appIcon.destroy();
}
electronApp.exit(0);
});
// Quit when all windows are closed.
electronApp.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (appIcon) {
appIcon.destroy();
}
electronApp.exit(0);
});
electronApp.on('activate', function() {
// On OS X 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 (mainWindow === null) {
createApp();
}
});
// Autostart on Login
if (process.platform == 'win32') {
electronApp.setLoginItemSettings({
openAtLogin: true,
args: []
})
}
}
} else { // if its not running under Electron, lets get Chrome up.
var isPi = require('detect-rpi');
if (isPi()) {
DEBUG = true;
debug_log('Running on Raspberry Pi!');
status.driver.operatingsystem = 'rpi'
startChrome();
} else {
debug_log("Running under NodeJS...");
}
}
function stop(data) {
//data = { stop: false, jog: false, abort: true}
if (status.comms.connectionStatus > 0) {
status.comms.paused = true;
debug_log('STOP');
switch (status.machine.firmware.type) {
case 'grbl':
if (data.jog) {
addQRealtime(String.fromCharCode(0x85)); // canceljog
debug_log('Sent: 0x85 Jog Cancel');
debug_log(queuePointer, gcodeQueue)
}
if (!data.abort && !data.jog) { // pause motion first.
addQRealtime('!'); // hold
debug_log('Sent: !');
}
if (status.machine.firmware.version === '1.1d') {
addQRealtime(String.fromCharCode(0x9E)); // Stop Spindle/Laser
debug_log('Sent: Code(0x9E)');
}
debug_log('Cleaning Queue');
if (!data.jog) {
setTimeout(function() {
addQRealtime(String.fromCharCode(0x18)); // ctrl-x
debug_log('Sent: Code(0x18)');
}, 200);
}
status.comms.connectionStatus = 2;
break;
}
clearInterval(queueCounter);
if (jogWindow) {
jogWindow.setProgressBar(0);
}
status.comms.queue = 0
queuePointer = 0;
gcodeQueue.length = 0; // Dump the queue
sentBuffer.length = 0; // Dump the queue
// sentBuffer.length = 0; // Dump bufferSizes
laserTestOn = false;
status.comms.blocked = false;
status.comms.paused = false;
status.comms.runStatus = 'Stopped';
status.comms.alarm = "";
} else {
debug_log('ERROR: Machine connection not open!');
}
}
function pause() {
if (status.comms.connectionStatus == 3) {
status.comms.paused = true;
debug_log('PAUSE');
switch (status.machine.firmware.type) {
case 'grbl':
addQRealtime('!'); // Send hold command
debug_log('Sent: !');
if (status.machine.firmware.version === '1.1d') {
addQRealtime(String.fromCharCode(0x9E)); // Stop Spindle/Laser
debug_log('Sent: Code(0x9E)');
}
break;
}
status.comms.runStatus = 'Paused';
status.comms.connectionStatus = 4;
} else {
debug_log('ERROR: Machine connection not open!');
}
}
function unpause() {
if (status.comms.connectionStatus > 0) {
debug_log('UNPAUSE');
switch (status.machine.firmware.type) {
case 'grbl':
addQRealtime('~'); // Send resume command
debug_log('Sent: ~');
break;
}
status.comms.paused = false;
status.comms.blocked = false;
setTimeout(function() {
send1Q(); // restart queue
}, 200);
status.comms.runStatus = 'Resuming';
status.comms.connectionStatus = 3;
} else {
debug_log('ERROR: Machine connection not open!');
}
}
function isJson(item) {
item = typeof item !== "string" ?
JSON.stringify(item) :
item;
try {
item = JSON.parse(item);
} catch (e) {
return false;
}
if (typeof item === "object" && item !== null) {
return true;
}
return false;
}
function startChrome() {
if (status.driver.operatingsystem == 'rpi') {
const {
spawn
} = require('child_process');
const chrome = spawn('chromium-browser', [`-app=http://127.0.0.1:${config.webPort}`]);
chrome.on('close', (code) => {
debug_log(`Chromium process exited with code ${code}`);
process.exit(0);
});
} else {
debug_log('Not a Raspberry Pi. Please use Electron Instead');
}
}
// Interface Programming
// grab latest firmware.bin for Interface on startup
var file = fs.createWriteStream(path.join(uploadsDir, "firmware.bin"));
https.get("https://raw.githubusercontent.com/OpenBuilds/firmware/main/interface/firmware.bin", function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(function() {
const options = {
hostname: 'raw.githubusercontent.com',
port: 443,
path: '/OpenBuilds/firmware/main/interface/version.txt',
method: 'GET'
}
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
status.interface.firmware.availVersion = parseFloat(d.toString())
var output = {
'command': 'interface firmware update tool',
'response': "Downloaded firmware.bin v" + status.interface.firmware.availVersion,
'type': 'info'
}
io.sockets.emit('data', output);
debug_log(JSON.stringify(output));
})
})
req.on('error', error => {
var output = {
'command': 'interface firmware update tool',
'response': "Unable to download latest firmware.bin",
'type': 'error'
}
io.sockets.emit('data', output);
})
req.end()
});
});
})
var firmwareImagePath = path.join(uploadsDir, './firmware.bin');
var spawn = require('child_process').spawn;
const multer = require('multer');
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, uploadsDir);
},
// By default, multer removes file extensions so let's add them back
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + new Date().toJSON().replace(new RegExp(':', 'g'), '.') + path.extname(file.originalname));
}
});
function flashInterface(data) {
status.comms.connectionStatus = 6;
var port = data.port;
var file = data.file;
var board = data.board
console.log("Flashing Interface on " + port + " with file: " + file)
// var data = {
// 'port': port,
// 'string': debugString
// }
// io.sockets.emit("progStatus", data);
//
//for (let i = 0; i < ports.length; i++) {
var data = {
'port': port,
'string': "[Starting...]"
}
io.sockets.emit("progStatus", data);
var esptool_opts = [
'--chip', 'esp32',
'--port', port,
'--baud', '921600',
'--before', 'default_reset',
'--after', 'hard_reset',
'write_flash',
'-z',
'--flash_mode', 'dio',
'--flash_freq', '80m',
'--flash_size', 'detect',
'0xe000', path.join(__dirname, "./boot_app0.bin").replace('app.asar', 'app.asar.unpacked'),
'0x1000', path.join(__dirname, "./bootloader_qio_80m.bin").replace('app.asar', 'app.asar.unpacked'),
'0x10000', path.resolve(firmwareImagePath).replace('app.asar', 'app.asar.unpacked'),
'0x8000', path.join(__dirname, "./firmware.partitions.bin").replace('app.asar', 'app.asar.unpacked')
];
if (process.platform == 'linux') {
//path.join(__dirname, "..", "lib", "resources", "vad.onnx"),
fs.chmodSync(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), 0o755);
var child = spawn(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), esptool_opts);
} else if (process.platform == 'win32') {
var child = spawn(path.join(__dirname, "./esptool.exe").replace('app.asar', 'app.asar.unpacked'), esptool_opts);
} else if (process.platform == 'darwin') {
fs.chmodSync(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), 0o755);
var child = spawn(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), esptool_opts);
}
child.stdout.on('data', function(data) {
var debugString = data.toString();
console.log(debugString)
var data = {
'port': port,
'string': debugString
}
io.sockets.emit("progStatus", data);
status.comms.connectionStatus = 6;
});
child.stderr.on('data', function(data) {
var debugString = data.toString();
console.log(debugString)
var data = {
'port': port,
'string': debugString
}
io.sockets.emit("progStatus", data);
status.comms.connectionStatus = 6;
});
child.on('close', (code) => {
var data = {
'port': port,
'string': `[exit:` + code + `]`,
'code': code
}
io.sockets.emit("progStatus", data);
status.comms.connectionStatus = 0;
});
}
// end Interface Programming
function flashGrblHal(data) {
console.log(JSON.stringify(data))
status.comms.connectionStatus = 6;
var port = data.port;
var file = data.file;
var customImg = data.customImg
var erase = data.erase
if (customImg == true) {
var firmwarePath = firmwareImagePath
} else {
var firmwarePath = path.join(__dirname, file)
}
console.log("Flashing BlackBoxX32 on " + port + " with file: " + path.resolve(firmwarePath).replace('app.asar', 'app.asar.unpacked'))
var data = {
'port': port,
'string': "[Starting...]"
}
io.sockets.emit("progStatus", data);
var esptool_opts = [
'--port', port,
'--baud', '460800',
'--before', 'default_reset',
'--after', 'hard_reset',
'--chip', 'esp32',
'write_flash',
'--flash_mode', 'dio',
'--flash_size', 'detect',
'--flash_freq', '40m',
'0x1000', path.join(__dirname, "./grblhal-bootloader.bin").replace('app.asar', 'app.asar.unpacked'),
'0x8000', path.join(__dirname, "./grblhal-partition-table.bin").replace('app.asar', 'app.asar.unpacked'),
'0x10000', path.resolve(firmwarePath).replace('app.asar', 'app.asar.unpacked')
];
if (erase == true) {
esptool_opts.push('--erase-all');
}
if (process.platform == 'linux') {
fs.chmodSync(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), 0o755);
var child = spawn(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), esptool_opts);
} else if (process.platform == 'win32') {
var child = spawn(path.join(__dirname, "./esptool.exe").replace('app.asar', 'app.asar.unpacked'), esptool_opts);
} else if (process.platform == 'darwin') {
console.log("Running on MacOS")
fs.chmodSync(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), 0o755);
var child = spawn(path.join(__dirname, "./esptool.py").replace('app.asar', 'app.asar.unpacked'), esptool_opts);
}
child.stdout.on('data', function(data) {
var debugString = data.toString();
console.log(debugString)
var data = {
'port': port,
'string': debugString
}
io.sockets.emit("progStatus", data);
status.comms.connectionStatus = 6;
});
child.stderr.on('data', function(data) {
var debugString = data.toString();
console.log(debugString)
var data = {
'port': port,
'string': debugString
}
io.sockets.emit("progStatus", data);
status.comms.connectionStatus = 6;
});
child.on('close', (code) => {
var data = {
'port': port,
'string': `[exit:` + code + `]`,
'code': code
}
io.sockets.emit("progStatus", data);
status.comms.connectionStatus = 0;
});
}
// end BlackBoxX32 Programming
// LAN Scanner for BlackBox X32, Interface, SwitchBlox etc //
function scanForTelnetDevices(range) {
//var localNetwork = ip.address().split('.');
//var network = localNetwork[0] + '.' + localNetwork[1] + '.' + localNetwork[2];
//var range = network + ".1-" + network + ".254"
var networkDevices = []
oldiplist = status.comms.interfaces.networkDevices;
const telnetScanOptions = {
target: range,
port: '23',
status: 'TROU', // Timeout, Refused, Open, Unreachable
banner: true
};
var output = {
'command': 'network',
'response': "Starting network scan for: " + telnetScanOptions.target,
'type': 'success'
}
io.sockets.emit('data', output);
new Evilscan(telnetScanOptions, (err, scan) => {
if (err) {
var output = {
'command': 'network',
'response': "Network Scan error: " + err,
'type': 'success'
}
io.sockets.emit('data', output);
//console.log(err);
return;
}
scan.on('result', data => {
// fired when item is matching options
//console.log(data);
if (data.status == "open") {
var type = false;
if (data.banner.indexOf("GrblHAL") != -1) {
type = "grblHAL"
} else if (data.banner.indexOf("Grbl") != -1) {
type = "grbl"
}
networkDevices.push({
ip: data.ip,
type: type,
banner: data.banner
})
}
});
scan.on('error', err => {
//throw new Error(data.toString());
});
scan.on('done', () => {
// finished !
networkDevices.sort((a, b) => {
return a.ip.split('.')[3] - b.ip.split('.')[3];
});
status.comms.interfaces.networkDevices = networkDevices;
if (!_.isEqual(status.comms.interfaces.networkDevices, oldiplist)) {
var newTelnetPorts = _.differenceWith(status.comms.interfaces.networkDevices, oldiplist, _.isEqual)
if (newTelnetPorts.length > 0) {
debug_log("Detected new device: " + newTelnetPorts[0].ip);
}
var removedTelnetPorts = _.differenceWith(oldiplist, status.comms.interfaces.networkDevices, _.isEqual)
if (removedTelnetPorts.length > 0) {
debug_log("No longer detecting device: " + removedTelnetPorts[0].ip);
}
}
oldiplist = status.comms.interfaces.networkDevices;
if (status.comms.interfaces.networkDevices.length > 0) {
var output = {
'command': 'network',
'response': "Network Scan completed. Found " + status.comms.interfaces.networkDevices.length + " devices. Network addresses added to the Port selection dropdown.",
'type': 'success'
}
} else {
var output = {
'command': 'network',
'response': "Network Scan completed. Found " + status.comms.interfaces.networkDevices.length + " devices",
'type': 'error'
}
}
io.sockets.emit('data', output);
});
scan.run();
});
}
// end LAN Scanner
process.on('exit', () => debug_log('exit'))