kiln-controller/public/assets/js/picoreflow.js
sbman 26f02996c3 Added ability to show currently selected profile
Added the ability for the web app to show the currently selected profile
upon browser load.  Previously the application was showing the wrong
profile when loading a new client during a run, and backlog information
was shown on top of the incorrect graph.
2015-07-01 17:37:03 -07:00

593 lines
17 KiB
JavaScript

var state = "IDLE";
var state_last = "";
var graph = [ 'profile', 'live'];
var points = [];
var profiles = [];
var selected_profile = 0;
var time_mode = 0;
var selected_profile_name = "leadfree";
var host = "ws://" + window.location.hostname + ":" + window.location.port;
var ws_status = new WebSocket(host+"/status");
var ws_control = new WebSocket(host+"/control");
var ws_storage = new WebSocket(host+"/storage");
if(window.webkitRequestAnimationFrame) window.requestAnimationFrame = window.webkitRequestAnimationFrame;
graph.profile =
{
label: "Profile",
data: [],
points: { show: false },
color: "#75890c",
draggable: false
};
graph.live =
{
label: "Live",
data: [],
points: { show: false },
color: "#d8d3c5",
draggable: false
};
function updateProfile(id)
{
selected_profile = id;
job_time = parseInt(profiles[id].data[profiles[id].data.length-1][0]);
var kwh = (3850*job_time/3600/1000).toFixed(2);
var cost = (kwh*0.26).toFixed(2);
var minutes = Math.floor(job_time/60), seconds = job_time-minutes*60;
job_time = minutes+':'+ (seconds < 10 ? "0" : "") + seconds;
$('#sel_prof').html(profiles[id].name);
$('#sel_prof_eta').html(job_time);
$('#sel_prof_cost').html(kwh + ' kWh (EUR: '+ cost +')');
graph.profile.data = profiles[id].data;
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions());
}
function deleteProfile()
{
console.log("Delete profile:" + selected_profile_name);
// FIXME: Add cmd for socket communication to delete stored profile
leaveEditMode();
}
function updateProgress(percentage)
{
if(state=="RUNNING")
{
if(percentage > 100) percentage = 100;
$('#progressBar').css('width', percentage+'%');
if(percentage>5) $('#progressBar').html(parseInt(percentage)+'%');
}
else
{
$('#progressBar').css('width', 0+'%');
$('#progressBar').html('');
}
}
function updateProfileTable()
{
var dps = 0;
var slope = "";
var color = "";
var html = '<h3>Profile Points</h3><div class="table-responsive" style="scroll: none"><table class="table table-striped">';
html += '<tr><th style="width: 50px">#</th><th>Target Time</th><th>Target Temperature in °C</th><th>Slope in &deg;C/s</th><th></th></tr>';
for(var i=0; i<graph.profile.data.length;i++)
{
if (i>=1) dps = Math.round( (graph.profile.data[i][1]-graph.profile.data[i-1][1])/(graph.profile.data[i][0]-graph.profile.data[i-1][0]) * 10) / 10;
if (dps > 0) { slope = "up"; color="rgba(206, 5, 5, 1)"; } else
if (dps < 0) { slope = "down"; color="rgba(23, 108, 204, 1)"; dps *= -1; } else
if (dps == 0) { slope = "right"; color="grey"; }
html += '<tr><td><h4>' + (i+1) + '</h4></td>';
html += '<td><input type="text" class="form-control" id="profiletable-0-'+i+'" value="'+ graph.profile.data[i][0] + '" style="width: 60px" /></td>';
html += '<td><input type="text" class="form-control" id="profiletable-1-'+i+'" value="'+ graph.profile.data[i][1] + '" style="width: 60px" /></td>';
html += '<td><div class="input-group"><span class="glyphicon glyphicon-circle-arrow-' + slope +
' input-group-addon ds-trend" style="background: '+color+'"></span><input type="text" class="form-control ds-input" readonly value="' + dps + '" style="width: 50px" /></div></td>';
html += '<td>&nbsp;</td></tr>';
}
html += '</table></div>';
$('#profile_table').html(html);
//Link table to graph
$(".form-control").change(function(e)
{
var id = $(this)[0].id; //e.currentTarget.attributes.id
var value = parseInt($(this)[0].value);
var fields = id.split("-");
var col = parseInt(fields[1]);
var row = parseInt(fields[2]);
graph.profile.data[row][col] = value;
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ], getOptions());
updateProfileTable();
});
}
function timeTickFormatter(val)
{
if (val < 1800)
{
return val;
}
else
{
var hours = Math.floor(val / (3600));
var div_min = val % (3600);
var minutes = Math.floor(div_min / 60);
if (hours < 10) {hours = "0"+hours;}
if (minutes < 10) {minutes = "0"+minutes;}
return hours+":"+minutes;
}
}
function runTask()
{
var cmd =
{
"cmd": "RUN",
"profile": profiles[selected_profile]
}
graph.live.data = [];
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions());
ws_control.send(JSON.stringify(cmd));
}
function runTaskSimulation()
{
var cmd =
{
"cmd": "SIMULATE",
"profile": profiles[selected_profile]
}
graph.live.data = [];
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions());
ws_control.send(JSON.stringify(cmd));
}
function abortTask()
{
var cmd = {"cmd": "STOP"};
ws_control.send(JSON.stringify(cmd));
}
function enterNewMode()
{
state="EDIT"
$('#status').slideUp();
$('#edit').show();
$('#profile_selector').hide();
$('#btn_controls').hide();
$('#form_profile_name').attr('value', '');
$('#form_profile_name').attr('placeholder', 'Please enter a name');
graph.profile.points.show = true;
graph.profile.draggable = true;
graph.profile.data = [];
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ], getOptions());
updateProfileTable();
}
function enterEditMode()
{
state="EDIT"
$('#status').slideUp();
$('#edit').show();
$('#profile_selector').hide();
$('#btn_controls').hide();
$('#form_profile_name').attr('value', profiles[selected_profile].name);
graph.profile.points.show = true;
graph.profile.draggable = true;
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ], getOptions());
updateProfileTable();
}
function leaveEditMode()
{
selected_profile_name = $('#form_profile_name').val();
ws_storage.send('GET');
state="IDLE";
$('#edit').hide();
$('#profile_selector').show();
$('#btn_controls').show();
$('#status').slideDown();
$('#profile_table').slideUp();
graph.profile.points.show = false;
graph.profile.draggable = false;
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ], getOptions());
}
function newPoint()
{
if(graph.profile.data.length > 0)
{
var pointx = parseInt(graph.profile.data[graph.profile.data.length-1][0])+15;
}
else
{
var pointx = 0;
}
graph.profile.data.push([pointx, Math.floor((Math.random()*230)+25)]);
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ], getOptions());
updateProfileTable();
}
function delPoint()
{
graph.profile.data.splice(-1,1)
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ], getOptions());
updateProfileTable();
}
function toggleTable()
{
if($('#profile_table').css('display') == 'none')
{
$('#profile_table').slideDown();
}
else
{
$('#profile_table').slideUp();
}
}
function saveProfile()
{
name = $('#form_profile_name').val();
var rawdata = graph.plot.getData()[0].data
var data = [];
var last = -1;
for(var i=0; i<rawdata.length;i++)
{
if(rawdata[i][0] > last)
{
data.push([rawdata[i][0], rawdata[i][1]]);
}
else
{
$.bootstrapGrowl("<span class=\"glyphicon glyphicon-exclamation-sign\"></span> <b>ERROR 88:</b><br/>An oven is not a time-machine", {
ele: 'body', // which element to append to
type: 'alert', // (null, 'info', 'error', 'success')
offset: {from: 'top', amount: 250}, // 'top', or 'bottom'
align: 'center', // ('left', 'right', or 'center')
width: 385, // (integer, or 'auto')
delay: 5000,
allow_dismiss: true,
stackup_spacing: 10 // spacing between consecutively stacked growls.
});
return false;
}
last = rawdata[i][0];
}
var profile = { "type": "profile", "data": data, "name": name }
var put = { "cmd": "PUT", "profile": profile }
var put_cmd = JSON.stringify(put);
ws_storage.send(put_cmd);
leaveEditMode();
}
function getOptions()
{
var options =
{
series:
{
lines:
{
show: true
},
points:
{
show: true,
radius: 5,
symbol: "circle"
},
shadowSize: 3
},
xaxis:
{
min: 0,
tickColor: 'rgba(216, 211, 197, 0.2)',
tickFormatter: timeTickFormatter,
font:
{
size: 14,
lineHeight: 14, weight: "normal",
family: "Digi",
variant: "small-caps",
color: "rgba(216, 211, 197, 0.85)"
}
},
yaxis:
{
tickSize: 25,
min: 0,
max: 250,
tickDecimals: 0,
draggable: false,
tickColor: 'rgba(216, 211, 197, 0.2)',
font:
{
size: 14,
lineHeight: 14,
weight: "normal",
family: "Digi",
variant: "small-caps",
color: "rgba(216, 211, 197, 0.85)"
}
},
grid:
{
color: 'rgba(216, 211, 197, 0.55)',
borderWidth: 1,
labelMargin: 10,
mouseActiveRadius: 50
},
legend:
{
show: false
}
}
return options;
}
$(document).ready(function()
{
if(!("WebSocket" in window))
{
$('#chatLog, input, button, #examples').fadeOut("fast");
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
}
else
{
// Status Socket ////////////////////////////////
ws_status.onopen = function()
{
console.log("Status Socket has been opened");
$.bootstrapGrowl("<span class=\"glyphicon glyphicon-exclamation-sign\"></span> <b>Yay</b><br/>I'm alive",
{
ele: 'body', // which element to append to
type: 'success', // (null, 'info', 'error', 'success')
offset: {from: 'top', amount: 250}, // 'top', or 'bottom'
align: 'center', // ('left', 'right', or 'center')
width: 385, // (integer, or 'auto')
delay: 2500,
allow_dismiss: true,
stackup_spacing: 10 // spacing between consecutively stacked growls.
});
};
ws_status.onclose = function()
{
$.bootstrapGrowl("<span class=\"glyphicon glyphicon-exclamation-sign\"></span> <b>ERROR 1:</b><br/>Status Websocket not available", {
ele: 'body', // which element to append to
type: 'error', // (null, 'info', 'error', 'success')
offset: {from: 'top', amount: 250}, // 'top', or 'bottom'
align: 'center', // ('left', 'right', or 'center')
width: 385, // (integer, or 'auto')
delay: 5000,
allow_dismiss: true,
stackup_spacing: 10 // spacing between consecutively stacked growls.
});
};
ws_status.onmessage = function(e)
{
x = JSON.parse(e.data);
if (x.type == "backlog")
{
if (x.profile)
{
selected_profile_name = x.profile.name;
$.each(profiles, function(i,v) {
if(v.name == x.profile.name) {
updateProfile(i);
$('#e2').select2('val', i);
});
}
$.each(x.log, function(i,v) {
graph.live.data.push([v.runtime, v.temperature]);
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions());
});
}
if(state!="EDIT")
{
state = x.state;
if (x.door == "OPEN")
{
}
if (state!=state_last)
{
if(state_last == "RUNNING")
{
$('#target_temp').html('---');
updateProgress(0);
$.bootstrapGrowl("<span class=\"glyphicon glyphicon-exclamation-sign\"></span> <b>Run completed</b>", {
ele: 'body', // which element to append to
type: 'success', // (null, 'info', 'error', 'success')
offset: {from: 'top', amount: 250}, // 'top', or 'bottom'
align: 'center', // ('left', 'right', or 'center')
width: 385, // (integer, or 'auto')
delay: 0,
allow_dismiss: true,
stackup_spacing: 10 // spacing between consecutively stacked growls.
});
}
}
if(state=="RUNNING")
{
$("#nav_start").hide();
$("#nav_stop").show();
graph.live.data.push([x.runtime, x.temperature]);
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions());
left = parseInt(x.totaltime-x.runtime);
var minutes = Math.floor(left / 60);
var seconds = left - minutes * 60;
eta = minutes+':'+ (seconds < 10 ? "0" : "") + seconds;
updateProgress(parseFloat(x.runtime)/parseFloat(x.totaltime)*100);
$('#state').html('<span class="glyphicon glyphicon-time" style="font-size: 22px; font-weight: normal"></span><span style="font-family: Digi; font-size: 40px;">' + eta + '</span>');
$('#target_temp').html(parseInt(x.target));
}
else
{
$("#nav_start").show();
$("#nav_stop").hide();
$('#state').html('<p class="ds-text">'+state+'</p>');
}
$('#act_temp').html(parseInt(x.temperature));
if (x.heat > 0.5) { $('#heat').addClass("ds-led-heat-active"); } else { $('#heat').removeClass("ds-led-heat-active"); }
if (x.cool > 0.5) { $('#cool').addClass("ds-led-cool-active"); } else { $('#cool').removeClass("ds-led-cool-active"); }
if (x.air > 0.5) { $('#air').addClass("ds-led-air-active"); } else { $('#air').removeClass("ds-led-air-active"); }
if (x.temperature > 45) { $('#hazard').addClass("ds-led-hazard-active"); } else { $('#hazard').removeClass("ds-led-hazard-active"); }
state_last = state;
}
};
// Control Socket ////////////////////////////////
ws_control.onopen = function()
{
};
ws_control.onmessage = function(e)
{
//Data from Simulation
console.log (e.data);
x = JSON.parse(e.data);
graph.live.data.push([x.runtime, x.temperature]);
graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions());
}
// Storage Socket ///////////////////////////////
ws_storage.onopen = function()
{
ws_storage.send('GET');
};
ws_storage.onmessage = function(e)
{
message = JSON.parse(e.data);
if(message.resp)
{
if(message.resp == "FAIL")
{
if (confirm('Overwrite?'))
{
message.force=true;
console.log("Sending: " + JSON.stringify(message));
ws_storage.send(JSON.stringify(message));
}
else
{
//do nothing
}
}
return;
}
//the message is an array of profiles
//FIXME: this should be better, maybe a {"profiles": ...} container?
profiles = message;
//delete old options in select
$('#e2').find('option').remove().end();
// fill select with new options from websocket
for (var i=0; i<profiles.length; i++)
{
var profile = profiles[i];
//console.log(profile.name);
$('#e2').append('<option value="'+i+'">'+profile.name+'</option>');
if (profile.name == selected_profile_name)
{
selected_profile = i;
$('#e2').select2('val', i);
updateProfile(i);
}
}
};
$("#e2").select2(
{
placeholder: "Select Profile",
allowClear: false,
minimumResultsForSearch: -1
});
$("#e2").on("change", function(e)
{
updateProfile(e.val);
});
}
});