1 <?xml version="1.0" encoding="utf-8"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4 <html xml:lang="en" lang="en" xmlns="http://www.w3.org/1999/xhtml">
11 var tmin, tmax, tfact;
12 var xzero = 36, yzero = 24;
13 var cold_d = [], hot_d = [];
15 function showdate(utime) {
16 var dt = new Date(utime*1000);
17 return dt.toLocaleDateString() + " " + dt.toLocaleTimeString();
20 function getcomb(lo, hi) {
21 var comb = [], lb = [];
23 var ord = Math.pow(10, Math.floor(Math.log10(d)));
24 var scl = Math.floor(d / ord);
25 var inc, inc2, first, x, lb;
27 if (scl < 2) { inc = 0.1; inc2 = 0.2; }
28 else if (scl < 5) { inc = 0.1; inc2 = 0.2; }
29 else { inc = 0.5; inc2 = 1; }
32 first = (Math.floor(lo / inc) + 1) * inc;
33 for (x = 0; x < (d / inc) - 1.2; x++)
34 comb.push(first + inc * x);
35 first = (Math.floor(lo / inc2) + 1) * inc2;
36 for (x = 0; x < (d / inc2) - 1.2; x++)
37 lb.push(first + inc2 * x);
38 //dbg.innerHTML = "ord=" + ord + "<br>inc=" + inc + "<br>"
39 // + comb + "<br>" + lb;
44 return xzero + ((x - tmin) * tfact);
48 return wh - yzero - (y * hfact);
53 ctx.moveTo(px(tmin), py(0));
54 ctx.lineTo(px(tmax), py(0));
55 ctx.strokeStyle = "black";
58 ctx.fillStyle = "black";
59 ctx.font = "bold 12px Courier";
60 ctx.textAlign = "left";
61 ctx.fillText(showdate(tmin), px(tmin), py(0) + 20);
62 ctx.textAlign = "right";
63 ctx.fillText(showdate(tmax), px(tmax), py(0) + 20);
67 var comb = getcomb(0, hmax);
71 for (i = 0; comb[0][i]; i++) {
72 ctx.moveTo(px(tmin) - 5, py(comb[0][i]));
73 ctx.lineTo(px(tmax), py(comb[0][i]));
75 ctx.strokeStyle = "lightgray";
79 ctx.moveTo(px(tmin), py(0));
80 ctx.lineTo(px(tmin), py(hmax));
81 ctx.strokeStyle = "black";
84 ctx.fillStyle = "black";
85 ctx.font = "bold 12px Courier";
86 ctx.textAlign = "right";
87 ctx.fillText(0, px(tmin) - 6, py(0));
88 for (i = 0; comb[1][i]; i++) {
89 ctx.fillText(comb[1][i].toFixed(1), px(tmin) - 6, py(comb[1][i]));
91 ctx.textAlign = "left";
92 ctx.fillText("l/h", px(tmin) + 4, py(hmax) + 8);
95 /* @ updates global var `hmax` */
96 function differentiate(times) {
100 for (i = 0; i < times.length - 1; i++) {
101 dv = times[i+1][1] - times[i][1];
102 dt = times[i+1][0] - times[i][0];
103 if (dt != 0 && dv != 0) {
104 v = (dv / dt) * 360 ; /* Litres per hour */
105 if (hmax < v) hmax = v;
106 res.push([times[i][0], v]);
109 if (i) res.push([times[i][0], v]);
114 function drawplot(data, color) {
118 ctx.moveTo(px(data[0][0]), py(data[0][1]));
119 for (i = 1; i < data.length; i++) {
120 ctx.lineTo(px(data[i][0]), py(data[i - 1][1]));
121 ctx.lineTo(px(data[i][0]), py(data[i][1]));
123 ctx.strokeStyle = color;
127 function showloading() {
128 ctx.fillStyle = "green";
129 ctx.font = "bold 16px Courier";
130 ctx.textAlign="center";
131 ctx.fillText("...loading...", (ww / 2) , (wh / 2) + 8);
134 function showempty() {
135 ctx.fillStyle = "red";
136 ctx.font = "bold 24px Courier";
137 ctx.textAlign="center";
138 ctx.fillText("No data for the requested time interval",
139 (ww / 2) , (wh / 2) + 8);
142 function clearplot() {
143 ctx.clearRect(0, 0, ww, wh);
148 if (cold_d.length || hot_d.length) {
149 tfact = (ww - xzero) / (tmax - tmin);
150 hfact = (wh - yzero) / hmax;
153 drawplot(cold_d, "blue");
154 drawplot(hot_d, "red");
160 function gotdata(data) {
161 document.getElementById("cold").innerHTML =
162 (data.current.cold / 100).toFixed(2);
163 document.getElementById("hot").innerHTML =
164 (data.current.hot / 100).toFixed(2);
165 tmin = data.range.lo;
166 tmax = data.range.hi;
167 //dbg.innerHTML = "from " + tmin + " to " + tmax
168 // + "<br>from " + showdate(tmin) + " to " + showdate(tmax);
169 /* differetiate() updates hmax */
171 cold_d = differentiate(data.cold);
172 hot_d = differentiate(data.hot);
173 //dbg.innerHTML = "hmax=" + hmax + " hfact=" + hfact + "<br>"
174 // + cold_d + "<br>" + hot_d;
178 function sendreq(qstr) {
179 var url = "query.cgi" + qstr;
180 var xmlhttp = new XMLHttpRequest();
182 //dbg.innerHTML = url;
183 xmlhttp.onreadystatechange = function() {
184 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
185 // dbg.innerHTML = xmlhttp.responseText;
186 var myData = JSON.parse(xmlhttp.responseText);
190 xmlhttp.open("GET", url, true);
196 function iso2qu(idate) {
197 return idate.replace("T", "+").replace("0Z", "");
200 function sendquery(lo, hi) {
201 return sendreq("?lo=" + iso2qu(lo) + "&hi=" + iso2qu(hi));
205 ww = window.innerWidth - 4;
206 if (ww > window.innerHeight) ww = window.innerHeight;
210 canvas.style.width = ww + "px";
211 canvas.style.height = wh + "px";
215 function daystart(date) {
216 date.setMilliseconds(0);
223 function prevweek() {
224 var tdy = daystart(new Date());
225 var dow = tdy.getDay();
228 wstart = new Date(1*tdy - 86400000 * (dow + 7));
229 wend = new Date(1*wstart + 86400000 * 7);
230 sendquery(wstart.toISOString(), wend.toISOString());
233 function thisweek() {
234 var tdy = daystart(new Date());
235 var dow = tdy.getDay();
238 wstart = new Date(1*tdy - 86400000 * dow);
239 wend = new Date(1*wstart + 86400000 * 7);
240 sendquery(wstart.toISOString(), wend.toISOString());
243 function beforeyesterday() {
244 var tdy = daystart(new Date());
245 var ytd = new Date(1*tdy - 86400000);
246 var byd = new Date(1*ytd - 86400000);
247 sendquery(byd.toISOString(), ytd.toISOString());
250 function yesterday() {
251 var tdy = daystart(new Date());
252 var ytd = new Date(1*tdy - 86400000);
253 sendquery(ytd.toISOString(), tdy.toISOString());
257 var tdy = daystart(new Date());
258 var tmr = new Date(1*tdy + 86400000);
259 sendquery(tdy.toISOString(), tmr.toISOString());
262 function initialize() {
263 var qstr = window.location.search;
265 dbg = document.getElementById("debug");
266 canvas = document.getElementById("plot");
267 ctx = canvas.getContext("2d");
269 if (qstr) sendreq(qstr);
272 document.getElementById("today").onclick = today;
273 document.getElementById("yesterday").onclick = yesterday;
274 document.getElementById("beforeyesterday").onclick = beforeyesterday;
275 document.getElementById("thisweek").onclick = thisweek;
276 document.getElementById("prevweek").onclick = prevweek;
280 window.onload = initialize;
281 window.onresize = resize;
285 font-family: PipeDream;
286 src: url('PIPED.TTF') format('truetype');
287 /* Free to use font from http://www.mlink.net/~paterson/jpfonts.htm */
292 font-family: PipeDream;
295 background-color: lightgray;
311 border: solid 1px black;
316 background-color: #d0e0ff;
320 background-color: #ffd0e0;
328 /* border: solid 1px black; */
337 display: inline-block;
341 border: solid 1px black;
343 background-color: lightgray;
351 transform: translate(0, -50%);
357 <title>Water Meters</title>
359 <h1>WATER METERS</h1>
360 <div id="currentvals">
362 <div class="current" id="cold">cold</div>
363 <div class="current" id="hot">hot</div>
366 <canvas id="plot" width="640" height = "320"></canvas>
369 <div class="query" id="prevweek"><div class="label">PREVIOUS WEEK</div></div>
370 <div class="query" id="beforeyesterday"><div class="label">DAY
371 BEFORE YESTERDAY</div></div>
372 <div class="query" id="yesterday"><div class="label">YESTERDAY</div></div>
373 <div class="query" id="today"><div class="label">TODAY</div></div>
374 <div class="query" id="thisweek"><div class="label">THIS WEEK</div></div>
377 <div id="debug"></div>