kopia lustrzana https://github.com/jamesgao/kiln_controller
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
rodzic
f7a8a77edb
commit
f242783476
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 101 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 30 KiB |
|
@ -56,7 +56,7 @@ class ProfileHandler(tornado.web.RequestHandler):
|
|||
|
||||
def post(self, name):
|
||||
try:
|
||||
schedule = self.get_argument("schedule")
|
||||
schedule = json.loads(self.get_argument("schedule"))
|
||||
fname = os.path.join(paths.profile_path, name)
|
||||
with open(fname, 'w') as fp:
|
||||
json.dump(schedule, fp)
|
||||
|
|
|
@ -53,6 +53,7 @@ class Lit(State):
|
|||
interval=float(interval)
|
||||
)
|
||||
return Running, kwargs
|
||||
|
||||
def stop(self):
|
||||
_shutoff(self.parent.regulator, self.parent.notify)
|
||||
return Cooling, dict(history=self.history)
|
||||
|
@ -88,7 +89,7 @@ class Running(State):
|
|||
super(Running, self).__init__(parent)
|
||||
self.start_time = start_time
|
||||
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
|
||||
|
||||
def _notify(self, therm, setpoint, out):
|
||||
|
|
|
@ -41,6 +41,16 @@ body {
|
|||
.profile-pane {
|
||||
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 {
|
||||
stroke:green;
|
||||
stroke-width:1.5px;
|
||||
|
|
|
@ -83,7 +83,7 @@ var tempgraph = (function(module) {
|
|||
.attr("d", line);
|
||||
|
||||
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 marker = this.axes.append("g").selectAll("."+selector+".dot")
|
||||
.data(data, key).enter().append("circle")
|
||||
|
@ -157,7 +157,7 @@ var tempgraph = (function(module) {
|
|||
|
||||
var join, selector;
|
||||
if (this.lines[className].marker) {
|
||||
selector = className.replace(" ", ".");
|
||||
selector = className.replace(/ /gi, ".");
|
||||
join = this.axes.selectAll("."+selector+".dot")
|
||||
.data(data, function(d){ return d.id;});
|
||||
join.enter().append("circle")
|
||||
|
@ -188,6 +188,15 @@ var tempgraph = (function(module) {
|
|||
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;
|
||||
}(tempgraph || {}));
|
||||
|
||||
|
|
|
@ -20,11 +20,6 @@ var tempgraph = (function(module) {
|
|||
$("#current_time").text(nowstr);
|
||||
$("#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
|
||||
if (now > this.last().time) {
|
||||
this.temperature.push(data);
|
||||
|
@ -69,6 +64,7 @@ var tempgraph = (function(module) {
|
|||
$("#profile_time_start").text(start);
|
||||
//$("#profile_time_finish") = this.profile.time_finish();
|
||||
$("#profile_info, #profile_actions").hide().removeClass("hidden").slideDown();
|
||||
return this.profile;
|
||||
}
|
||||
module.Monitor.prototype.last = function() {
|
||||
return this.temperature[this.temperature.length-1];
|
||||
|
@ -117,7 +113,9 @@ var tempgraph = (function(module) {
|
|||
$("#stop_button").addClass("disabled");
|
||||
$("#stop_button_navbar").addClass("hidden disabled");
|
||||
$("#profile_select").removeClass("disabled");
|
||||
}
|
||||
} else if (name == "Profile") {
|
||||
|
||||
}
|
||||
}
|
||||
module.Monitor.prototype._bindUI = function() {
|
||||
$("#temp_scale_C").click(function() { this.setScale("C");}.bind(this));
|
||||
|
@ -155,8 +153,12 @@ var tempgraph = (function(module) {
|
|||
});
|
||||
|
||||
$("#profile_list a").click(function(e) {
|
||||
$("#profile_list li").removeClass("active");
|
||||
$(e.target).parent().addClass("active");
|
||||
$("#profile_name").val($(e.target).text());
|
||||
var fname = $(e.target).attr("data-fname");
|
||||
if (this.profile)
|
||||
this.profile.cleanup();
|
||||
$.getJSON("/profile/"+fname, function(data) {
|
||||
this.setProfile(data);
|
||||
}.bind(this));
|
||||
|
|
|
@ -1,49 +1,52 @@
|
|||
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 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_total = juration.stringify(end);
|
||||
this.time_start = start_time;
|
||||
this.schedule = schedule;
|
||||
this.running = running === undefined ? false : running;
|
||||
|
||||
this.graph = graph;
|
||||
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
|
||||
var now = new Date();
|
||||
var rstart = new Date(now.getTime() - 10*60*100);
|
||||
var rend = this.time_finish(now);
|
||||
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) {
|
||||
return {x:this.graph.x(d.x), y:this.graph.y(d.y)};
|
||||
}.bind(this)).on("dragstart", function(d) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
this._node = this._findNode(d);
|
||||
}.bind(this)).on("drag", this.dragNode.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
this.update();
|
||||
|
||||
//events
|
||||
this._bindUI();
|
||||
}
|
||||
module.Profile.prototype.time_finish = function(now) {
|
||||
if (this.time_start instanceof Date) {
|
||||
return new Date(this.time_start.getTime() + this.length*1000);
|
||||
}
|
||||
module.Profile.prototype.time_finish = function() {
|
||||
var now = this.time_start instanceof Date ? this.time_start : new Date();
|
||||
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 end_time = new Date(start_time.getTime()+this.length*1000);
|
||||
var width = this.graph.x(end_time) - this.graph.x(start_time);
|
||||
var x = this.graph.x(start_time);
|
||||
this.pane.attr("width", width)
|
||||
.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());
|
||||
join.on("mouseover.profile", this.hoverNode.bind(this))
|
||||
.on("mouseout.profile", this._hideInfo.bind(this))
|
||||
.on("dblclick.profile", this.delNode.bind(this));
|
||||
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() {
|
||||
$("#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
|
||||
var updateNode = function() {
|
||||
clearTimeout(this.timeout_infoedit);
|
||||
|
@ -97,8 +115,7 @@ var tempgraph = (function(module) {
|
|||
|
||||
//Graph events
|
||||
this.graph.zoom.on("zoom.profile", this.update.bind(this));
|
||||
this.line.marker.on("dblclick", this.delNode.bind(this));
|
||||
this.graph.pane.on("dblclick", this.addNode.bind(this));
|
||||
this.setState();
|
||||
}
|
||||
module.Profile.prototype._schedule = function() {
|
||||
var start_time = this.time_start instanceof Date ? this.time_start : new Date();
|
||||
|
@ -152,7 +169,9 @@ var tempgraph = (function(module) {
|
|||
this.update();
|
||||
|
||||
//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) {
|
||||
this._node = node;
|
||||
|
@ -193,6 +212,9 @@ var tempgraph = (function(module) {
|
|||
}
|
||||
}
|
||||
this.update();
|
||||
$("#profile_name").removeAttr("disabled");
|
||||
$("#profile_actions .btn-success").removeClass("disabled");
|
||||
$("#profile_actions .btn-primary").addClass("disabled");
|
||||
}
|
||||
module.Profile.prototype.delNode = function(d) {
|
||||
d3.event.stopPropagation();
|
||||
|
@ -205,6 +227,9 @@ var tempgraph = (function(module) {
|
|||
}
|
||||
}
|
||||
this.update();
|
||||
$("#profile_name").removeAttr("disabled");
|
||||
$("#profile_actions .btn-success").removeClass("disabled");
|
||||
$("#profile_actions .btn-primary").addClass("disabled");
|
||||
}
|
||||
module.Profile.prototype.dragNode = function(d) {
|
||||
var time = this.graph.x.invert(d3.event.x);
|
||||
|
@ -215,6 +240,98 @@ var tempgraph = (function(module) {
|
|||
clearTimeout(this.timeout_infohide);
|
||||
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;
|
||||
|
|
|
@ -96,6 +96,8 @@
|
|||
<dd id="profile_time_start"></dd>
|
||||
<dt>Finish at</dt>
|
||||
<dd id="profile_time_finish"></dd>
|
||||
<dt>Remaining</dt>
|
||||
<dd id="profile_time_remain"></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -139,6 +141,7 @@
|
|||
d3.json("temperature.json", function(error, data) {
|
||||
monitor = new tempgraph.Monitor(data);
|
||||
monitor.setState("{{ state }}");
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
Ładowanie…
Reference in New Issue