/*! Reprap Ormerod Web Control | by Matt Burnett . | open license */ var ver = 1.03; //App version var polling = false; var printing = false; var paused = false; var chart,chart2,ormerodIP,layerCount,currentLayer,objHeight,objTotalFilament,printStartTime,gFilename,ubuff,timerStart,storage,layerHeight,lastUpdatedTime; var startingFilamentPos = [], currentFilamentPos = [], objUsedFilament = []; var sFactor = 100, e1Factor = 100, e2Factor = 100; var sSliderActive = false, e1SliderActive = false, e2SliderActive = false; var maxUploadBuffer = 2000; var messageSeqId = 0; var currentTool = 0; //Temp/Layer Chart settings var maxDataPoints = 200; var chartData = [[], [], []]; var maxLayerBars = 100; var layerData = []; var filamentData = []; var bedColour = "#454BFF"; //blue var head1Colour = "#FC2D2D"; //red var head2Colour = "#00A000"; //green var gFileData = ""; var gFileIndex = 0; var expansionFactor = 1.33; var macroGs = ['setbed.g']; var chevLeft = ""; var chevRight = ""; var gcodeDir = "gcodes/"; var webDir = "www/"; var sysDir = "sys/"; jQuery.extend({ askElle: function(reqType, code) { var result; var query = ""; switch(reqType) { case 'upload_data': query = "?data="+code; // 'code' has already been URI encoded break; case 'gcode': query = "?gcode="+encodeURIComponent(code); break; case 'fileinfo': if (code == null) break; case 'upload_begin': case 'delete': query = "?name="+encodeURIComponent(code); break; case 'upload_end': query = "?size="+code; break; } var url = '//' + ormerodIP + '/rr_'+reqType+query; $.ajax(url, {async:false,dataType:"json",success:function(data){result = data;}}); return result; } }); $(document).ready(function() { storage=$.localStorage; getCookies(); loadSettings(); moveVals(['X','Y','Z']); ormerodIP = location.host; $('span#hostLocation').text(ormerodIP); if ($.support.fileDrop) { fileDrop(); } else { alert('Your browser does not support file drag-n-drop :( \nYou have to Click and select a file instead'); } //fill chart with dummy data for (var i = 0; i < maxDataPoints; i++) { chartData[0].push([i, 0]); chartData[1].push([i, 10]); chartData[2].push([i, 20]); } //chart line colours $('#bedTxt').css("color", bedColour); $('#head1Click').css("color", head1Colour); $('#head2Click').css("color", head2Colour); chart = $.plot("#tempchart", chartData, { series: {shadowSize: 0}, colors: [bedColour, head1Colour, head2Colour], yaxis: {min: -20, max: 280}, xaxis: {show: false}, grid: { borderWidth: 0 } }); chart2 = $.plot("#layerChart", [{ data: layerData, bars: {show: true} }], { series: {shadowSize: 0}, xaxis: {minTickSize: 1, tickDecimals: 0, panRange: [0, null], zoomRange: [20, 50]}, yaxis: {minTickSize: 1, min: 0, tickDecimals: 0, panRange: false}, grid: {borderWidth: 0}, pan: {interactive: true} }); message('success', 'Page Load Complete'); $('button#connect').removeClass('disabled'); var htmVer = getHTMLver(); $('p#htmVer').text(htmVer); $('p#jsVer').text(ver); if (htmVer < ver) { //pop message modalMessage("Update! v"+ver+" is Available", "The version of reprap.htm on your Duet SD card is "+getHTMLver()+", the latest version is "+ver +", to ensure compatibility and with the latest javascript code, new features, and correct functionality it is highly recommended that you upgrade. The newest reprap.htm can be found at https://github.com/dc42/OrmerodWebControl", true); } }); $('#connect').on('click', function() { if (polling) { polling = false; updatePage(); } else { polling = true; $.askElle("connect"); updatePage(); listGFiles(); // Get the machine name var resp = $.askElle("name"); if (resp !== undefined && resp.hasOwnProperty('myName') && resp.myName.length != 0) { $('span#machineName').text(resp.myName); } // See if the machine is printing a file, if so get the details and switch to the print Status tab resp = $.askElle("fileinfo", null); if (resp != undefined && resp.hasOwnProperty('fileName')) { var temp = updateFileTableInfo(resp, 'slic3rMain'); layerHeight = temp[0]; var height = temp[1], filament = temp[2]; $('span#gFileDisplay').html('Printing ' + resp.fileName + ' from Duet SD card'); printStartTime = (new Date()).getTime() - resp.printDuration; $('span#elapsed').text(resp.printDuration.toHHMMSS()); resetLayerData(height, filament, true); $('progress#printProgressBar').show(); $('#tabs a:eq(1)').tab('show'); } // Get the firmware version. The response will be captured automatically from the next status response. $.askElle("gcode", "M115"); poll(); } }); //temp controls $('div#bedActiveTemperature').on('click', 'a#bedActiveTempLink', function() { $('input#bedActiveTempInput').val($(this).text()); $.askElle('gcode', "M140 S" + $(this).text()); }); $('div#head1ActiveTemperature').on('click', 'a#head1ActiveTempLink', function() { $('input#head1ActiveTempInput').val($(this).text()); $.askElle('gcode', "G10 P1 S" + $(this).text()); }); $('div#head1StandbyTemperature').on('click', 'a#head1StandbyTempLink', function() { $('input#head1StandbyTempInput').val($(this).text()); $.askElle('gcode', "G10 P1 R" + $(this).text()); }); $('div#head2ActiveTemperature').on('click', 'a#head2ActiveTempLink', function() { $('input#head2ActiveTempInput').val($(this).text()); $.askElle('gcode', "G10 P2 S" + $(this).text()); }); $('div#head2StandbyTemperature').on('click', 'a#head2StandbyTempLink', function() { $('input#head2StandbyTempInput').val($(this).text()); $.askElle('gcode', "G10 P2 R" + $(this).text()); }); $('input#bedActiveTempInput').keydown(function(event) { if (event.which === 13) { event.preventDefault(); $.askElle('gcode', "M140 S" + $(this).val()); $('input#bedActiveTempInput').blur(); } }); $('input#head1ActiveTempInput').keydown(function(event) { if (event.which === 13) { event.preventDefault(); $.askElle('gcode', "G10 P1 S" + $(this).val()); $('input#head1ActiveTempInput').blur(); } }); $('input#head1StandbyTempInput').keydown(function(event) { if (event.which === 13) { event.preventDefault(); $.askElle('gcode', "G10 P1 R" + $(this).val()); $('input#head1StandbyTempInput').blur() } }); $('input#head2ActiveTempInput').keydown(function(event) { if (event.which === 13) { event.preventDefault(); $.askElle('gcode', "G10 P2 S" + $(this).val()); $('input#head2ActiveTempInput').blur(); } }); $('input#head2StandbyTempInput').keydown(function(event) { if (event.which === 13) { event.preventDefault(); $.askElle('gcode', "G10 P2 R" + $(this).val()); $('input#head2StandbyTempInput').blur(); } }); $('div#bedActiveTemperature ul').on('click', 'a#addBedActiveTemp', function() { addTemp($('input#bedActiveTempInput').val(), 'bed'); }); $('div#head1ActiveTemperature ul').on('click', 'a#addHead1ActiveTemp', function() { addTemp($('input#head1ActiveTempInput').val(), 'active'); }); $('div#head1StandbyTemperature ul').on('click', 'a#addHead1StandbyTemp', function() { addTemp($('input#head1StandbyTempInput').val(), 'standby'); }); $('div#head2ActiveTemperature ul').on('click', 'a#addHead2ActiveTemp', function() { addTemp($('input#head2ActiveTempInput').val(), 'active'); }); $('div#head2StandbyTemperature ul').on('click', 'a#addHead2StandbyTemp', function() { addTemp($('input#head2StandbyTempInput').val(), 'standby'); }); $('a#head1Click').on('click', function() { $.askElle('gcode', (currentTool == 1) ? "T0" : "T1"); }); $('a#head2Click').on('click', function() { $.askElle('gcode', (currentTool == 2) ? "T0" : "T2"); }); //feed controls $('div#feed button#feed').on('click', function() { var amount = $(this).val(); var dir = ""; if ($('input[name="feeddir"]:checked').attr('id') == "reverse") { dir = "-"; } var feedRate = " F" + $('input[name="speed"]:checked').val(); var code = "M120\nM83\nG1 E" + dir + amount + feedRate + "\nM121"; $.askElle('gcode', code); }); //gcodes $('div#sendG button#txtinput, div#sendG a').on('click', function() { var code; if (this.nodeName === 'BUTTON') { code = $('input#gInput').val().toUpperCase(); } else { code = $(this).text(); } $.askElle('gcode', code); //send gcode }); $('table#quicks').on('click', 'a', function() { var code; if (this.attributes.itemprop) { code = this.attributes.itemprop.value; } else { code = $(this).text(); } $.askElle('gcode', code); }); $('input#gInput').keydown(function(event) { if (event.which === 13) { event.preventDefault(); $.askElle('gcode', $(this).val().toUpperCase()); } }); //move controls $('table#moveHead').on('click', 'button', function() { var btnVal = $(this).attr('value'); if (btnVal) { $.askElle('gcode', btnVal); } else { var value = $(this).text(); var feedRate = " F4000"; if (value.indexOf("Z") >= 0) feedRate = " F200"; var movePreCode = "M120\nG91\nG1 "; var movePostCode = "\nM121"; $.askElle('gcode', movePreCode + value + feedRate + movePostCode); } }); //panic buttons $('div#panicBtn button').on('click', function() { var btnVal = $(this).attr('value'); switch (btnVal) { case "M112": //panic stop window.stop(); paused = false; break; case "reset": //reset printing after pause printing = false; paused = false; btnVal = "M0"; resetLayerData(0, 0, false); //no break case "M24": //resume paused = false; $('button#pause').removeClass('active').text('Pause').attr('value', 'M25'); $('button#printing').text("Ready :)"); $('button#reset').addClass('hidden'); break; case "M25": //pause paused = true; $(this).addClass('active').text('Resume').attr('value', 'M24'); $('button#printing').text("Paused"); $('button#reset').removeClass('hidden'); break; } $.askElle('gcode', btnVal); }); //sliders $('#sFactor').slider({orientation:'vertical', reversed:true, min:10, max:300, step:5, value:100, tooltip:'show'}); $('#e1Factor').slider({orientation:'vertical', reversed:true, min:80, max:120, step:1, value:100, tooltip:'show'}); $('#e2Factor').slider({orientation:'vertical', reversed:true, min:80, max:120, step:1, value:100, tooltip:'show'}); $("#sFactor").on('slide', function(slideEvt) { sSliderActive = true; sFactor = slideEvt.value; $("span#sPercent").text(sFactor); }); $("#e1Factor").on('slide', function(slideEvt) { e1SliderActive = true; e1Factor = slideEvt.value; $("span#e1Percent").text(e1Factor); }); $("#e2Factor").on('slide', function(slideEvt) { e2SliderActive = true; e2Factor = slideEvt.value; $("span#e2Percent").text(e2Factor); }); $("#sFactor").on('slideStop', function(slideEvt) { sFactor = slideEvt.value; $("span#sPercent").text(sFactor); $.askElle('gcode', "M220 S"+sFactor); sSliderActive = false; }); $("#e1Factor").on('slideStop', function(slideEvt) { e1Factor = slideEvt.value; $("span#e1Percent").text(e1Factor); $.askElle('gcode', "M221 D0 S"+e1Factor); e1SliderActive = false; }); $("#e2Factor").on('slideStop', function(slideEvt) { e2Factor = slideEvt.value; $("span#e2Percent").text(e2Factor); $.askElle('gcode', "M221 D1 S"+e2Factor); e2SliderActive = false; }); //g files $("div#gFileList1, div#gFileList2, div#gFileList3") .on('mouseover', 'span#fileDelete', function() { $(this).parent().parent().parent().parent().parent().addClass('file-red'); }).on('mouseout', 'span#fileDelete', function() { $(this).parent().parent().parent().parent().parent().removeClass('file-red'); }).on('mouseover', 'span#filePrint', function() { $(this).parent().parent().parent().parent().parent().addClass('file-green'); }).on('mouseout', 'span#filePrint', function() { $(this).parent().parent().parent().parent().parent().removeClass('file-green'); }).on('mouseover', 'span#fileInfo', function() { $(this).parent().parent().parent().parent().parent().addClass('file-blue'); }).on('mouseout', 'span#fileInfo', function() { $(this).parent().parent().parent().parent().parent().removeClass('file-blue'); }).on('click', 'span#fileDelete', function() { var filename = gcodeDir + $(this).parent().parent().text(); var resp = $.askElle('delete', filename); if (resp.err == 0) { message('success', "G file [" + filename + "] deleted from the SD card"); } else { modalMessage("Delete failed", "Failed to delete file " + filename, true); } listGFiles(); }).on('click', 'span#filePrint', function() { var filename = $(this).parent().parent().text(); printSDfile(filename); }).on('click', 'span#fileInfo', function() { showFileInfo(gcodeDir + $(this).parent().parent().text()); }); $("button#filereload").on('click', function() { $('span#ulTitle').text("File Upload Status"); setProgress(0, "ul", 0,0); listGFiles(); }); $('#uploadPrintGfile').on('click', function(){ $('input#uploadPrintGselect').click(); }); $('#uploadGfile').on('click', function(){ $('input#uploadGselect').click(); }); $('#ulConfigG').on('click', function(){ $('input#ulConfigGselect').click(); }); $('#ulReprapHTM').on('click', function(){ $('input#ulReprapHTMselect').click(); }); $("input#uploadPrintGselect:file").change(function (e){ var file = this.files[0]; readFile(this.files[0], function(e) { handleFileDrop(e.target.result, file.name, 'uploadandprint'); }); $(this).val(''); }); $("input#uploadGselect:file").change(function (e){ var file = this.files[0]; readFile(this.files[0], function(e) { handleFileDrop(e.target.result, file.name, 'upload'); }); $(this).val(''); }); $("input#ulConfigGselect:file").change(function (e){ var file = this.files[0]; readFile(this.files[0], function(e) { handleFileDrop(e.target.result, file.name, 'config'); }); $(this).val(''); }); $("input#ulReprapHTMselect:file").change(function (e){ var file = this.files[0]; readFile(this.files[0], function(e) { handleFileDrop(e.target.result, file.name, 'htm'); }); $(this).val(''); }); //Settings/cookie buttons $("div#settings button#saveSettings").on('click', function(){ saveSettings(); }); $("div#settings button#delSettings").on('click', function(){ delSettings(); }); $('a[data-toggle="tab"]').on('show.bs.tab', function(e) { if (e.target.hash == "#settings") { $.askElle("gcode", "M503"); //get config.g on setting view } }); //Messages $("div#messages button#clearLog").on('click', function(){ message('clear', ''); }); //In the following, 'whichTemp' should be 'active', 'standby' or 'bed' function addTemp(tempVal, whichTemp) { if (tempVal != "") { var temps = storage.get('temps', whichTemp); var newTemp = parseInt(tempVal); if (temps.indexOf(newTemp) < 0) { temps.push(newTemp); temps.sort(function(a, b){return b-a}); storage.set('temps.' + whichTemp, temps); loadSettings(); } }else{ modalMessage("Error Adding Head Temp!", "You must enter a Temperature to add it to the dropdown list", close); } } function getCookies() { //if none use defaults here, probably move them elsewhere at some point! if (!storage.get('settings')) { storage.set('settings', { pollDelay : 1000, layerHeight : 0.24, halfz : 0, noOK : 0 }); } if (!storage.get('temps') || !storage.get('temps.active')) { storage.set('temps', {'bed' : [120,65,0], 'active' : [240,185,0], 'standby' : [190,150,0] }); } } function loadSettings() { $('div#settings input#pollDelay').val(storage.get('settings', 'pollDelay').toString()); $('div#settings input#layerHeight').val(storage.get('settings', 'layerHeight').toString().toString()) storage.get('settings', 'halfz')==1?$('div#settings input#halfz').prop('checked', true):$('div#settings input#halfz').prop('checked', false); storage.get('settings', 'noOK')==1?$('div#messages input#noOK').prop('checked', true):$('div#messages input#noOK').prop('checked', false); $('div#bedActiveTemperature ul').html('
  • Add Temp
  • '); $('div#head1ActiveTemperature ul').html('
  • Add Temp
  • '); $('div#head1StandbyTemperature ul').html('
  • Add Temp
  • '); $('div#head2ActiveTemperature ul').html('
  • Add Temp
  • '); $('div#head2StandbyTemperature ul').html('
  • Add Temp
  • '); storage.get('temps', 'bed').forEach(function(item){ $('div#bedActiveTemperature ul').prepend('
  • '+item+'
  • '); }); storage.get('temps', 'active').forEach(function(item){ $('div#head1ActiveTemperature ul').prepend('
  • '+item+'
  • '); $('div#head2ActiveTemperature ul').prepend('
  • '+item+'
  • '); }); storage.get('temps', 'standby').forEach(function(item){ $('div#head1StandbyTemperature ul').prepend('
  • '+item+'
  • '); $('div#head2StandbyTemperature ul').prepend('
  • '+item+'
  • '); }); } function delSettings() { storage.removeAll(); getCookies(); loadSettings(); } function saveSettings() { var zwas = storage.get('settings', 'halfz'); storage.set('settings.pollDelay', parseInt($('div#settings input#pollDelay').val())); storage.set('settings.layerHeight', parseFloat($('div#settings input#layerHeight').val())); $('div#settings input#halfz').is(':checked')?storage.set('settings.halfz','1'):storage.set('settings.halfz','0'); $('div#settings input#noOK').is(':checked')?storage.set('settings.noOk','1'):storage.set('settings.noOK','0'); if (zwas !== storage.get('settings', 'halfz')) { $('div#Zminus, div#Zplus').text(''); moveVals(['Z']); } } function moveVals(axis) { axis.forEach(function(value) { storage.get('settings','halfz')==1&&value=='Z'?i=50:i=100; var button = 0; for (i; i >= 0.05; i=i/10) { $('div#'+value+'minus').append(''); $('div#'+value+'plus').prepend(''); button++; } }); } function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function fileDrop() { $('#uploadGfile').fileDrop({ decodeBase64: true, removeDataUriScheme: true, overClass: 'btn-success', onFileRead: function(file) { handleFileDrop(file[0].data, file[0].name, "upload"); } }); $('#uploadPrintGfile').fileDrop({ decodeBase64: true, removeDataUriScheme: true, overClass: 'btn-success', onFileRead: function(file) { handleFileDrop(file[0].data, file[0].name, "uploadandprint"); } }); $('#ulConfigG').fileDrop({ decodeBase64: true, removeDataUriScheme: true, overClass: 'btn-success', onFileRead: function(file) { handleFileDrop(file[0].data, file[0].name, "config"); } }); $('#ulReprapHTM').fileDrop({ decodeBase64: true, removeDataUriScheme: true, overClass: 'btn-success', onFileRead: function(file) { handleFileDrop(file[0].data, file[0].name, "htm"); } }); } function getFileLength() { var kbytes = gFileData.length/1024; return (kbytes < 1000) ? kbytes.toFixed(0) + "Kb" : (kbytes/1024).toFixed(2) + "Mb"; } function handleFileDrop(data, fName, action) { var ext = getFileExt(fName).toLowerCase(); gFileData = data; gFileIndex = 0; switch (action) { case "config": uploadFile(action, fName, sysDir + fName, ""); break; case "htm": switch(ext) { case "js": uploadFile(action, fName, webDir + "js/" + fName, ""); break; case "css": uploadFile(action, fName, webDir + "css/" + fName, ""); break; case "eot": case "svg": case "ttf": case "woff": uploadFile(action, fName, webDir + "fonts/" + fName, ""); break; case "png": uploadFile(action, fName, webDir + "img/" + fName, ""); break; default: uploadFile(action, fName, webDir + fName, ""); break; } break; case "upload": uploadFile(action, fName, gcodeDir + fName, ""); break; case "uploadandprint": var tempFilename = "tempWebPrint.gcode"; uploadFile("upload", fName, gcodeDir + tempFilename, tempFilename); break; } } function uploadFile(action, fromFile, toFile, printAfterUpload) { timer(); var resp = $.askElle('upload_begin', toFile); if (resp.err == 0) { ubuff = resp.ubuff; if (fromFile == toFile) { message("info", "File Upload of " + fromFile + " started"); } else { message("info", "File Upload of " + fromFile + " to " + toFile + " started"); } gFilename = fromFile; uploadModal(); expansionFactor = 1.33; // this is about right for gcode files uploadLoop(action, printAfterUpload); } else { uploadCantStart(toFile); } } // Update a table with file info and return [layerHeight, height, filament] function updateFileInfo(fileName, id) { var info = $.askElle('fileinfo', fileName); return updateFileTableInfo(info, id); } // Ditto where we already have the file info function updateFileTableInfo(info, id) { clearSlic3rSettings(id); var height = 0, filament = 0, locLayerHeight = 0; if (info.hasOwnProperty('height') && isNumber(info.height)) { height = info.height; if (id == 'slic3rModal') { addSlic3rSetting(id, "Object height", info.height + 'mm'); } } if (info.hasOwnProperty('filament')) { if ($.isArray(info.filament)) { if (info.filament.length != 0) { // Duet firmware 0.78d-dc42 and later return an array of filament lengths needed. For now we just total them. var fils = ""; for(var i = 0; i < info.filament.length; ++i) { filament += info.filament[i]; if (i != 0) { fils += ' + '; } fils += info.filament[i]; } addSlic3rSetting(id, "Filament Needed", fils + "mm"); } } else if (isNumber(info.filament) && info.filament != 0) { filament = info.filament; addSlic3rSetting(id, "Filament Needed", filament + "mm"); } } if (info.hasOwnProperty('layerHeight') && isNumber(info.layerHeight) && info.layerHeight != 0) { locLayerHeight = info.layerHeight; addSlic3rSetting(id, "Layer height", info.layerHeight + 'mm'); } if (info.hasOwnProperty('generatedBy') && info.generatedBy.length != 0) { addSlic3rSetting(id, "Generated by", info.generatedBy); } if (info.hasOwnProperty('size') && isNumber(info.size)) { var sizeText = (info.size >= 1048576) ? (info.size/1048576).toFixed(3) + 'Mb' : (info.size >= 1024) ? (info.size/1024).toFixed(3) + 'Kb' : info.size + 'b'; addSlic3rSetting(id, "File size", sizeText); } return [locLayerHeight, height, filament]; } function printSDfile(fName) { var temp = updateFileInfo(gcodeDir + fName, 'slic3rMain'); layerHeight = temp[0]; var height = temp[1], filament = temp[2]; $.askElle('gcode', "M23 " + fName + "\nM24"); message('success', "File [" + fName + "] sent to print"); $('span#gFileDisplay').html('Printing ' + fName + ' from Duet SD card'); resetLayerData(height, filament, false); $('progress#printProgressBar').show(); $('#tabs a:eq(1)').tab('show'); } function uploadModal() { modalMessage('File Upload Status', "Uploading " + gFilename + " (" + getFileLength() + ") "+ "", false); } function uploadCantStart(toFile) { modalMessage("File upload failed", "Upload failed because file " + toFile + " could not be created.", true); message("info", "Failed to create file " + toFile); } function readFile(file, onLoadCallback){ //read file from click-choose type printing/upload var reader = new FileReader(); reader.onload = onLoadCallback; reader.readAsText(file); } function clearSlic3rSettings(id) { $('table#' + id + ' tbody').html(''); //slic3r setting } function addSlic3rSetting(id, key, data) { $('table#' + id + ' tbody').append(''); } function uploadLoop(action, fileToPrint) { //Web Printing/Uploading if (gFileIndex == gFileData.length) { //Finished with Dropped file, stop loop, end tasks var resp = $.askElle('upload_end', gFileData.length); if (resp.err != 0) { $('span#ulTitle').text(gFilename + " Upload Failed!"); $('span#ulProgressText').text("ERROR!"); $('div#modal button#modalClose').removeClass('hidden'); message("info", gFilename + " Upload Failed!"); } else { var duration = (timer() - timerStart).toHHMMSS(); switch (action) { case "upload": listGFiles(); if (fileToPrint != "") { $('div#modal').modal('hide'); printSDfile(fileToPrint); } else { $('span#ulTitle').text(gFilename + " uploaded " + getFileLength() + " in " + duration); $('div#modal button#modalClose').removeClass('hidden'); message("info", gFilename + " Upload Complete in " + duration); } break; case "config": $.askElle("gcode", "M503"); //update config.g on setting view // no break case "htm": $('span#ulTitle').text(gFilename + " uploaded " + getFileLength() + " in " + duration); $('div#modal button#modalClose').removeClass('hidden'); message("info", gFilename + " Upload Complete in "+ duration); break; } } gFileData = ""; } else { var wait = 20; var progress = Math.floor((100 * gFileIndex) / gFileData.length); var lastProgress = progress; if (paused == true) { wait = 2000; } else { // Send a number of packets in quick succession while there is plenty of buffer space, to speed up the file upload. // If we send too many then the user interface doesn't update often enough. do { webSend(action); progress = Math.floor((100 * gFileIndex) / gFileData.length); } while (ubuff >= 600 && gFileIndex < gFileData.length && progress == lastProgress); if (ubuff >= 200) { wait = 1; } } setProgress(progress, "ul", 0, 0); setTimeout(function() { uploadLoop(action, fileToPrint); }, wait); } } function webSend(action) { //Web Printing/Uploading if (ubuff > maxUploadBuffer) { ubuff = maxUploadBuffer; } if (gFileIndex < gFileData.length) { var line = ""; var startIndex = gFileIndex; while(gFileIndex < gFileData.length && line.length + 50 <= ubuff) { // keep going until less than 50 bytes free space left var chunkSize = Math.min(Math.floor((ubuff - line.length)/(expansionFactor * 1.1)), gFileData.length - gFileIndex); var chunk = encodeURIComponent(gFileData.substring(gFileIndex, gFileIndex + chunkSize)); if (line.length + chunk.length <= ubuff) { line += chunk; gFileIndex += chunkSize; } else { expansionFactor = chunk.length/chunkSize; } } var resp = $.askElle('upload_data', line); //send chunk of gcodes or html, and get buffer response expansionFactor = (0.875 * expansionFactor) + (0.125 * (line.length/(gFileIndex - startIndex))); if (typeof resp != 'undefined') { ubuff = resp.ubuff; } else { ubuff = 0; } } } function listGFiles() { var count = 0; var filesPerCol; var list = "gFileList1"; $('div#gFileList1, div#gFileList2, div#gFileList3').html(""); var result = $.askElle("files", ""); result.files.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); result.files.length>18?filesPerCol=Math.ceil(result.files.length/3):filesPerCol = 6; result.files.forEach(function(item) { count++; switch (true) { case (count > (filesPerCol * 2)): list = "gFileList3"; break; case (count > filesPerCol): list = "gFileList2"; break; } if(jQuery.inArray(item, macroGs) >= 0) { if (!$('table#quicks a[itemprop="M23 '+item+'\nM24"]').text()) { $('table#quicks td:eq(0)').append(''+item+''); } } $('div#' + list).append('
    '+key+'
    '+data+'
    ' + '' + '' + '' + '
    ' + item + '
    '); }); } function getFileExt(filename) { return filename.split('.').pop(); } function getFileName(filename) { return filename.split('.').shift(); } function disableButtons(which) { switch (which) { case "head": $('table#moveHead button, table#extruder button, table#extruder label, table#quicks a, button#uploadGfile, button#ulConfigG, button#ulReprapHTM, button#uploadPrintGfile').addClass('disabled'); break; case "temp": $('table#temp button').addClass('disabled'); break; case "panic": $('div#panicBtn button').addClass('disabled'); $('button#reset').addClass('hidden'); break; case "gfilelist": $('div#gcodefiles button').addClass('disabled'); break; case "sendG": $('div#sendG button, div#sendG a, input#gInput').addClass('disabled'); break; } } function enableButtons(which) { switch (which) { case "head": $('table#moveHead button, table#extruder button, table#extruder label, table#quicks a, button#uploadGfile, button#ulConfigG, button#ulReprapHTM, button#uploadPrintGfile').removeClass('disabled'); break; case "temp": $('table#temp button').removeClass('disabled'); break; case "panic": $('div#panicBtn button').removeClass('disabled'); break; case "gfilelist": $('div#gcodefiles button').removeClass('disabled'); break; case "sendG": $('div#sendG button, div#sendG a, input#gInput').removeClass('disabled'); break; } } function modalMessage(title, text, close) { $('div#modal h4.modal-title').text(title); $('div#modal div.modal-body').html(text); close?$('div#modal button#modalClose').removeClass('hidden'):$('div#modal button#modalClose').addClass('hidden'); $('div#modal').modal({show:true}); } function showFileInfo(fileName) { $('div#modalFileInfo h4.modal-title').text("File information for " + fileName); updateFileInfo(fileName, 'slic3rModal'); $('div#modalFileInfo').modal({show:true}); } function message(type, text) { var d = new Date(); var time = zeroPrefix(d.getHours()) + ":" + zeroPrefix(d.getMinutes()) + ":" + zeroPrefix(d.getSeconds()); if (type == 'clear') { $('div#messageText').html(time + " Log Cleared
    "); } else { $('div#messageText').prepend(time + " " + text + "
    "); } } function parseResponse(res) { switch (true) { case res.indexOf('Debugging enabled') >= 0: message('info', 'M111
    ' + res.replace(/\n/g, "
    ")); break; case res.indexOf('Firmware') >= 0: var strt = res.indexOf("SION:") +5 ; var end = res.indexOf(" ELEC"); $('p#firmVer').text(res.substr(strt, end - strt)); message('info', 'M115
    ' + res.replace(/\n/g, "
    ")); $.askElle("gcode", "M105"); break; case res.indexOf('M550') >= 0: message('info', 'M503
    ' + res.replace(/\n/g, "
    ")); $('div#config').html("
    Config.g File Contents:
    "); res.split(/\n/g).forEach(function(item) { $('div#config').append("" + item + "
    "); }); $.askElle("gcode", "M105"); break; case res == "ok": if ($('div#messages input#noOK').is(':checked')) { message('info', res); } break; default: message('info', res); break; } } function homedWarning(x,y,z) { if ((x+y+z) < 3) { $('span#warning').text('*some axes are not homed'); } else { $('span#warning').text(''); } x===0?$('button#homeX').removeClass('btn-primary').addClass('btn-warning'):$('button#homeX').removeClass('btn-warning').addClass('btn-primary'); y===0?$('button#homeY').removeClass('btn-primary').addClass('btn-warning'):$('button#homeY').removeClass('btn-warning').addClass('btn-primary'); z===0?$('button#homeZ').removeClass('btn-primary').addClass('btn-warning'):$('button#homeZ').removeClass('btn-warning').addClass('btn-primary'); } function updatePage() { var status = $.askElle("status", ""); if (!status || !polling) { $('button#connect').removeClass('btn-success').addClass('btn-danger'); $('button#printing').removeClass('btn-warning').removeClass('btn-success').addClass('btn-danger').text("Disconnected"); if (polling) { message('danger', "Warning! Ormerod webserver is probably broken, power cycle/reset your Duet Board :("); $('button#connect').text("Retrying"); } else { message('info', "Disconnected Page not being updated"); $('button#connect').text("Connect"); } $('span[id$="Temp"], span[id$="pos"]').text("0"); $('span[id$="State"]').text(getState(-1)); $('td#head1, td#head2').css('background-color', '#FFFFFF'); disableButtons("head"); disableButtons("temp"); disableButtons("panic"); } else { $('button#connect').removeClass('btn-danger').addClass('btn-success').text("Online"); //Connected Hoorahhh! if (messageSeqId !== status.seq) { messageSeqId = status.seq; parseResponse(status.resp); } currentFilamentPos = status.extr; homedWarning(status.homed[0],status.homed[1],status.homed[2]); currentTool = status.tool; if (currentTool == 1) { $('td#head1').css('background-color', '#E0E0E0'); } else { $('td#head1').css('background-color', '#FFFFFF'); } if (currentTool == 2) { $('td#head2').css('background-color', '#E0E0E0'); } else { $('td#head2').css('background-color', '#FFFFFF'); } if (status.hasOwnProperty('fanRPM')) { $('#fanRPM').text(status.fanRPM.toString()); } if (status.status == "S") { //stopped printing = false; $('button#printing').removeClass('btn-danger').removeClass('btn-success').addClass('btn-warning').text("Halted"); disableButtons('panic'); disableButtons("head"); disableButtons("temp"); disableButtons("gfilelist"); } else if (status.status === "P") { //printing printing = true; objHeight = $('input#objheight').val(); if (!isNumber(objHeight) && status.status == 'P' && status.hasOwnProperty('height')) { objHeight = status.height; $('input#objheight').val(objHeight.toString()); } $('button#printing').removeClass('btn-danger').removeClass('btn-warning').addClass('btn-success').text("Active"); enableButtons('panic'); enableButtons('temp'); disableButtons("print"); disableButtons("gfilelist"); currentLayer = whichLayer(status.pos[2]); if (isNumber(objHeight)) { if (!layerHeight) layerHeight = storage.get('settings','layerHeight'); layerCount = Math.ceil(objHeight / layerHeight); } if (printStartTime && objTotalFilament > 0) { setProgress(Math.floor((objUsedFilament.reduce(function(a, b){ return a + b; }, 0) / objTotalFilament) * 100), 'print', currentLayer, layerCount); estEndTime(); } else if (printStartTime && layerCount > 0) { setProgress(Math.floor((currentLayer / layerCount) * 100), 'print', currentLayer, layerCount); } else { setProgress(0, 'print', 0, 0); } layers(currentLayer); } else if (status.status === "I" && !paused ) { //inactive, not printing printing = false; $('button#printing').removeClass('btn-danger').removeClass('btn-success').addClass('btn-warning').text("Ready :)"); disableButtons("panic"); enableButtons('head'); enableButtons('temp'); enableButtons("gfilelist"); } else if (status.status === "I" && paused) { //paused printing = true; $('button#printing').removeClass('btn-danger').removeClass('btn-success').addClass('btn-warning').text("Paused"); enableButtons('panic'); enableButtons('head'); enableButtons('temp'); } else { //unknown state printing = paused = false; $('button#printing').removeClass('btn-warning').removeClass('btn-success').addClass('btn-danger').text("Error!"); message('danger', 'Unknown Poll State : ' + status.status); } // Update the current and selected active/standby temperatures $('span#bedCurrentTemp').text(status.heaters[0].toFixed(1) + "°C"); if (!$('input#bedActiveTempInput').is(":focus")) { $('input#bedActiveTempInput').val(status.active[0].toFixed(0)); } $('span#head1CurrentTemp').text(status.heaters[1].toFixed(1) + "°C"); if (!$('input#head1ActiveTempInput').is(":focus")) { $('input#head1ActiveTempInput').val(status.active[1].toFixed(0)); } if (!$('input#head1StandbyTempInput').is(":focus")) { $('input#head1StandbyTempInput').val(status.standby[1].toFixed(0)); } $('span#head2CurrentTemp').text((status.heaters.length >= 3) ? status.heaters[2].toFixed(1) + "°C" : "n/a"); if (!$('input#head2ActiveTempInput').is(":focus")) { $('input#head2ActiveTempInput').val((status.active.length >= 3) ? status.active[2].toFixed(0) : "n/a"); } if (!$('input#head2StandbyTempInput').is(":focus")) { $('input#head2StandbyTempInput').val((status.standby.length >= 3) ? status.standby[2].toFixed(0) : "n/a"); } // Update the heater statuses $('span#head1State').text(getState(status.hstat[1])); $('span#head2State').text(getState(status.hstat[2])); $('span#bedState').text(getState(status.hstat[0])); // Update the head position and zprobe $('span#Xpos').text(status.pos[0].toFixed(2)); $('span#Ypos').text(status.pos[1].toFixed(2)); $('span#Zpos').text(status.pos[2].toFixed(2)); var ePosText = ""; for(var i=0; i < status.extr.length; ++i) { if (i != 0) { ePosText += ", "; } ePosText += status.extr[i].toFixed(1); } $('span#Epos').text(ePosText); $('span#probe').text(status.probe); //Temp chart stuff chartData[0].push(parseFloat(status.heaters[0])); chartData[1].push(parseFloat(status.heaters[1])); chartData[2].push((status.heaters.length >= 3) ? parseFloat(status.heaters[2]) : 0); chart.setData(parseChartData()); chart.draw(); // Update the slider positions if (!sSliderActive) { sFactor = status.sfactor; $('#sFactor').slider('setValue', sFactor); $("span#sPercent").text(sFactor); } if (!e1SliderActive) { e1Factor = status.efactor[0]; $('#e1Factor').slider('setValue', e1Factor); $("span#e1Percent").text(e1Factor); } if (status.efactor.length >= 2 && !e2SliderActive) { e2Factor = status.efactor[1]; $('#e2Factor').slider('setValue', e2Factor); $("span#e2Percent").text(e2Factor); } } } function getState(state) { switch(state) { case 0: return 'off'; case 1: return 'standby'; case 2: return 'active'; case 3: return 'fault'; default: return ''; } } function getFilamentUsed() { for(var i = 0; i < currentFilamentPos.length && i < startingFilamentPos.length; ++i) { if (currentFilamentPos[i] - startingFilamentPos[i] < objUsedFilament[i] - 12) { //gone backwards by more than 12mm so probably just done a G30 E0 to reset the filament origin startingFilamentPos[i] = currentFilamentPos[i] - objUsedFilament[i]; } else { objUsedFilament[i] = currentFilamentPos[i] - startingFilamentPos[i]; } } return objUsedFilament.reduce(function(a, b){ return a + b; }, 0); } function estEndTime() { var utime = (new Date()).getTime(); if (layerData.length >= 3 && layerCount > 0) { var layerLeft = layerCount - currentLayer; // average over the last 5 layers, or all layers if less var startAt = (layerData.length > 6) ? layerData.length - 5 : 1; var avg5 = (layerData[layerData.length - 1] - layerData[startAt])/(layerData.length - startAt); var avg5R = new Date(utime + (avg5 * layerLeft)); $('span#avg5R').text((avg5 * layerLeft).toHHMMSS()); $('span#avg5').text(avg5R.toLocaleTimeString()); } if (objTotalFilament > 0) { var objTotalUsedFilament = getFilamentUsed(); if (objTotalUsedFilament <= objTotalFilament) { var filamentLeft = objTotalFilament - objTotalUsedFilament; if (filamentData.length >= 3 && layerCount > 0) { var startAt = (layerData.length > 6) ? layerData.length - 5 : 1; filamentRate = (filamentData[filamentData.length - 1] - filamentData[startAt])/(layerData[filamentData.length - 1] - layerData[startAt]); if (filamentRate != 0) { var timeLeft = filamentLeft/filamentRate; var estEndTimeFil = new Date(utime + timeLeft); $('span#filTimeR').text(timeLeft.toHHMMSS()); $('span#filTime').text(estEndTimeFil.toLocaleTimeString()); } } else if (objTotalUsedFilament > objTotalFilament * 0.03) { //if at least 3% filament consumed var timeSoFar = utime - printStartTime; var timeLeft = timeSoFar * filamentLeft/objTotalUsedFilament; var estEndTimeFil = new Date(utime + timeLeft); $('span#filTimeR').text(timeLeft.toHHMMSS()); $('span#filTime').text(estEndTimeFil.toLocaleTimeString()); } } } } function whichLayer(currZ) { if (!layerHeight) { layerHeight = storage.get('settings','layerHeight'); } var n = Math.round(currZ / layerHeight); if (n === currentLayer + 1 && currentLayer) { layerChange(); } return n; } function resetLayerData(h, f, reconnecting) { //clear layer count,times and chart $('input#objheight').val((h == 0) ? "" : h.toString()); objTotalFilament = f; if (reconnecting) { startingFilamentPos = []; objUsedFilament = currentFilamentPos; for(var i = 0; i < currentFilamentPos.length; ++i) { startingFilamentPos.push(0); } } else { startingFilamentPos = currentFilamentPos; objUsedFilament = []; for(var i = 0; i < currentFilamentPos.length; ++i) { objUsedFilament.push(0); } } layerData = []; filamentData = []; if (!reconnecting) { printStartTime = null; } setProgress(0, 'print', 0, 0); $('span#elapsed, span#lastlayer, table#finish span').text("00:00:00"); chart2.setData(parseLayerData()); chart2.setupGrid(); chart2.draw(); } function layerChange() { var utime = (new Date()).getTime(); layerData.push(utime); filamentData.push(getFilamentUsed()); if (layerData.length > maxLayerBars) { layerData.shift(); filamentData.shift(); } if (printStartTime && layerData.length > 1) { var lastLayerStart = layerData[layerData.length - 2]; $('span#lastlayer').text((utime - lastLayerStart).toHHMMSS()); chart2.setData(parseLayerData()); chart2.setupGrid(); chart2.draw(); if (isNumber(objHeight)) { estEndTime(); } } } function layers(layer) { var utime = (new Date()).getTime(); if ((layer === 1 || layer === 2) && !printStartTime) { printStartTime = utime; layerData.push(utime); filamentData.push(startingFilamentPos.reduce(function(a, b){ return a + b; }, 0)); } if (printStartTime) { $('span#elapsed').text((utime - printStartTime).toHHMMSS()); } } function zeroPrefix(num) { var n = num.toString(); if (n.length === 1) { return "0" + n; } return n; } function setProgress(percent, bar, layer, layers) { $('progress#'+bar+'ProgressBar').attr('value', percent); var ptext = percent + "% Complete"; if (bar == 'print') { if (layers > 0) { ptext += ", Layer " + layer + " of " + layers; } if (objTotalFilament > 0) { var currentTotal = currentFilamentPos.reduce(function(a, b) {return a + b; }, 0); var startingTotal = startingFilamentPos.reduce(function(a, b) {return a + b; }, 0); ptext += ", Filament " + (currentTotal - startingTotal).toFixed(1) + " of " + objTotalFilament + "mm"; } } $('span#'+bar+'ProgressText').text(ptext); } function parseLayerData() { var res = []; //res.push([0,0]); var elapsed; for (var i = 1; i < layerData.length; ++i) { elapsed = Math.round((layerData[i] - layerData[i - 1]) / 1000); res.push([i, elapsed]); } return [res]; } function parseChartData() { if (chartData[0].length > maxDataPoints) chartData[0].shift(); if (chartData[1].length > maxDataPoints) chartData[1].shift(); if (chartData[2].length > maxDataPoints) chartData[2].shift(); var res = [[], [], []]; for (var i = 0; i < chartData[0].length; ++i) { res[0].push([i, chartData[0][i]]); res[1].push([i, chartData[1][i]]); res[2].push([i, chartData[2][i]]); } return res; } function timer() { var utime = (new Date()).getTime(); if (!timerStart) { timerStart = utime; } else { var elapsed = utime - timerStart; timerStart = null; return elapsed; } } function poll() { if (polling) { setTimeout(function() { updatePage(); poll(); }, storage.get('settings', 'pollDelay')); } } function getHTMLver() { return document.title.substr(document.title.indexOf("v")+1); } Number.prototype.toHHMMSS = function() { var h,m; var sec_num = Math.floor(this / 1000); // don't forget the second param var hours = Math.floor(sec_num / 3600); var minutes = Math.floor((sec_num - (hours * 3600)) / 60); var seconds = sec_num - (hours * 3600) - (minutes * 60); hours < 10?hours="0"+hours:false; minutes < 10?minutes="0"+minutes:false; seconds < 10?seconds="0"+seconds:false; hours=='00'?h="":h=hours+"h "; minutes=='00'?m="":m=minutes+"m "; return h+m+seconds + 's'; };