Schedule saving complete, add pcb images

Most of the UI elements behave as expected, but setting the Running
state is not yet complete. Still need to work out how to deal with the
profile names on the server side. Added a couple of images for the PCB.
Rendered version of the pcb is care of dirtypcb.com, where I ordered the
first batch of PCBs. The other image is rendered via EagleCAD.
master
James Gao 2014-11-01 23:07:27 -07:00
rodzic f7a8a77edb
commit f242783476
9 zmienionych plików z 177 dodań i 35 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 101 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 30 KiB

Wyświetl plik

@ -56,7 +56,7 @@ class ProfileHandler(tornado.web.RequestHandler):
def post(self, name): def post(self, name):
try: try:
schedule = self.get_argument("schedule") schedule = json.loads(self.get_argument("schedule"))
fname = os.path.join(paths.profile_path, name) fname = os.path.join(paths.profile_path, name)
with open(fname, 'w') as fp: with open(fname, 'w') as fp:
json.dump(schedule, fp) json.dump(schedule, fp)

Wyświetl plik

@ -53,6 +53,7 @@ class Lit(State):
interval=float(interval) interval=float(interval)
) )
return Running, kwargs return Running, kwargs
def stop(self): def stop(self):
_shutoff(self.parent.regulator, self.parent.notify) _shutoff(self.parent.regulator, self.parent.notify)
return Cooling, dict(history=self.history) return Cooling, dict(history=self.history)
@ -88,7 +89,7 @@ class Running(State):
super(Running, self).__init__(parent) super(Running, self).__init__(parent)
self.start_time = start_time 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, start_time=start_time **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):

Wyświetl plik

@ -41,6 +41,16 @@ body {
.profile-pane { .profile-pane {
fill:#EEE; fill:#EEE;
} }
.profile-pane-stroke {
fill:none;
stroke:#CCC;
stroke-width:3px;
cursor:ew-resize;
}
.profile-pane-stroke:hover {
stroke-width:15px;
stroke:#333;
}
.profile-line { .profile-line {
stroke:green; stroke:green;
stroke-width:1.5px; stroke-width:1.5px;

Wyświetl plik

@ -83,7 +83,7 @@ var tempgraph = (function(module) {
.attr("d", line); .attr("d", line);
if (marker !== undefined && marker) { if (marker !== undefined && marker) {
var selector = className.replace(" ", "."); var selector = className.replace(/ /gi, ".");
var key = data.id === undefined ? undefined : function(d){ return d.id;}; var key = data.id === undefined ? undefined : function(d){ return d.id;};
var marker = this.axes.append("g").selectAll("."+selector+".dot") var marker = this.axes.append("g").selectAll("."+selector+".dot")
.data(data, key).enter().append("circle") .data(data, key).enter().append("circle")
@ -157,7 +157,7 @@ var tempgraph = (function(module) {
var join, selector; var join, selector;
if (this.lines[className].marker) { if (this.lines[className].marker) {
selector = className.replace(" ", "."); selector = className.replace(/ /gi, ".");
join = this.axes.selectAll("."+selector+".dot") join = this.axes.selectAll("."+selector+".dot")
.data(data, function(d){ return d.id;}); .data(data, function(d){ return d.id;});
join.enter().append("circle") join.enter().append("circle")
@ -188,6 +188,15 @@ var tempgraph = (function(module) {
this.svg.select(".ylabel").text(text); this.svg.select(".ylabel").text(text);
} }
module.Graph.prototype.remove = function(className) {
var selector = className.replace(/ /gi, ".");
this.axes.selectAll("path."+selector).remove();
if (this.lines[className].marker) {
this.axes.selectAll("."+selector+".dot").remove();
}
delete this.lines[className];
}
return module; return module;
}(tempgraph || {})); }(tempgraph || {}));

Wyświetl plik

@ -20,11 +20,6 @@ var tempgraph = (function(module) {
$("#current_time").text(nowstr); $("#current_time").text(nowstr);
$("#current_temp").text(this.scalefunc.print(Math.round(temp*100) / 100)); $("#current_temp").text(this.scalefunc.print(Math.round(temp*100) / 100));
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);
@ -69,6 +64,7 @@ var tempgraph = (function(module) {
$("#profile_time_start").text(start); $("#profile_time_start").text(start);
//$("#profile_time_finish") = this.profile.time_finish(); //$("#profile_time_finish") = this.profile.time_finish();
$("#profile_info, #profile_actions").hide().removeClass("hidden").slideDown(); $("#profile_info, #profile_actions").hide().removeClass("hidden").slideDown();
return this.profile;
} }
module.Monitor.prototype.last = function() { module.Monitor.prototype.last = function() {
return this.temperature[this.temperature.length-1]; return this.temperature[this.temperature.length-1];
@ -117,6 +113,8 @@ var tempgraph = (function(module) {
$("#stop_button").addClass("disabled"); $("#stop_button").addClass("disabled");
$("#stop_button_navbar").addClass("hidden disabled"); $("#stop_button_navbar").addClass("hidden disabled");
$("#profile_select").removeClass("disabled"); $("#profile_select").removeClass("disabled");
} else if (name == "Profile") {
} }
} }
module.Monitor.prototype._bindUI = function() { module.Monitor.prototype._bindUI = function() {
@ -155,8 +153,12 @@ var tempgraph = (function(module) {
}); });
$("#profile_list a").click(function(e) { $("#profile_list a").click(function(e) {
$("#profile_list li").removeClass("active");
$(e.target).parent().addClass("active");
$("#profile_name").val($(e.target).text()); $("#profile_name").val($(e.target).text());
var fname = $(e.target).attr("data-fname"); var fname = $(e.target).attr("data-fname");
if (this.profile)
this.profile.cleanup();
$.getJSON("/profile/"+fname, function(data) { $.getJSON("/profile/"+fname, function(data) {
this.setProfile(data); this.setProfile(data);
}.bind(this)); }.bind(this));

Wyświetl plik

@ -1,49 +1,52 @@
var tempgraph = (function(module) { var tempgraph = (function(module) {
module.Profile = function(graph, scale, schedule, start_time) { module.Profile = function(graph, scale, schedule, start_time, running) {
var end = schedule[schedule.length-1][0]; 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.length = end;
this.time_total = daystr+hourstr+minstr; this.time_total = juration.stringify(end);
this.time_start = start_time; this.time_start = start_time;
this.schedule = schedule; this.schedule = schedule;
this.running = running === undefined ? false : running;
this.graph = graph; this.graph = graph;
this.scalefunc = scale; this.scalefunc = scale;
this.drag_start = d3.behavior.drag()
.on("dragstart", function() {
d3.event.sourceEvent.stopPropagation();
}).on("drag.profile", this.dragStart.bind(this));
//Generate the highlight pane to indicate where the profile is running
this.pane_stroke = this.graph.pane.insert("line", ":first-child")
.attr("class", "profile-pane-stroke")
.attr("y1", 0).attr("y2", this.graph.height)
.call(this.drag_start);
this.pane = this.graph.pane.insert("rect", ":first-child")
.attr("class", "profile-pane")
.attr("height", this.graph.height)
.attr("clip-path", "url(#pane)");
this.line = this.graph.plot(this._schedule(), "profile-line", true);
//immediately view range from 10 min before to end time of profile //immediately view range from 10 min before to end time of profile
var now = new Date(); var now = new Date();
var rstart = new Date(now.getTime() - 10*60*100); var rstart = new Date(now.getTime() - 10*60*100);
var rend = this.time_finish(now); var rend = this.time_finish(now);
this.graph.xlim(rstart, rend); this.graph.xlim(rstart, rend);
this.pane = this.graph.pane.insert("rect", ":first-child")
.attr("class", "profile-pane")
.attr("height", this.graph.height)
.attr("clip-path", "url(#pane)")
this.line = this.graph.plot(this._schedule(), "profile-line", true);
this.drag = d3.behavior.drag().origin(function(d) { this.drag = d3.behavior.drag().origin(function(d) {
return {x:this.graph.x(d.x), y:this.graph.y(d.y)}; return {x:this.graph.x(d.x), y:this.graph.y(d.y)};
}.bind(this)).on("dragstart", function(d) { }.bind(this)).on("dragstart", function(d) {
d3.event.sourceEvent.stopPropagation(); d3.event.sourceEvent.stopPropagation();
this._node = this._findNode(d); this._node = this._findNode(d);
}.bind(this)).on("drag", this.dragNode.bind(this)); }.bind(this));
this.update(); this.update();
//events //events
this._bindUI(); this._bindUI();
} }
module.Profile.prototype.time_finish = function(now) { module.Profile.prototype.time_finish = function() {
if (this.time_start instanceof Date) { var now = this.time_start instanceof Date ? this.time_start : new Date();
return new Date(this.time_start.getTime() + this.length*1000);
}
return new Date(now.getTime() + this.length*1000); return new Date(now.getTime() + this.length*1000);
} }
@ -55,17 +58,32 @@ var tempgraph = (function(module) {
var start_time = this.time_start instanceof Date ? this.time_start : new Date(); var start_time = this.time_start instanceof Date ? this.time_start : new Date();
var end_time = new Date(start_time.getTime()+this.length*1000); var end_time = new Date(start_time.getTime()+this.length*1000);
var width = this.graph.x(end_time) - this.graph.x(start_time); var width = this.graph.x(end_time) - this.graph.x(start_time);
var x = this.graph.x(start_time);
this.pane.attr("width", width) this.pane.attr("width", width)
.attr("transform","translate("+this.graph.x(start_time)+",0)"); .attr("transform","translate("+this.graph.x(start_time)+",0)");
this.pane_stroke.attr("x1", x).attr("x2", x);
var join = this.graph.update("profile-line", this._schedule()); var join = this.graph.update("profile-line", this._schedule());
join.on("mouseover.profile", this.hoverNode.bind(this)) join.on("mouseover.profile", this.hoverNode.bind(this))
.on("mouseout.profile", this._hideInfo.bind(this)) .on("mouseout.profile", this._hideInfo.bind(this))
.on("dblclick.profile", this.delNode.bind(this)); .on("dblclick.profile", this.delNode.bind(this));
join.call(this.drag); join.call(this.drag);
//update the profile info box
var start = this.time_start instanceof Date ? module.format_time(this.time_start) : "Not started";
var finish = this.time_finish();
var remain = (finish - (new Date())) / 1000;
$("#profile_time_finish").text(module.format_time(finish));
$("#profile_time_start").text(start);
$("#profile_time_remain").text(juration.stringify(remain));
} }
module.Profile.prototype._bindUI = function() { module.Profile.prototype._bindUI = function() {
$("#profile_name").attr("disabled", "disabled");
$("#profile_actions .btn-success").click(this.save.bind(this));
$("#profile_actions .btn-primary").click(this.start.bind(this));
$("#profile_actions .btn-default").click(this.pause.bind(this));
// Info pane events // Info pane events
var updateNode = function() { var updateNode = function() {
clearTimeout(this.timeout_infoedit); clearTimeout(this.timeout_infoedit);
@ -97,8 +115,7 @@ var tempgraph = (function(module) {
//Graph events //Graph events
this.graph.zoom.on("zoom.profile", this.update.bind(this)); this.graph.zoom.on("zoom.profile", this.update.bind(this));
this.line.marker.on("dblclick", this.delNode.bind(this)); this.setState();
this.graph.pane.on("dblclick", this.addNode.bind(this));
} }
module.Profile.prototype._schedule = function() { module.Profile.prototype._schedule = function() {
var start_time = this.time_start instanceof Date ? this.time_start : new Date(); var start_time = this.time_start instanceof Date ? this.time_start : new Date();
@ -152,7 +169,9 @@ var tempgraph = (function(module) {
this.update(); this.update();
//Unlock the save buttons and names //Unlock the save buttons and names
$("#profile_name").removeAttr("disabled");
$("#profile_actions .btn-success").removeClass("disabled");
$("#profile_actions .btn-primary").addClass("disabled");
} }
module.Profile.prototype._showInfo = function(node) { module.Profile.prototype._showInfo = function(node) {
this._node = node; this._node = node;
@ -193,6 +212,9 @@ var tempgraph = (function(module) {
} }
} }
this.update(); this.update();
$("#profile_name").removeAttr("disabled");
$("#profile_actions .btn-success").removeClass("disabled");
$("#profile_actions .btn-primary").addClass("disabled");
} }
module.Profile.prototype.delNode = function(d) { module.Profile.prototype.delNode = function(d) {
d3.event.stopPropagation(); d3.event.stopPropagation();
@ -205,6 +227,9 @@ var tempgraph = (function(module) {
} }
} }
this.update(); this.update();
$("#profile_name").removeAttr("disabled");
$("#profile_actions .btn-success").removeClass("disabled");
$("#profile_actions .btn-primary").addClass("disabled");
} }
module.Profile.prototype.dragNode = function(d) { module.Profile.prototype.dragNode = function(d) {
var time = this.graph.x.invert(d3.event.x); var time = this.graph.x.invert(d3.event.x);
@ -215,6 +240,98 @@ var tempgraph = (function(module) {
clearTimeout(this.timeout_infohide); clearTimeout(this.timeout_infohide);
this._showInfo(d.id); this._showInfo(d.id);
} }
module.Profile.prototype.dragStart = function() {
this.time_start = this.graph.x.invert(d3.event.x);
if (this.time_start > new Date())
this.time_start = null;
this.update();
}
module.Profile.prototype.save = function() {
//convert name into filename
var rawname = $("#profile_name").val();
var name = rawname.replace(/ /gi, "_");
name = name.replace(/Δ/gi, "^");
name += ".json";
var post = {schedule:JSON.stringify(this.schedule)};
$.post("/profile/"+name, post).done(function(result) {
if (result.type == "success") {
$("#profile_name").attr("disabled", "disabled");
$("#profile_actions .btn-success").addClass("disabled");
//Check if the name exists in the menu, otherwise add new entry
var notnew = false;
$("#profile_list a").each(function() {
console.log($(this).data("fname"), $(this).data("fname") == name);
notnew = $(this).data("fname") == name || notnew;
});
if (!notnew) {
//Add a new entry into the profile list dropdown
$("#profile_list li").removeClass("active");
var html = "<li><a href='#' data-fname='"+name+"' class='active'>"+rawname+"</a></li>";
$("#profile_list").append(html).addClass("active").select("a")
.click(function(e) {
$("#profile_list a").removeClass("active");
$(e.target).addClass("active");
$("#profile_name").val($(e.target).text());
var fname = $(e.target).attr("data-fname");
this.cleanup();
$.getJSON("/profile/"+fname, function(data) {
monitor.setProfile(data);
});
}.bind(this));
}
this.setState(false);
} else if (result.type == "error") {
alert(result.msg);
}
}.bind(this));
}
module.Profile.prototype.setState = function(running) {
this.running = running === undefined ? this.running : running;
console.log("Set State: ", this.running);
if (this.running) {
this.line.marker.on("dblclick.profile", null);
this.graph.pane.on("dblclick.profile", null);
$("#profile-node-info input").attr("disabled", "disabled");
this.drag.on("drag.profile", null);
this.drag_start.on("drag.profile", null);
$("#profile_actions .btn-success").addClass("disabled");
$("#profile_actions .btn-primary").addClass("disabled");
$("#profile_actions .btn-default").removeClass("disabled");
} else {
this.line.marker.on("dblclick.profile", this.delNode.bind(this));
this.graph.pane.on("dblclick.profile", this.addNode.bind(this));
$("#profile-node-info input").removeAttr("disabled");
this.drag.on("drag.profile", this.dragNode.bind(this));
this.drag_start.on("drag.profile", this.dragStart.bind(this));
$("#profile_actions .btn-success").addClass("disabled");
$("#profile_actions .btn-primary").removeClass("disabled");
$("#profile_actions .btn-default").addClass("disabled");
}
}
module.Profile.prototype.cleanup = function() {
this.graph.remove("profile-line");
this.pane.remove();
this.pane_stroke.remove();
}
module.Profile.prototype.pause = function() {
$("#profile_actions .btn-default").addClass("disabled");
//TODO: ajax query
this.setState(false)
}
module.Profile.prototype.start = function() {
$("#profile_actions .btn-primary").addClass("disabled");
//TODO: ajax query
//This should be done by the query
this.setState(true);
if (!(this.time_start instanceof Date))
this.time_start = new Date();
}
return module; return module;

Wyświetl plik

@ -96,6 +96,8 @@
<dd id="profile_time_start"></dd> <dd id="profile_time_start"></dd>
<dt>Finish at</dt> <dt>Finish at</dt>
<dd id="profile_time_finish"></dd> <dd id="profile_time_finish"></dd>
<dt>Remaining</dt>
<dd id="profile_time_remain"></dd>
</dl> </dl>
</div> </div>
</div> </div>
@ -139,6 +141,7 @@
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.setState("{{ state }}"); monitor.setState("{{ state }}");
}); });
</script> </script>
</body> </body>