LaundrySorcery

Log | Files | Refs

commit 69625650b100421fb66e0c53ea8dcf69e6d001a7
parent 77ad500002b44170b556dfac2d28afbfb38e7e33
Author: Dominik Schmidt <das1993@hotmail.com>
Date:   Thu, 28 Jun 2018 23:58:01 +0000

Implement guess of time left until washing machine finished in web-interface

Diffstat:
www/index.cgi | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+), 0 deletions(-)

diff --git a/www/index.cgi b/www/index.cgi @@ -20,6 +20,9 @@ Content-Type: text/html text-align:center; font-size:x-large; } + #status.off guessTime{ + display:none; + } #power{ font-size:xx-large; } @@ -43,6 +46,7 @@ Content-Type: text/html <div id="status"> <div id="power"><img src="off.svg" /></div> <div id="delta_t"></div> + <div id="guessTime"></div> </div> <div id="footer"> <a href="details.html">Details</a> @@ -51,9 +55,11 @@ Content-Type: text/html <!-- delta_t=${RTIME}; function updateDisplay(){ + var g=document.getElementById("guessTime"); var dt=document.getElementById("delta_t"); var st=document.getElementById("status"); var txt=""; + var txt2=""; var classn=""; if(delta_t < 0){ txt="since "+timeSince(-delta_t); @@ -61,14 +67,86 @@ Content-Type: text/html } else{ txt="since "+timeSince(delta_t); + txt2="The crystal ball guesses: "+timeSince(Math.round(guessTimeLeft(delta_t)))+" left" classn="on"; } dt.textContent=txt; + g.textContent=txt2; st.setAttribute("class",classn); st.getElementsByTagName("img")[0].setAttribute("src",classn+".svg"); } setInterval(function(){ if(delta_t>=0){delta_t++}else{delta_t--}; updateDisplay();}, 1000); setInterval(function(){ readTextFile("print_delta_t.cgi", function(rawFile){delta_t = rawFile.responseText});}, 5*1000); + readTextFile("clusters", function(rawFile){parseClusters(rawFile.responseText);}); + clusters=[]; + function Cluster(apriori, mean, variance){ + this.apriori=apriori; + this.mean=mean; + this.variance=variance; + } + function parseClusters(txt){ + var lines=txt.split("\n"); + clusters=[]; + var sum=0; + for(var i=0; i<lines.length; i++){ + var line=lines[i]; + if(line.length==0) continue; + var spl=line.split("\t"); + clusters.push(new Cluster(parseInt(spl[0]),parseFloat(spl[1]),parseFloat(spl[2]))); + sum+=parseInt(spl[0]); + } + for(var i=0; i<clusters.length; i++){ + clusters[i].apriori/=sum; + } + } + + function guessTimeLeft(elapsed){ + return guessTime(elapsed)-elapsed; + } + + function guessTime(elapsed){ + var expect=0; + var obsprior=observationPrior(elapsed); + for(var i=0; i<clusters.length; i++){ + cluster=clusters[i]; + var prior=(1-cluster.cdf(elapsed))*cluster.apriori/obsprior; + expect+=prior*Math.max(cluster.mean,elapsed); + } + return expect; + } + + function observationPrior(elapsed){ + var prior=0; + for(var i=0; i<clusters.length; i++){ + cluster=clusters[i]; + prior+=(1-cluster.cdf(elapsed))*cluster.apriori + } + return prior; + } + + Cluster.prototype.cdf=function(to) { + variance=this.variance; + mean=this.mean; + var z = (to-mean)/Math.sqrt(2*variance); + var t = 1/(1+0.3275911*Math.abs(z)); + var a1 = 0.254829592; + var a2 = -0.284496736; + var a3 = 1.421413741; + var a4 = -1.453152027; + var a5 = 1.061405429; + var erf = 1-(((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*Math.exp(-z*z); + var sign = 1; + if(z < 0){ + sign = -1; + } + return (1/2)*(1+sign*erf); + } + + Cluster.prototype.pdf=function(to){ + mean=this.mean; + variance=this.variance; + return 1/(Math.sqrt(2*pi*variance))*Math.exp(-((to-mean)^2/(4*variance))); + } function readTextFile(file, func){ var rawFile = new XMLHttpRequest(); rawFile.open("GET", file, true);