move files around, add local js/css, start profiles

master
James Gao 2014-10-23 01:20:53 -07:00
rodzic 3ac85ba18b
commit 5034ed1ce1
14 zmienionych plików z 155 dodań i 49 usunięć

0
kiln/Adafruit_I2C.py 100755 → 100644
Wyświetl plik

Wyświetl plik

@ -1,5 +1,6 @@
import time
import os import os
import re
import time
import json import json
import traceback import traceback
import inspect import inspect
@ -8,15 +9,7 @@ import tornado.ioloop
import tornado.web import tornado.web
from tornado import websocket from tornado import websocket
cwd = os.path.split(os.path.abspath(__file__))[0] import paths
class ManagerHandler(tornado.web.RequestHandler):
def initialize(self, manager):
self.manager = manager
class MainHandler(ManagerHandler):
def get(self):
return self.render("template.html", state=self.manager.state.__class__.__name__)
class ClientSocket(websocket.WebSocketHandler): class ClientSocket(websocket.WebSocketHandler):
def initialize(self, parent): def initialize(self, parent):
@ -28,12 +21,46 @@ class ClientSocket(websocket.WebSocketHandler):
def on_close(self): def on_close(self):
self.parent.clients.remove(self) self.parent.clients.remove(self)
class ManagerHandler(tornado.web.RequestHandler):
def initialize(self, manager):
self.manager = manager
class MainHandler(ManagerHandler):
def get(self):
files = os.listdir(paths.profile_path)
fixname = lambda x: os.path.splitext(x)[0].replace("_", " ")
profiles = dict((fname, fixname(fname)) for fname in files)
return self.render(os.path.join(paths.html_templates, "main.html"),
state=self.manager.state.__class__.__name__,
profiles=profiles,
)
class DataRequest(ManagerHandler): class DataRequest(ManagerHandler):
def get(self): def get(self):
data = list(self.manager.history) data = list(self.manager.history)
output = [dict(time=ts.time, temp=ts.temp) for ts in data] output = [dict(time=ts.time, temp=ts.temp) for ts in data]
self.write(json.dumps(output)) self.write(json.dumps(output))
class ProfileHandler(tornado.web.RequestHandler):
def get(self, name):
try:
with open(os.path.join(paths.profile_path, name)) as fp:
self.write(fp.read())
except IOError:
self.write_error(404)
def post(self, name):
try:
schedule = self.get_argument("schedule")
fname = os.path.join(paths.profile_path, name)
with open(fname, 'w') as fp:
json.dump(schedule, fp)
self.write(dict(type="success"))
except IOError:
self.write_error(404)
except Exception as e:
self.write(dict(type="error", error=repr(e), msg=traceback.format_exc()))
class DoAction(ManagerHandler): class DoAction(ManagerHandler):
def _run(self, name, argfunc): def _run(self, name, argfunc):
func = getattr(self.manager.state, name) func = getattr(self.manager.state, name)
@ -68,16 +95,17 @@ class DoAction(ManagerHandler):
self._run(action, self.get_argument) self._run(action, self.get_argument)
self.write(json.dumps(dict(type="success"))) self.write(json.dumps(dict(type="success")))
except Exception as e: except Exception as e:
self.write(json.dumps(dict(type="error", error=repr(e), msg=traceback.format_exc()))) self.write(json.dumps(dict(type="error", error=repr(e), msg=traceback.format_exc())))
class WebApp(object): class WebApp(object):
def __init__(self, manager, port=8888): def __init__(self, manager, port=8888):
self.handlers = [ self.handlers = [
(r'/', MainHandler, dict(manager=manager)), (r"^/$", MainHandler, dict(manager=manager)),
(r"/ws/", ClientSocket, dict(parent=self)), (r"^/ws/?$", ClientSocket, dict(parent=self)),
(r"/temperature.json", DataRequest, dict(manager=manager)), (r"^/temperature.json$", DataRequest, dict(manager=manager)),
(r"/do/(.*)", DoAction, dict(manager=manager)), (r"^/do/(.*)/?$", DoAction, dict(manager=manager)),
(r"/(.*)", tornado.web.StaticFileHandler, dict(path=cwd)), (r"^/profile/?(.*)$", ProfileHandler),
(r"^/(.*)$", tornado.web.StaticFileHandler, dict(path=paths.html_static)),
] ]
self.clients = [] self.clients = []
self.port = port self.port = port

Wyświetl plik

@ -84,10 +84,11 @@ class Cooling(State):
return Running, kwargs return Running, kwargs
class Running(State): class Running(State):
def __init__(self, parent, history, **kwargs): def __init__(self, parent, history, start_time=None, **kwargs):
super(Running, self).__init__(parent) super(Running, self).__init__(parent)
self.start_time = start_time
self.profile = manager.Profile(therm=self.parent.therm, regulator=self.parent.regulator, self.profile = manager.Profile(therm=self.parent.therm, regulator=self.parent.regulator,
callback=self._notify, **kwargs) callback=self._notify, start_time=start_time **kwargs)
self.history = history self.history = history
def _notify(self, therm, setpoint, out): def _notify(self, therm, setpoint, out):

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

File diff suppressed because one or more lines are too long

5
kiln/static/js/d3.v3.min.js vendored 100644

File diff suppressed because one or more lines are too long

4
kiln/static/js/jquery.min.js vendored 100644

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -1,31 +1,34 @@
var tempgraph = (function(module) { var tempgraph = (function(module) {
module.Monitor = function(initial) { module.Monitor = function(initial) {
this.temperature = initial; this.temperature = initial;
this.profile = null;
//default to F //default to F
this.scalefunc = module.temp_to_F; this.scalefunc = module.temp_to_F;
this.temp_suffix = "°F" this.temp_suffix = "°F"
this.temp_prefix = "" this.temp_prefix = ""
this.graph = new tempgraph.Graph(); this.graph = new module.Graph();
this._mapped = this.temperature.map(this._map_temp.bind(this)); this._mapped = this.temperature.map(this._map_temp.bind(this));
this.graph.plot(this._mapped, "temperature", false); this.graph.plot(this._mapped, "temperature", false);
this.update_temp(this.last()); this.updateTemp(this.last());
this._bindUI(); this._bindUI();
} }
module.Monitor.prototype.update_temp = function(data) { module.Monitor.prototype.updateTemp = function(data) {
var now = new Date(data.time*1000.); var now = new Date(data.time*1000.);
var temp = this.scalefunc(data.temp); var temp = this.scalefunc(data.temp);
var hourstr = now.getHours() % 12; var nowstr = module.format_time(now);
hourstr = hourstr == 0 ? 12 : hourstr;
var minstr = now.getMinutes();
minstr = minstr.length < 2 ? "0"+minstr : minstr;
var nowstr = hourstr + ":" + minstr + (now.getHours() >= 12 ? " pm" : " am");
var tempstr = Math.round(temp*100) / 100; var tempstr = Math.round(temp*100) / 100;
$("#current_time").text(nowstr); $("#current_time").text(nowstr);
$("#current_temp").text(this.temp_prefix+tempstr+this.temp_suffix); $("#current_temp").text(this.temp_prefix+tempstr+this.temp_suffix);
if (this.profile) {
var finish = module.format_time(this.profile.time_finish(now));
$("#profile_time_finish").text(finish);
}
//Adjust x and ylims //Adjust x and ylims
if (now > this.last().time) { if (now > this.last().time) {
this.temperature.push(data); this.temperature.push(data);
@ -60,10 +63,14 @@ var tempgraph = (function(module) {
} }
} }
} }
module.Monitor.prototype.update_UI = function(packet) { module.Monitor.prototype.setProfile = function(schedule, start_time) {
this.profile = new module.Profile(schedule, start_time);
} var start = this.profile.time_start === undefined ?
module.Monitor.prototype.setProfile = function(profile) { "Not started" : module.format_time(start_time);
$("#profile_time_total").text(this.profile.time_total);
$("#profile_time_start").text(start);
//$("#profile_time_finish") = this.profile.time_finish();
$("#profile_info, #profile_actions").hide().removeClass("hidden").slideDown();
} }
module.Monitor.prototype.last = function() { module.Monitor.prototype.last = function() {
return this.temperature[this.temperature.length-1]; return this.temperature[this.temperature.length-1];
@ -103,17 +110,19 @@ var tempgraph = (function(module) {
return {x:new Date(d.time*1000), y:this.scalefunc(d.temp)}; return {x:new Date(d.time*1000), y:this.scalefunc(d.temp)};
} }
module.Monitor.prototype.set_state = function(name) { module.Monitor.prototype.setState = function(name) {
if (name == "Lit") { if (name == "Lit") {
$("#ignite_button").addClass("disabled"); $("#ignite_button").addClass("disabled");
$("#current_output").removeAttr("disabled"); $("#current_output").removeAttr("disabled");
$("#stop_button").removeClass("disabled"); $("#stop_button").removeClass("disabled");
$("#stop_button_navbar").removeClass("hidden disabled"); $("#stop_button_navbar").removeClass("hidden disabled");
$("#profile_select").removeClass("disabled");
} else if (name == "Idle" || name == "Cooling") { } else if (name == "Idle" || name == "Cooling") {
$("#ignite_button").removeClass("disabled"); $("#ignite_button").removeClass("disabled");
$("#current_output").attr("disabled", "disabled"); $("#current_output").attr("disabled", "disabled");
$("#stop_button").addClass("disabled"); $("#stop_button").addClass("disabled");
$("#stop_button_navbar").addClass("hidden disabled"); $("#stop_button_navbar").addClass("hidden disabled");
$("#profile_select").removeClass("disabled");
} }
} }
module.Monitor.prototype._bindUI = function() { module.Monitor.prototype._bindUI = function() {
@ -121,6 +130,8 @@ var tempgraph = (function(module) {
$("#temp_scale_F").click(function() { this.setScale("F");}.bind(this)); $("#temp_scale_F").click(function() { this.setScale("F");}.bind(this));
//$("#temp_scale_C").click(function() { this.setScale("C");}.bind(this)); //$("#temp_scale_C").click(function() { this.setScale("C");}.bind(this));
$("#profile_name").val("");
$("#ignite_button").click(function() { $("#ignite_button").click(function() {
this._disable_all(); this._disable_all();
$.getJSON("/do/ignite", function(data) { $.getJSON("/do/ignite", function(data) {
@ -149,9 +160,13 @@ var tempgraph = (function(module) {
}) })
}); });
$("#profile_list a").click(function(e) {
$("#profile_name").val($(e.target).text());
var fname = $(e.target).attr("data-fname");
$.getJSON("/profile/"+fname, function(data) {
this.setProfile(data);
}.bind(this));
}.bind(this));
try { try {
var sock = new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws/", "protocolOne"); var sock = new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws/", "protocolOne");
@ -159,9 +174,9 @@ var tempgraph = (function(module) {
sock.onmessage = function(event) { sock.onmessage = function(event) {
var data = JSON.parse(event.data); var data = JSON.parse(event.data);
if (data.type == "temperature") if (data.type == "temperature")
this.update_temp(data); this.updateTemp(data);
else if (data.type == "state") { else if (data.type == "state") {
this.set_state(data.state); this.setState(data.state);
} }
}.bind(this); }.bind(this);
} catch (e) {} } catch (e) {}
@ -180,5 +195,15 @@ var tempgraph = (function(module) {
return "Not implemented" return "Not implemented"
} }
module.format_time = function(now) {
if (!(now instanceof Date))
now = new Date(now);
var hourstr = now.getHours() % 12;
hourstr = hourstr == 0 ? 12 : hourstr;
var minstr = now.getMinutes();
minstr = minstr < 10 ? "0"+minstr : minstr;
return hourstr + ":" + minstr + (now.getHours() >= 12 ? " pm" : " am");
}
return module; return module;
}(tempgraph || {})); }(tempgraph || {}));

Wyświetl plik

@ -0,0 +1,22 @@
var tempgraph = (function(module) {
module.Profile = function(schedule, start_time) {
var end = schedule[schedule.length-1][0];
var days = Math.floor(end / 60 / 60 / 24);
var hours = Math.floor((end - days*60*60*24) / 60 / 60);
var minutes = Math.ceil((end - days*60*60*24 - hours*60*60) / 60);
var daystr = days > 0 ? days + " days, " : "";
var hourstr = hours > 0 ? hours + " hours": "";
var minstr = minutes > 0 ? ", "+minutes + " minutes.":".";
this.length = end;
this.time_total = daystr+hourstr+minstr;
this.time_start = start_time;
}
module.Profile.prototype.time_finish = function(now) {
if (this.time_start instanceof Date) {
return new Date(this.time_start.getTime() + this.length*1000);
}
return new Date(now.getTime() + this.length*1000);
}
return module;
}(tempgraph || {}));

Wyświetl plik

@ -8,10 +8,10 @@
<!-- Bootstrap --> <!-- Bootstrap -->
<!-- Latest compiled and minified CSS --> <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap.min.css">
<!-- Optional theme --> <!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"> <link rel="stylesheet" href="css/bootstrap-theme.min.css">
<style type="text/css"> <style type="text/css">
body { body {
@ -118,11 +118,13 @@
</div> </div>
<div class="col-sm-4 col-md-4"> <div class="col-sm-4 col-md-4">
<div class="input-group input-group-lg row-space"> <div class="input-group input-group-lg row-space">
<input type="text" disabled="disabled" class="form-control" placeholder="Load profile"> <input id="profile_name" type="text" disabled="disabled" class="form-control" placeholder="Load profile">
<div class="input-group-btn"> <div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right" role="menu"> <ul id="profile_list" class="dropdown-menu dropdown-menu-right" role="menu">
<li><a href="#">...</a></li> {% for fname, name in profiles.items() %}
<li><a href="#" data-fname="{{fname}}">{{ name }}</a></li>
{% end %}
</ul> </ul>
</div><!-- /btn-group --> </div><!-- /btn-group -->
</div><!-- /input-group --> </div><!-- /input-group -->
@ -130,9 +132,11 @@
<div class='panel-body'> <div class='panel-body'>
<dl class='dl-horizontal'> <dl class='dl-horizontal'>
<dt>Total time</dt> <dt>Total time</dt>
<dd></dd> <dd id="profile_time_total"></dd>
<dt>Start time</dt>
<dd id="profile_time_start"></dd>
<dt>Finish at</dt> <dt>Finish at</dt>
<dd></dd> <dd id="profile_time_finish"></dd>
</dl> </dl>
</div> </div>
</div> </div>
@ -148,16 +152,17 @@
</div> <!-- /container --> </div> <!-- /container -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script src="js/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script> <script src="js/bootstrap.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script src="js/d3.v3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="temp_graph.js"></script> <script type="text/javascript" src="js/temp_graph.js"></script>
<script type="text/javascript" src="temp_monitor.js"></script> <script type="text/javascript" src="js/temp_profile.js"></script>
<script type="text/javascript" src="js/temp_monitor.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var monitor; var monitor;
d3.json("temperature.json", function(error, data) { d3.json("temperature.json", function(error, data) {
monitor = new tempgraph.Monitor(data); monitor = new tempgraph.Monitor(data);
monitor.set_state("{{ state }}"); monitor.setState("{{ state }}");
}); });
</script> </script>
</body> </body>