
Bug fix: M92 command would only set extruder steps/mm if either 1 or 5 extruder steps/mm was provided Bug fix: Z-homing or bed probing when axis compensation was enabled caused the X and Y origins to shift We now mark drives as not homed when we disable them File info code now supports gcode files generated by Simplify3D Updated SD card files to latest web interface Added more comments to config.g Head movement commands that were in the tfree files have been moved to the tpre files, to work better when G10 nozzle offsets are used
1378 lines
48 KiB
JavaScript
1378 lines
48 KiB
JavaScript
/*! Reprap Ormerod Web Control | by Matt Burnett <matt@burny.co.uk>. | open license
|
|
*/
|
|
var ver = 1.02; //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 = "<span class='glyphicon glyphicon-chevron-left'></span>";
|
|
var chevRight = "<span class='glyphicon glyphicon-chevron-right'></span>";
|
|
|
|
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 <a href='https://github.com/dc42/OrmerodWebControl'>https://github.com/dc42/OrmerodWebControl</a>",
|
|
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('<strong>Printing ' + resp.fileName + ' from Duet SD card</strong>');
|
|
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 = "M1";
|
|
//switch off heaters
|
|
$.askElle('gcode', "M140 S0"); //bed off
|
|
$.askElle('gcode', "G10 P1 S0 R0\nT1"); //head 1 off
|
|
$.askElle('gcode', "G10 P2 S0 R0\nT1"); //head 2 off
|
|
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('<li class="divider"></li><li><a href="#" id="addBedActiveTemp">Add Temp</a></li>');
|
|
$('div#head1ActiveTemperature ul').html('<li class="divider"></li><li><a href="#" id="addHead1ActiveTemp">Add Temp</a></li>');
|
|
$('div#head1StandbyTemperature ul').html('<li class="divider"></li><li><a href="#" id="addHead1StandbyTemp">Add Temp</a></li>');
|
|
$('div#head2ActiveTemperature ul').html('<li class="divider"></li><li><a href="#" id="addHead2ActiveTemp">Add Temp</a></li>');
|
|
$('div#head2StandbyTemperature ul').html('<li class="divider"></li><li><a href="#" id="addHead2StandbyTemp">Add Temp</a></li>');
|
|
storage.get('temps', 'bed').forEach(function(item){
|
|
$('div#bedActiveTemperature ul').prepend('<li><a href="#" id="bedActiveTempLink">'+item+'</a></li>');
|
|
});
|
|
storage.get('temps', 'active').forEach(function(item){
|
|
$('div#head1ActiveTemperature ul').prepend('<li><a href="#" id="head1ActiveTempLink">'+item+'</a></li>');
|
|
$('div#head2ActiveTemperature ul').prepend('<li><a href="#" id="head2ActiveTempLink">'+item+'</a></li>');
|
|
});
|
|
storage.get('temps', 'standby').forEach(function(item){
|
|
$('div#head1StandbyTemperature ul').prepend('<li><a href="#" id="head1StandbyTempLink">'+item+'</a></li>');
|
|
$('div#head2StandbyTemperature ul').prepend('<li><a href="#" id="head2StandbyTempLink">'+item+'</a></li>');
|
|
});
|
|
}
|
|
|
|
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('<button type="button" class="btn btn-default disabled">'+chevLeft+value+'-'+i.toString()+'</button>');
|
|
$('div#'+value+'plus').prepend('<button type="button" class="btn btn-default disabled">'+value+'+'+i.toString()+chevRight+'</button>');
|
|
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('<strong>Printing ' + fName + ' from Duet SD card</strong>');
|
|
resetLayerData(height, filament, false);
|
|
$('progress#printProgressBar').show();
|
|
$('#tabs a:eq(1)').tab('show');
|
|
}
|
|
|
|
function uploadModal() {
|
|
modalMessage('File Upload Status',
|
|
"<span id='ulTitle'>Uploading " + gFilename + " (" + getFileLength() + ")</span> <span id='ulProgressText' class='pull-right'></span>"+
|
|
"<progress id='ulProgressBar' style='width:100%' max='100' value='0'></progress>",
|
|
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 <table>
|
|
}
|
|
|
|
function addSlic3rSetting(id, key, data) {
|
|
$('table#' + id + ' tbody').append('<tr><th>'+key+'</th></tr><tr><td>'+data+'</td></tr>');
|
|
}
|
|
|
|
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('<a href="#" role="button" class="btn btn-default disabled" itemprop="M23 '+item+'\nM24" id="quickgfile">'+item+'</a>');
|
|
}
|
|
}
|
|
$('div#' + list).append('<div id="gFileLink" class="file-button"><table style="width:100%"><tbody><tr><td style="width:70%;word-break:break-all"><span id="fileName">'
|
|
+ item + '</span></td>'
|
|
+ '<td style="width:20px"><span id="fileInfo" class="glyphicon glyphicon-info-sign pull-right"></span></td>'
|
|
+ '<td style="width:20px"><span id="filePrint" class="glyphicon glyphicon-print pull-right"></span></td>'
|
|
+ '<td style="width:20px"><span id="fileDelete" class="glyphicon glyphicon-trash pull-right"></span></td>'
|
|
+ '</tr></tbody></table></div>');
|
|
});
|
|
}
|
|
|
|
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 + " <span class='alert-info'>Log Cleared</span><br />");
|
|
} else {
|
|
$('div#messageText').prepend(time + " <span class='alert-" + type + "'>" + text + "</span><br />");
|
|
}
|
|
}
|
|
|
|
function parseResponse(res) {
|
|
switch (true) {
|
|
case res.indexOf('Debugging enabled') >= 0:
|
|
message('info', '<strong>M111</strong><br />' + res.replace(/\n/g, "<br />"));
|
|
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', '<strong>M115</strong><br />' + res.replace(/\n/g, "<br />"));
|
|
$.askElle("gcode", "M105");
|
|
break;
|
|
case res.indexOf('M550') >= 0:
|
|
message('info', '<strong>M503</strong><br />' + res.replace(/\n/g, "<br />"));
|
|
$('div#config').html("<span class='col-md-9'><br/><strong>Config.g File Contents:</strong></span>");
|
|
res.split(/\n/g).forEach(function(item) {
|
|
$('div#config').append("<span class='alert-info col-md-9'>" + item + "</span><br />");
|
|
});
|
|
$.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', "<strong>Warning!</strong> Ormerod webserver is probably broken, power cycle/reset your Duet Board :(");
|
|
$('button#connect').text("Retrying");
|
|
} else {
|
|
message('info', "<strong>Disconnected</strong> 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';
|
|
};
|