From 2c9b6c09c99eba4939ea1617036ba5bae6889b2a Mon Sep 17 00:00:00 2001 From: Yves Fischer Date: Wed, 7 Dec 2011 23:27:27 +0100 Subject: fix timezone handling --- ebus/webapp/__init__.py | 47 +++++++++++++++++----------- ebus/webapp/static/console.html | 68 ++++++++++++++++++++--------------------- ebus/webapp/static/src/ebus.js | 6 ++++ 3 files changed, 69 insertions(+), 52 deletions(-) diff --git a/ebus/webapp/__init__.py b/ebus/webapp/__init__.py index d0e0ff6..9c98ac3 100644 --- a/ebus/webapp/__init__.py +++ b/ebus/webapp/__init__.py @@ -9,13 +9,16 @@ from sqlalchemy import text app = bottle.Bottle("ebus") -def maketime(datetime): - """rechnet zeit so um wie Javascript es braucht XXX""" - return (time.mktime(datetime.timetuple())+time.altzone*-1)*1000 +def maketime(dt): + """Rechnet CET DateTime in CET timestamp""" + return time.mktime(dt.timetuple())*1000 # - time.altzone)*1000 def parsetime(timestamp): - """macht aus javascript zeit systemzeit""" - return ((timestamp/1000) - time.altzone*-1) + """Machtaus CET timestamp Local DateTime""" + return datetime.datetime.fromtimestamp((timestamp/1000)) # + time.altzone) + +def now(conn): + return conn.execute(text("SELECT CURRENT_TIMESTAMP t")).first().t @app.route('/') def index_file(): @@ -108,11 +111,12 @@ import psycopg2 @app.route('/stream') @app.route('/stream/:startdate') def stream(soup, startdate=None): - time_start = startdate != None and datetime.datetime.fromtimestamp(parsetime(float(startdate))) \ - or datetime.datetime.now() - connection = soup.connection() conn = connection.connection + + time_start = startdate != None and parsetime(float(startdate)) \ + or now(connection) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) @@ -120,14 +124,20 @@ def stream(soup, startdate=None): cursor.execute("LISTEN evt_ebus_value_insert;") values = [] - if select.select([conn],[],[],10) == ([],[],[]): - time_stop = datetime.datetime.now() - else: - time_stop = datetime.datetime.now() - conn.poll() - - notify = conn.notifies.pop() - if notify: + fails = 0 + while fails < 5: + if select.select([conn],[],[],10) == ([],[],[]): + time_stop = now(connection) + fails += 1 + else: + conn.poll() + + notify = conn.notifies.pop() + if not notify: + continue + + time_stop = now(connection) + print "time_stop %s"%time_stop sql = text("""SELECT sensor.name, value.timestamp, COALESCE(value.value_int,value.value_float) "value_real", @@ -144,6 +154,8 @@ def stream(soup, startdate=None): }, connection.execute(sql, time_start=time_start, time_stop=time_stop)) + break + cursor.close() return {'time_start' : maketime(time_start), 'time_stop':maketime(time_stop), 'data':values} @@ -166,6 +178,7 @@ def all_values(soup): AND value.sensor_id = last_value.sensor_id AND value.sensor_id = sensor.id""") + time_stop = now(conn) values = map(lambda row: { "name":row.name, "timestamp":maketime(row.timestamp), @@ -173,6 +186,6 @@ def all_values(soup): "value_string":row.value_string}, conn.execute(sql)) - return {'data':values} + return {'data':values, 'time_stop':maketime(time_stop)} # vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=python diff --git a/ebus/webapp/static/console.html b/ebus/webapp/static/console.html index b8b732b..71a861c 100644 --- a/ebus/webapp/static/console.html +++ b/ebus/webapp/static/console.html @@ -36,18 +36,10 @@ "tempkollektorWert" : "heizkreisregler9.solarDaten.tempKollektor", "boilerWert" : "feuerungsautomat1.betriebsdatenRegler1.boilerTemperatur" }; - function timeToString(timestamp) { - var d = new Date(); - return dateToString(d); - } - function dateToString(d) { - var f = d3.format("02d"); - return f(d.getHours()) + ":" + f(d.getMinutes()) + ":" + f(d.getSeconds()); - } - function log(data) { + function process(data) { for (var i in data) { var row = data[i]; - var caption = "[" + timeToString(row.timestamp) + "] " + row.name + " Value: " + row.value_real + " - " + row.value_string; + var caption = "[" + d3.format("02d")(row.timestamp) + "] " + row.name + " Value: " + row.value_real + " - " + row.value_string; d3.select("#log").insert("div", "div").text(caption); @@ -62,19 +54,20 @@ function reload(time_stop) { d3.json("../stream/" + time_stop, function(resp) { - log(resp.data); + process(resp.data); reload(resp.time_stop); }); } d3.xml("draw.svg", "image/svg+xml", function(xml) { - // Put SVG Data into the DOM + // Write SVG Data into the DOM d3.select("#image")[0][0].appendChild(xml.documentElement); + // Create Popup for (var mapping_element_id in mapping) { d3.select("#" + mapping_element_id).on("click", function() { var elem = d3.select( this ); - var sensor_name = mapping[elem.attr("id")]; + var sensor_name = mapping[elem.attr("id")]; var popup = d3.select("body") .append("div") .classed("popup", true) @@ -86,7 +79,7 @@ popup.append("div") .text(sensor_name); - var w = 550, h = 200, margin = 20; + var w = 550, h = 200, margin = 30; var plot = popup.append("svg:svg") .classed("plot", true) .attr("width", w) @@ -108,14 +101,15 @@ y_min = d3.min(data, function(d){return d[1]}), y_max = d3.max(data, function(d){return d[1]}); - var y = d3.scale.linear() - .domain([y_min, y_max]) - .range([0+margin, h-margin]); - var x = d3.time.scale() .domain([x_min, x_max]) .range([0+margin, w-margin]); - + + var y = d3.scale.linear() + .domain([y_min, y_max]) + .range([0+margin, h]); + + var g = plot.append("svg:g") .attr("transform", "translate(0, "+h+")"); @@ -126,21 +120,15 @@ g.append("svg:path") .attr("d", line(data)); - // Axes + // Axes X-Line g.append("svg:line") - .attr("x1", x(x_min)) - .attr("y1", -1 * y(y_min)) + .attr("x1", 0) + .attr("y1", -1*y(y_min)) .attr("x2", x(x_max)) .attr("y2", -1 * y(y_min)); - - g.append("svg:line") - .attr("x1", x(x_min)) - .attr("y1", -1*y(y_min)) - .attr("x2", x(x_min)) - .attr("y2", -1*y(y_max)); - // Ticks + // Tick Labels var tick_format = d3.time.format("%d.%m"); g.selectAll(".xlabel") .data(x.ticks(5)).enter() @@ -158,18 +146,28 @@ .attr("y", function(d) {return -1*y(d)}) .attr("text-anchor", "right"); + // X-Lines + g.selectAll(".xlines") + .data(x.ticks(5)).enter() + .append("svg:line") + .style("fill", "none") + .style("stroke", "#000000") + .style("stroke-width", 0.5) + .style("stroke-dasharray", "6,1") + .attr("x1", function(d){return x(d)}) + .attr("y1", -1*y(y_min)+(margin/2)) + .attr("x2", function(d){return x(d)}) + .attr("y2", -1*y(y_max)); + + // wait_indicator.remove(); }); }); } d3.json("../all_values", function(resp) { - log(resp.data); - - d3.json("../stream", function(resp) { - log(resp.data); - reload(resp.time_stop); - }); + process(resp.data); + reload(resp.time_stop); }); }); diff --git a/ebus/webapp/static/src/ebus.js b/ebus/webapp/static/src/ebus.js index ba14f51..46a9ed8 100644 --- a/ebus/webapp/static/src/ebus.js +++ b/ebus/webapp/static/src/ebus.js @@ -66,10 +66,15 @@ $(document).ready(function(){ unplotSensorDetail(sensorname); unplotSensorOverview(sensorname); }; + var tzFix = function(d) { + return d - new Date().getTimezoneOffset() * 60 * 1000; + } + var plotSensorDetail = function(sensorConfig) { $.getJSON("sensor/"+escape(sensorConfig.sensorname)+"/"+from+"/"+to, function(response) { if (response['data']) { + response.data = response.data.map(function(d) { return [ tzFix(d[0]), d[1] ]; }); datasetDetail.push({'data':response['data'], 'label':sensorConfig.sensorname, 'color':sensorConfig.color}); @@ -94,6 +99,7 @@ $(document).ready(function(){ $.getJSON("sensor_cached/"+escape(sensorConfig.sensorname)+"/"+fromOverview, //+"/"+to, function(response) { if (response['data']) { + response.data = response.data.map(function(d) { return [ tzFix(d[0]), d[1] ]; }); datasetOverview.push({'data':response['data'], 'label':sensorConfig.sensorname, 'color':sensorConfig.color}); -- cgit v1.2.1