kopia lustrzana https://github.com/jamesgao/kiln_controller
testing temperature streaming
rodzic
0459d84a00
commit
769202e41b
|
@ -1,19 +1,46 @@
|
|||
import time
|
||||
import json
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
|
||||
cwd = os.path.split(os.path.abspath(__file__))[0]
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
def initialize(self, monitor):
|
||||
self.monitor = monitor
|
||||
def get(self):
|
||||
self.write("<!doctype html><head><meta http-equiv='refresh' content='5' ></head><p>Current temperature: %.2f°C, %.2f°F"%read_temp())
|
||||
class ClientSocket(websocket.WebSocketHandler):
|
||||
def initialize(self, parent):
|
||||
self.parent = parent
|
||||
|
||||
def open(self):
|
||||
self.parent.sockets.append(self)
|
||||
|
||||
def on_close(self):
|
||||
self.parent.sockets.remove(self)
|
||||
|
||||
class WebApp(object):
|
||||
def __init__(self, handlers, port=8888):
|
||||
self.handlers = [
|
||||
(r"/ws/", ClientSocket, dict(parent=self)),
|
||||
(r"/(.*)", tornado.web.StaticFileHandler, dict(path=cwd)),
|
||||
]
|
||||
self.sockets = []
|
||||
self.port = port
|
||||
|
||||
def send(self, data):
|
||||
jsondat = json.dumps(data)
|
||||
for sock in self.sockets:
|
||||
socket.write_message(jsondat)
|
||||
|
||||
def run(self):
|
||||
self.app = tornado.web.Application(self.handlers, gzip=True)
|
||||
self.app.listen(8888)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r"/", MainHandler, dict(monitor=mon)),
|
||||
])
|
||||
import thermo
|
||||
monitor = thermo.Monitor()
|
||||
|
||||
application.listen(8888)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
app = WebApp([])
|
||||
def send_temp(time, temp):
|
||||
app.send(dict(time=time, temp=temp))
|
||||
monitor.callback = send_temp
|
||||
monitor.start()
|
||||
app.run()
|
||||
|
|
|
@ -5,7 +5,6 @@ var tempgraph = (function(module) {
|
|||
show_axes: true,
|
||||
}
|
||||
module.Graph = function(options) {
|
||||
//Options: margin, width, height, object
|
||||
if (options === undefined)
|
||||
options = module.graph_defaults;
|
||||
|
||||
|
@ -19,51 +18,69 @@ var tempgraph = (function(module) {
|
|||
this.height = options.height ? options.height : $(this.obj).height() - this.margin.top - options.margin.bottom;
|
||||
|
||||
this.svg = d3.select(this.obj);
|
||||
this.svg.append("defs").append("rect")
|
||||
.attr("class", "pane")
|
||||
.attr("width", this.width)
|
||||
.attr("height", this.height);
|
||||
this.axes = this.svg.append("g")
|
||||
.attr("class", "axes")
|
||||
this.svg.append("defs").append("clipPath").attr("id", "pane")
|
||||
.append("rect")
|
||||
.attr("width", this.width)
|
||||
.attr("height", this.height);
|
||||
|
||||
var xfm = this.svg.append("g")
|
||||
.attr("transform", "translate("+this.margin.left+","+this.margin.top+")")
|
||||
.attr("clip-path", "url(#pane)");
|
||||
|
||||
/*xfm.append("rect")
|
||||
.attr("style", "fill:#DDD")
|
||||
.attr("width", this.width)
|
||||
.attr("height", this.height);*/
|
||||
|
||||
this.x = d3.time.scale().range([0, this.width]);
|
||||
this.y = d3.scale.linear().range([this.height, 0]);
|
||||
|
||||
this.zoom = d3.behavior.zoom().on("zoom", this.draw.bind(this));
|
||||
this.zoom = d3.behavior.zoom().on("zoom", this.draw.bind(this))
|
||||
.on("zoomend", this.recenter.bind(this));
|
||||
|
||||
if (options.show_axes === undefined || options.show_axes) {
|
||||
this.x_axis = d3.svg.axis().scale(this.x).orient("bottom");
|
||||
this.y_axis = d3.svg.axis().scale(this.y).orient("left");
|
||||
this.x_axis = d3.svg.axis().scale(this.x).orient("bottom")
|
||||
.tickSize(this.height).tickSubdivide(true);
|
||||
this.y_axis = d3.svg.axis().scale(this.y).orient("left")
|
||||
.tickSize(this.width).tickSubdivide(true);
|
||||
|
||||
//setup axies labels and ticks
|
||||
this.axes.append("g")
|
||||
xfm.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + this.height + ")")
|
||||
//.attr("transform", "translate(0," + this.height + ")")
|
||||
.call(this.x_axis);
|
||||
|
||||
this.axes.append("g")
|
||||
xfm.append("g")
|
||||
.attr("class", "y axis")
|
||||
.attr("transform", "translate("+this.width+", 0)")
|
||||
.call(this.y_axis)
|
||||
.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("class", "ylabel")
|
||||
.attr("transform", "translate(-"+this.width+",0)rotate(-90)")
|
||||
.attr("y", 6)
|
||||
.attr("dy", ".71em")
|
||||
.style("text-anchor", "end")
|
||||
.text("Temperature (°F)");
|
||||
|
||||
}
|
||||
|
||||
this.axes = xfm.append("g")
|
||||
.attr("class", "axes")
|
||||
.attr("style", "clip-path:url(#pane)");
|
||||
window.onresize = this.resize.bind(this);
|
||||
this.lines = [];
|
||||
this.lines = {};
|
||||
};
|
||||
module.Graph.prototype.plot = function(data, marker) {
|
||||
module.Graph.prototype.plot = function(data, className, marker) {
|
||||
this.x.domain(d3.extent(data, function(d) { return d.x; }));
|
||||
this.y.domain(d3.extent(data, function(d) { return d.y; }));
|
||||
this.zoom.x(this.x);
|
||||
|
||||
var line = d3.svg.line()
|
||||
.x(function(d) { return this.x(d.x); }.bind(this))
|
||||
.y(function(d) { return this.y(d.y); }.bind(this));
|
||||
|
||||
this.axes.append("path")
|
||||
.datum(data)
|
||||
.attr("class", "line")
|
||||
.attr("class", className)
|
||||
.attr("d", line);
|
||||
|
||||
if (marker !== undefined && marker) {
|
||||
|
@ -75,11 +92,8 @@ var tempgraph = (function(module) {
|
|||
.attr("cx", function(d) { return this.x(d.x); }.bind(this))
|
||||
.attr("cy", function(d) { return this.y(d.y); }.bind(this));
|
||||
}
|
||||
this.lines.push({line:line, data:data, marker:marker});
|
||||
|
||||
this.x.domain(d3.extent(data, function(d) { return d.x; }));
|
||||
this.y.domain(d3.extent(data, function(d) { return d.y; }));
|
||||
this.zoom.x(this.x);
|
||||
this.lines[className] = {line:line, data:data, marker:marker};
|
||||
this.svg.call(this.zoom);
|
||||
this.draw();
|
||||
return line;
|
||||
|
@ -88,18 +102,17 @@ var tempgraph = (function(module) {
|
|||
this.svg.select("g.x.axis").call(this.x_axis);
|
||||
this.svg.select("g.y.axis").call(this.y_axis);
|
||||
var line, data, marker;
|
||||
for (var i = 0; i < this.lines.length; i++) {
|
||||
line = this.lines[i].line;
|
||||
data = this.lines[i].data;
|
||||
marker = this.lines[i].marker;
|
||||
for (var name in this.lines) {
|
||||
line = this.lines[name].line;
|
||||
data = this.lines[name].data;
|
||||
marker = this.lines[name].marker;
|
||||
if (marker !== undefined) {
|
||||
this.svg.selectAll(".dot").data(data)
|
||||
.attr("cx", function(d) { return this.x(d.x)}.bind(this))
|
||||
.attr("cy", function(d) { return this.y(d.y)}.bind(this));
|
||||
}
|
||||
this.svg.select("path.line").attr("d", line);
|
||||
this.svg.select("path."+name).attr("d", line);
|
||||
}
|
||||
console.log("draw");
|
||||
}
|
||||
module.Graph.prototype.resize = function() {
|
||||
var margin = this.margin;
|
||||
|
@ -109,54 +122,48 @@ var tempgraph = (function(module) {
|
|||
this.svg
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
this.svg.select("rect.pane")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
this.x.range([0, width]);
|
||||
this.y.range([height, 0]);
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.draw();
|
||||
console.log("resized");
|
||||
}
|
||||
|
||||
module.Graph.prototype.update = function(line, data) {
|
||||
for (var i = 0; i < this.lines.length; i++) {
|
||||
if (this.lines[i].line === line) {
|
||||
this.axes.select("path.line").datum(data).attr("d", line);
|
||||
}
|
||||
module.Graph.prototype.recenter = function() {
|
||||
var extent = [], data, valid,
|
||||
low = this.x.domain()[0], high=this.x.domain()[1];
|
||||
for (var name in this.lines) {
|
||||
data = this.lines[name].data;
|
||||
valid = data.filter(function(d) { return low <= d.x && d.x <= high; })
|
||||
extent = extent.concat(valid);
|
||||
}
|
||||
this.y.domain(d3.extent(extent, function(d) {return d.y;}));
|
||||
this.draw();
|
||||
}
|
||||
module.Graph.prototype.update = function(className, data) {
|
||||
this.lines[className].data = data;
|
||||
this.axes.select("path."+className).datum(data)
|
||||
.attr("d", this.lines[className].line);
|
||||
this.draw();
|
||||
}
|
||||
module.Graph.prototype.xlim = function(min, max) {
|
||||
if (min === undefined)
|
||||
return this.x.domain();
|
||||
|
||||
this.x.domain([min, max]);
|
||||
this.draw();
|
||||
}
|
||||
module.Graph.prototype.ylim = function(min, max) {
|
||||
if (min === undefined)
|
||||
return this.y.domain();
|
||||
|
||||
this.y.domain([min, max]);
|
||||
this.draw();
|
||||
}
|
||||
module.Graph.prototype.ylabel = function(text) {
|
||||
this.svg.select(".ylabel").text(text);
|
||||
}
|
||||
|
||||
return module;
|
||||
}(tempgraph || {}));
|
||||
|
||||
var data = d3.tsv("data.txt", function(error, data) {
|
||||
data.forEach(function(d) {
|
||||
d.x = new Date(d.time*1000.);
|
||||
d.y = +d.temp*9/5+32;
|
||||
});
|
||||
|
||||
graph = new tempgraph.Graph()
|
||||
line = graph.plot(data, false);
|
||||
});
|
||||
|
||||
|
||||
function update() {
|
||||
d3.tsv("data2.txt", function(error, data) {
|
||||
data.forEach(function(d) {
|
||||
d.x = new Date(d.time*1000.);
|
||||
d.y = +d.temp;
|
||||
});
|
||||
|
||||
var lim = d3.extent(data, function(d){return d.x;});
|
||||
graph.xlim(lim[0], lim[1]);
|
||||
graph.update(line, data);
|
||||
console.log("done!");
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
var tempgraph = (function(module) {
|
||||
module.Monitor = function(initial) {
|
||||
this.temperature = initial;
|
||||
//default to F
|
||||
this.scalefunc = module.temp_to_F;
|
||||
this.temp_suffix = "°F"
|
||||
this.temp_prefix = ""
|
||||
this._mapped = this.temperature.map(this._map_temp.bind(this));
|
||||
|
||||
this.graph = new tempgraph.Graph();
|
||||
this.graph.plot(initial.map(this._map_temp.bind(this)), "temperature", false);
|
||||
|
||||
var latest = this.temperature[this.temperature.length-1];
|
||||
this.update_temp({time:latest.x/1000., temp:latest.y});
|
||||
this._bindUI();
|
||||
}
|
||||
module.Monitor.prototype.update_temp = function(data) {
|
||||
var now = new Date(data.time*1000.);
|
||||
var nowstr = now.getHours() % 12 + ":" + now.getMinutes() + (now.getHours() > 12 ? " pm" : " am");
|
||||
var temp = this.scalefunc(data.temp);
|
||||
$("#current_time").text(nowstr);
|
||||
$("#current_temp").text(this.temp_prefix+temp+this.temp_suffix);
|
||||
if (now > this.temperature[this.temperature.length-1].x) {
|
||||
this.temperature.push({x:now, y:+data.temp});
|
||||
this._mapped.push({x:now, y:temp});
|
||||
|
||||
var lims = this.graph.xlim();
|
||||
if (now > lims[1]) {
|
||||
var start = new Date(now.getTime() - lims[1].getTime() + lims[0].getTime());
|
||||
this.graph.xlim(start, now);
|
||||
}
|
||||
this.graph.update("temperature", this._mapped);
|
||||
}
|
||||
|
||||
if (data.output !== undefined) {
|
||||
$("#current_output_text").text(data.output*100+"%");
|
||||
$("#current_output").val(data.output*1000);
|
||||
}
|
||||
}
|
||||
module.Monitor.prototype.update_UI = function(packet) {
|
||||
|
||||
}
|
||||
module.Monitor.prototype.setProfile = function(profile) {
|
||||
}
|
||||
|
||||
|
||||
module.Monitor.prototype.setScale = function(scale) {
|
||||
$("a#temp_scale_C").parent().removeClass("active");
|
||||
$("a#temp_scale_F").parent().removeClass("active");
|
||||
$("a#temp_scale_cone").parent().removeClass("active");
|
||||
if (scale == "C") {
|
||||
$("li a#temp_scale_C").parent().addClass("active");
|
||||
this.scalefunc = module.temp_to_C;
|
||||
this.graph.ylabel("Temperature (°C)")
|
||||
this.temp_suffix = "°C";
|
||||
this.temp_prefix = "";
|
||||
} else if (scale == "F") {
|
||||
$("li a#temp_scale_F").parent().addClass("active");
|
||||
this.scalefunc = module.temp_to_F;
|
||||
this.graph.ylabel("Temperature (°F)")
|
||||
this.temp_suffix = "°F";
|
||||
this.temp_prefix = "";
|
||||
} else if (scale == "cone") {
|
||||
$("li a#temp_scale_cone").parent().addClass("active");
|
||||
this.scalefunc = module.temp_to_cone;
|
||||
this.graph.ylabel("Temperature (Δ)");
|
||||
this.temp_prefix = "Δ";
|
||||
this.temp_suffix = "";
|
||||
}
|
||||
this._mapped = this.temperature.map(this._map_temp.bind(this));
|
||||
this.graph.y.domain(d3.extent(this._mapped, function(d) { return d.y; }));
|
||||
var latest = this.temperature[this.temperature.length-1];
|
||||
this.update_temp({time:latest.x/1000., temp:latest.y});
|
||||
this.graph.update("temperature", this._mapped);
|
||||
}
|
||||
|
||||
module.Monitor.prototype._map_temp = function(d) {
|
||||
return {x:d.x, y:this.scalefunc(d.y)};
|
||||
}
|
||||
module.Monitor.prototype._bindUI = function() {
|
||||
/*
|
||||
var sock = new WebSocket("ws://localhost/socket/", "protocolOne");
|
||||
|
||||
|
||||
sock.onmessage = function(event) {
|
||||
var data = JSON.parse(event.data);
|
||||
this.update(data);
|
||||
}
|
||||
*/
|
||||
$("#temp_scale_C").click(function() { this.setScale("C");}.bind(this));
|
||||
$("#temp_scale_F").click(function() { this.setScale("F");}.bind(this));
|
||||
//$("#temp_scale_C").click(function() { this.setScale("C");}.bind(this));
|
||||
}
|
||||
|
||||
|
||||
module.temp_to_C = function(temp) { return temp; }
|
||||
module.temp_to_F = function(temp) {
|
||||
return temp * 9 / 5 + 32;
|
||||
}
|
||||
module.temp_to_cone = function(temp) {
|
||||
return "Not implemented"
|
||||
}
|
||||
|
||||
return module;
|
||||
}(tempgraph || {}));
|
||||
|
||||
d3.tsv("data.txt", function(error, data) {
|
||||
var newdata = [], d;
|
||||
for (var i = 0; i < data.length; i+=4) {
|
||||
d = data[i];
|
||||
newdata.push({x:new Date(d.time*1000), y:+d.temp});
|
||||
}
|
||||
|
||||
monitor = new tempgraph.Monitor(newdata);
|
||||
|
||||
});
|
|
@ -24,18 +24,19 @@
|
|||
width:7% !important;
|
||||
}
|
||||
|
||||
.axis path,
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.line {
|
||||
.temperature {
|
||||
fill: none;
|
||||
stroke: steelblue;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.axis .tick{
|
||||
stroke:#DDD;
|
||||
stroke-width:.5px;
|
||||
}
|
||||
.domain{
|
||||
display:none;
|
||||
}
|
||||
|
||||
#graph {
|
||||
width:100%;
|
||||
|
@ -48,6 +49,17 @@
|
|||
pointer-events: all;
|
||||
}
|
||||
|
||||
#stop-button {
|
||||
margin-left:10px;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
#current_temp {
|
||||
font-weight:bold;
|
||||
font-size:200%;
|
||||
color:black;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
|
@ -78,12 +90,13 @@
|
|||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="#" class="dropdown-toggle" data-toggle="dropdown"><div id="current_temp">Temperature</div></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="active"><a href="#">°F</a></li>
|
||||
<li><a href="#">°C</a></li>
|
||||
<li><a href="#">Δ</a></li>
|
||||
</ul></li>
|
||||
<li class="active"><a href="#" id="temp_scale_F">°F</a></li>
|
||||
<li><a href="#" id="temp_scale_C">°C</a></li>
|
||||
<li><a href="#" id="temp_scale_cone">Δ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#" id="current_time">Time</a></li>
|
||||
<li><a href="#" id="current_output">0%</a></li>
|
||||
<li><a href="#" id="current_output_text">0%</a></li>
|
||||
<li><button id="stop-button" class="btn btn-primary navbar-btn hidden" href="#">Stop</button></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
|
@ -96,16 +109,19 @@
|
|||
<div class="row">
|
||||
<div class="col-sm-8 col-md-8 row-space">
|
||||
<svg id="graph" class="row-space"></svg>
|
||||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="temp_graph.js"></script>
|
||||
<script type="text/javascript" src="temp_monitor.js"></script>
|
||||
|
||||
<div class="btn-group btn-group-justified row-space">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary disabled">Off</button>
|
||||
<button type="button" class="btn btn-primary disabled"><span class="glyphicon glyphicon-stop"></span> Off</button>
|
||||
</div>
|
||||
<div class="btn-group output-slider">
|
||||
<input type="range" min=0 max=1000 class="btn btn-default" />
|
||||
<input id="current_output" type="range" min=0 max=1000 class="btn btn-default" />
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-danger">Ignite</button>
|
||||
<button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-fire"></span> Ignite</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -119,10 +135,20 @@
|
|||
</ul>
|
||||
</div><!-- /btn-group -->
|
||||
</div><!-- /input-group -->
|
||||
<div class='btn-group btn-group-justified row-space'>
|
||||
<a href="#" class="btn btn-primary disabled"> Start <span class="glyphicon glyphicon-play"></span></a>
|
||||
<a href="#" class="btn btn-default disabled"> Pause <span class="glyphicon glyphicon-pause"></span></a>
|
||||
<a href="#" class="btn btn-success disabled"> Save <span class="glyphicon glyphicon-floppy-disk"></span></a>
|
||||
<div id='profile_info' class='panel panel-default hidden'>
|
||||
<div class='panel-body'>
|
||||
<dl class='dl-horizontal'>
|
||||
<dt>Total time</dt>
|
||||
<dd></dd>
|
||||
<dt>Finish at</dt>
|
||||
<dd></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div id='profile_actions' class='btn-group btn-group-justified row-space hidden'>
|
||||
<a href="#" class="btn btn-primary disabled"><span class="glyphicon glyphicon-play"></span> Start</a>
|
||||
<a href="#" class="btn btn-default disabled"><span class="glyphicon glyphicon-pause"></span> Pause</a>
|
||||
<a href="#" class="btn btn-success disabled"><span class="glyphicon glyphicon-floppy-disk"></span> Save</a>
|
||||
</div>
|
||||
<img src="http://placehold.it/640x480/" class="img-responsive" alt="webcam" />
|
||||
</div>
|
||||
|
@ -133,7 +159,5 @@
|
|||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
||||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="temp_graph.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Ładowanie…
Reference in New Issue