var state = "IDLE"; var state_last = ""; var graph = [ 'profile', 'live', 'movingProfile']; var points = []; var profiles = []; var time_mode = 0; var selected_profile = 2; var selected_profile_name = 'bisque'; var temp_scale = "c"; var time_scale_slope = "m"; var time_scale_profile = "m"; var time_scale_long = "Minutes"; var temp_scale_display = "C"; var kwh_rate = 0.26; var currency_type = "AUD"; 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_config = new WebSocket(host+"/config"); 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 }; graph.movingProfile = { label: "Live Profile", data: [], points: { show: false }, color: "#ffd300", draggable: false }; function updateProfile(id) { selected_profile = id; selected_profile_name = profiles[id].name; var job_seconds = profiles[id].data.length === 0 ? 0 : parseInt(profiles[id].data[profiles[id].data.length-1][0]); var kwh = (3850*job_seconds/3600/1000).toFixed(2); var cost = (kwh*kwh_rate).toFixed(2); var job_time = new Date(job_seconds * 1000).toISOString().substr(11, 8); $('#sel_prof').html(profiles[id].name); $('#sel_prof_eta').html(job_time); $('#sel_prof_cost').html(kwh + ' kWh ('+ currency_type +': '+ cost +')'); graph.profile.data = profiles[id].data; graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ] , getOptions()); } function deleteProfile() { var profile = { "type": "profile", "data": "", "name": selected_profile_name }; var delete_struct = { "cmd": "DELETE", "profile": profile }; var delete_cmd = JSON.stringify(delete_struct); console.log("Delete profile:" + selected_profile_name); ws_storage.send(delete_cmd); ws_storage.send('GET'); selected_profile_name = profiles[0].name; state="IDLE"; $('#edit').hide(); $('#profile_selector').show(); $('#btn_controls').show(); $('#status').slideDown(); $('#profile_table').slideUp(); $('#e2').select2('val', 0); graph.profile.points.show = false; graph.profile.draggable = false; graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ], getOptions()); } 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 = '

Profile Points

'; html += ''; for(var i=0; i=1) dps = ((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 += ''; html += ''; html += ''; html += ''; html += ''; } html += '
#Target Time in ' + time_scale_long+ 'Target Temperature in °'+temp_scale_display+'Slope in °'+temp_scale_display+'/'+time_scale_slope+'

' + (i+1) + '

 
'; $('#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]); if (graph.profile.data.length > 0) { if (col == 0) { graph.profile.data[row][col] = timeProfileFormatter(value,false); } else { graph.profile.data[row][col] = value; } graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ], getOptions()); } updateProfileTable(); }); } function timeProfileFormatter(val, down) { var rval = val switch(time_scale_profile){ case "m": if (down) {rval = val / 60;} else {rval = val * 60;} break; case "h": if (down) {rval = val / 3600;} else {rval = val * 3600;} break; } return Math.round(rval); } function formatDPS(val) { var tval = val; if (time_scale_slope == "m") { tval = val * 60; } if (time_scale_slope == "h") { tval = (val * 60) * 60; } return Math.round(tval); } function hazardTemp(){ if (temp_scale == "f") { return (45 * 9 / 5) + 32 } else { return 45 } } 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.movingProfile.data = []; graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ] , getOptions()); ws_control.send(JSON.stringify(cmd)); } function runTaskSimulation() { var cmd = { "cmd": "SIMULATE", "profile": profiles[selected_profile] } graph.live.data = []; graph.movingProfile.data = []; graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ] , 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, graph.movingProfile ], getOptions()); updateProfileTable(); } function enterEditMode() { state="EDIT" $('#status').slideUp(); $('#edit').show(); $('#profile_selector').hide(); $('#btn_controls').hide(); console.log(profiles); $('#form_profile_name').val(profiles[selected_profile].name); graph.profile.points.show = true; graph.profile.draggable = true; graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ], 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, graph.movingProfile ], 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, graph.movingProfile ], getOptions()); updateProfileTable(); } function delPoint() { graph.profile.data.splice(-1,1) graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ], 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 last) { data.push([rawdata[i][0], rawdata[i][1]]); } else { $.bootstrapGrowl(" ERROR 88:
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: { min: 0, 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"); $('

Oh no, you need a browser that supports WebSockets. How about Google Chrome?

').appendTo('#container'); } else { // Status Socket //////////////////////////////// ws_status.onopen = function() { console.log("Status Socket has been opened"); $.bootstrapGrowl(" Initialising
Give me a minute to get things together", { 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(" ERROR 1:
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.movingProfile.data.push([v.runtime, v.target]); graph.plot = $.plot("#graph_container", [ graph.profile, graph.live ] , getOptions()); }); } if(state!="EDIT") { state = x.state; if (state!=state_last) { if(state_last == "RUNNING") { $('#target_temp').html('---'); updateProgress(0); $.bootstrapGrowl("Firing completed", { 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.movingProfile.data.push([x.runtime, x.target]); graph.plot = $.plot("#graph_container", [ graph.profile, graph.live, graph.movingProfile ] , getOptions()); timeElapsed = new Date(parseInt(x.runtime) * 1000).toISOString().substr(11, 8); left = parseInt(x.totaltime-x.runtime); if (left < 0) { left = 0; } eta = new Date(left * 1000).toISOString().substr(11, 8); updateProgress(parseFloat(x.runtime)/parseFloat(x.totaltime)*100); $('#state').html('' + eta + ''); $('#timeElapsed').html('' + timeElapsed + ''); $('#target_temp').html(parseInt(x.target)); } else { $("#nav_start").show(); $("#nav_stop").hide(); $('#state').html('

'+state+'

'); $('#timeElapsed').html('

'+state+'

'); } $('#act_temp').html(parseInt(x.temperature)); if (x.heat > 0.5) { $('#heat').addClass("ds-led-heat-active"); } else { $('#heat').removeClass("ds-led-heat-active"); } // the commented out icons below were from the old reflow oven code // I don't worry about them for my kiln, but you might find a use for them some time // 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 > hazardTemp()) { $('#hazard').addClass("ds-led-hazard-active"); } else { $('#hazard').removeClass("ds-led-hazard-active"); } // if ((x.door == "OPEN") || (x.door == "UNKNOWN")) { $('#door').addClass("ds-led-door-open"); } else { $('#door').removeClass("ds-led-door-open"); } state_last = state; } }; // Config Socket ///////////////////////////////// ws_config.onopen = function() { ws_config.send('GET'); }; ws_config.onmessage = function(e) { console.log (e.data); x = JSON.parse(e.data); temp_scale = x.temp_scale; time_scale_slope = x.time_scale_slope; time_scale_profile = x.time_scale_profile; kwh_rate = x.kwh_rate; currency_type = x.currency_type; if (temp_scale == "c") {temp_scale_display = "C";} else {temp_scale_display = "F";} $('#act_temp_scale').html('º'+temp_scale_display); $('#target_temp_scale').html('º'+temp_scale_display); switch(time_scale_profile){ case "s": time_scale_long = "Seconds"; break; case "m": time_scale_long = "Minutes"; break; case "h": time_scale_long = "Hours"; break; } } // 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, graph.movingProfile ] , 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(); // check if current selected value is a valid profile name // if not, update with first available profile name var valid_profile_names = profiles.map(function(a) {return a.name;}); if ( valid_profile_names.length > 0 && $.inArray(selected_profile_name, valid_profile_names) === -1 ) { selected_profile = 0; selected_profile_name = valid_profile_names[0]; } // fill select with new options from websocket for (var i=0; i'+profile.name+''); if (profile.name == selected_profile_name) { selected_profile = i; $('#e2').select2('val', i); updateProfile(i); } } }; $("#e2").select2( { placeholder: "Select Profile", allowClear: true, minimumResultsForSearch: -1 }); $("#e2").on("change", function(e) { updateProfile(e.val); }); } });