kopia lustrzana https://github.com/jamesgao/kiln_controller
State machine done: #3. Manual override works
rodzic
7183181d44
commit
3ac85ba18b
|
@ -2,7 +2,6 @@ import stepper
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
import thermo
|
import thermo
|
||||||
import warnings
|
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
import logging
|
||||||
|
@ -36,10 +35,7 @@ class Manager(threading.Thread):
|
||||||
if self._send is not None:
|
if self._send is not None:
|
||||||
self._send(data)
|
self._send(data)
|
||||||
else:
|
else:
|
||||||
logging.warn("No notifier set, ignoring message: %s"%data)
|
logger.info("No notifier set, ignoring message: %s"%data)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.manager_stop()
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
"""Mutates the manager to return State actions
|
"""Mutates the manager to return State actions
|
||||||
|
@ -58,12 +54,12 @@ class Manager(threading.Thread):
|
||||||
if isinstance(output, type) and issubclass(output, states.State) :
|
if isinstance(output, type) and issubclass(output, states.State) :
|
||||||
self.state = output(self)
|
self.state = output(self)
|
||||||
self.state_change.set()
|
self.state_change.set()
|
||||||
self.notify(dict(type="change", state=output.__name__))
|
self.notify(dict(type="state", state=output.__name__))
|
||||||
elif isinstance(output, tuple) and issubclass(output[0], states.State):
|
elif isinstance(output, tuple) and issubclass(output[0], states.State):
|
||||||
newstate, kwargs = output
|
newstate, kwargs = output
|
||||||
self.state = newstate(self, **kwargs)
|
self.state = newstate(self, **kwargs)
|
||||||
self.notify(dict(type="change", state=newstate.__name__))
|
self.notify(dict(type="state", state=newstate.__name__))
|
||||||
elif isinstance(output, dict) and "type" in dict:
|
elif isinstance(output, dict) and "type" in output:
|
||||||
self.notify(output)
|
self.notify(output)
|
||||||
elif output is not None:
|
elif output is not None:
|
||||||
logger.warn("Unknown state output: %r"%output)
|
logger.warn("Unknown state output: %r"%output)
|
||||||
|
|
|
@ -2,65 +2,89 @@ import time
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
|
import inspect
|
||||||
|
|
||||||
import tornado.ioloop
|
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]
|
cwd = os.path.split(os.path.abspath(__file__))[0]
|
||||||
|
|
||||||
|
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):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
self.parent.sockets.append(self)
|
self.parent.clients.append(self)
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
self.parent.sockets.remove(self)
|
self.parent.clients.remove(self)
|
||||||
|
|
||||||
class DataRequest(tornado.web.RequestHandler):
|
|
||||||
def initialize(self, manager):
|
|
||||||
self.manager = manager
|
|
||||||
|
|
||||||
|
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 DoAction(tornado.web.RequestHandler):
|
class DoAction(ManagerHandler):
|
||||||
def initialize(self, manager):
|
def _run(self, name, argfunc):
|
||||||
self.manager = manager
|
func = getattr(self.manager.state, name)
|
||||||
|
#Introspect the function, get the arguments
|
||||||
|
args, varargs, keywords, defaults = inspect.getargspec(func)
|
||||||
|
|
||||||
|
kwargs = dict()
|
||||||
|
if defaults is not None:
|
||||||
|
#keyword arguments
|
||||||
|
for arg, d in zip(args[-len(defaults):], defaults):
|
||||||
|
kwargs[arg] = argfunc(arg, default=d)
|
||||||
|
end = len(defaults)
|
||||||
|
else:
|
||||||
|
end = len(args)
|
||||||
|
|
||||||
|
#required arguments
|
||||||
|
for arg in args[1:end]:
|
||||||
|
kwargs[arg] = argfunc(arg)
|
||||||
|
|
||||||
|
realfunc = getattr(self.manager, name)
|
||||||
|
realfunc(**kwargs)
|
||||||
|
|
||||||
def get(self, action):
|
def get(self, action):
|
||||||
try:
|
try:
|
||||||
func = getattr(self.manager, action)
|
self._run(action, self.get_query_argument)
|
||||||
func()
|
|
||||||
self.write(json.dumps(dict(type="success")))
|
self.write(json.dumps(dict(type="success")))
|
||||||
except:
|
except Exception as e:
|
||||||
self.write(json.dumps(dict(type="error", msg=traceback.format_exc())))
|
self.write(json.dumps(dict(type="error", error=repr(e), msg=traceback.format_exc())))
|
||||||
|
|
||||||
def post(self, action):
|
def post(self, action):
|
||||||
try:
|
try:
|
||||||
func = getattr(self.manager, action)
|
self._run(action, self.get_argument)
|
||||||
func()
|
|
||||||
self.write(json.dumps(dict(type="success")))
|
self.write(json.dumps(dict(type="success")))
|
||||||
except:
|
except Exception as e:
|
||||||
self.write(json.dumps(dict(type="error", 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"/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"/(.*)", tornado.web.StaticFileHandler, dict(path=cwd)),
|
||||||
]
|
]
|
||||||
self.sockets = []
|
self.clients = []
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
jsondat = json.dumps(data)
|
jsondat = json.dumps(data)
|
||||||
for sock in self.sockets:
|
for sock in self.clients:
|
||||||
sock.write_message(jsondat)
|
sock.write_message(jsondat)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
|
@ -7,31 +7,31 @@ import manager
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
def __init__(self, machine):
|
def __init__(self, manager):
|
||||||
self.parent = machine
|
self.parent = manager
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Action that must be continuously run while in this state"""
|
"""Action that must be continuously run while in this state"""
|
||||||
self.parent.state_change.clear()
|
|
||||||
self.parent.state_change.wait()
|
|
||||||
|
|
||||||
class Idle(State):
|
|
||||||
def __init__(self, parent):
|
|
||||||
super(Idle, self).__init__(parent)
|
|
||||||
self.history = deque(maxlen=1024) #about 10 minutes worth
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
ts = self.parent.therm.get()
|
ts = self.parent.therm.get()
|
||||||
self.history.append(ts)
|
self.history.append(ts)
|
||||||
self.parent.notify(dict(type="temperature", time=ts.time, temp=ts.temp))
|
return dict(type="temperature", time=ts.time, temp=ts.temp, output=self.parent.regulator.output)
|
||||||
|
|
||||||
|
class Idle(State):
|
||||||
|
def __init__(self, manager):
|
||||||
|
super(Idle, self).__init__(manager)
|
||||||
|
self.history = deque(maxlen=1024) #about 10 minutes worth
|
||||||
|
|
||||||
def ignite(self):
|
def ignite(self):
|
||||||
_ignite(self.parent.regulator, self.parent.notify)
|
_ignite(self.parent.regulator, self.parent.notify)
|
||||||
return Lit, dict(history=self.history)
|
return Lit, dict(history=self.history)
|
||||||
|
|
||||||
def start(self, **kwargs):
|
def start(self, schedule, start_time=None, interval=5):
|
||||||
_ignite(self.parent.regulator, self.parent.notify)
|
_ignite(self.parent.regulator, self.parent.notify)
|
||||||
kwargs['history'] = deque(self.history)
|
kwargs = dict(history=self.history,
|
||||||
|
schedule=json.loads(schedule),
|
||||||
|
start_time=float(start_time),
|
||||||
|
interval=float(interval)
|
||||||
|
)
|
||||||
return Running, kwargs
|
return Running, kwargs
|
||||||
|
|
||||||
class Lit(State):
|
class Lit(State):
|
||||||
|
@ -41,22 +41,21 @@ class Lit(State):
|
||||||
|
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
try:
|
try:
|
||||||
self.parent.regulator.set(value)
|
self.parent.regulator.set(float(value))
|
||||||
self.parent.notify(dict(type="success"))
|
return dict(type="success")
|
||||||
except:
|
except:
|
||||||
self.parent.notify(dict(type="error", msg=traceback.format_exc()))
|
return dict(type="error", msg=traceback.format_exc())
|
||||||
|
|
||||||
def run(self):
|
def start(self, schedule, start_time=None, interval=5):
|
||||||
ts = self.parent.therm.get()
|
kwargs = dict(history=self.history,
|
||||||
self.history.append(ts)
|
schedule=json.loads(schedule),
|
||||||
|
start_time=float(start_time),
|
||||||
def start(self, **kwargs):
|
interval=float(interval)
|
||||||
kwargs['history'] = self.history
|
)
|
||||||
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 Idle
|
return Cooling, dict(history=self.history)
|
||||||
|
|
||||||
class Cooling(State):
|
class Cooling(State):
|
||||||
def __init__(self, parent, history):
|
def __init__(self, parent, history):
|
||||||
|
@ -69,6 +68,20 @@ class Cooling(State):
|
||||||
if ts.temp < 50:
|
if ts.temp < 50:
|
||||||
#TODO: save temperature log somewhere
|
#TODO: save temperature log somewhere
|
||||||
return Idle
|
return Idle
|
||||||
|
return dict(type="temperature", time=ts.time, temp=ts.temp)
|
||||||
|
|
||||||
|
def ignite(self):
|
||||||
|
_ignite(self.parent.regulator, self.parent.notify)
|
||||||
|
return Lit, dict(history=self.history)
|
||||||
|
|
||||||
|
def start(self, schedule, start_time=None, interval=5):
|
||||||
|
_ignite(self.parent.regulator, self.parent.notify)
|
||||||
|
kwargs = dict(history=self.history,
|
||||||
|
schedule=json.loads(schedule),
|
||||||
|
start_time=float(start_time),
|
||||||
|
interval=float(interval)
|
||||||
|
)
|
||||||
|
return Running, kwargs
|
||||||
|
|
||||||
class Running(State):
|
class Running(State):
|
||||||
def __init__(self, parent, history, **kwargs):
|
def __init__(self, parent, history, **kwargs):
|
||||||
|
@ -92,7 +105,7 @@ class Running(State):
|
||||||
_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)
|
||||||
|
|
||||||
self.history.append(self.parent.therm.get())
|
return super(Running, self).run()
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
self.profile.stop()
|
self.profile.stop()
|
||||||
|
@ -111,7 +124,7 @@ def _ignite(regulator, notify):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = dict(type="error", msg="Cannot ignite: regulator not off")
|
msg = dict(type="error", msg="Cannot ignite: regulator not off")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = dict(type="error", msg=traceback.format_exc())
|
msg = dict(type="error", error=repr(e), msg=traceback.format_exc())
|
||||||
notify(msg)
|
notify(msg)
|
||||||
|
|
||||||
def _shutoff(regulator, notify):
|
def _shutoff(regulator, notify):
|
||||||
|
@ -121,5 +134,5 @@ def _shutoff(regulator, notify):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = dict(type="error", msg="Cannot shutoff: regulator not lit")
|
msg = dict(type="error", msg="Cannot shutoff: regulator not lit")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = dict(type="error", msg=traceback.format_exc())
|
msg = dict(type="error", error=repr(e), msg=traceback.format_exc())
|
||||||
notify(msg)
|
notify(msg)
|
|
@ -18,12 +18,13 @@ var tempgraph = (function(module) {
|
||||||
var temp = this.scalefunc(data.temp);
|
var temp = this.scalefunc(data.temp);
|
||||||
|
|
||||||
var hourstr = now.getHours() % 12;
|
var hourstr = now.getHours() % 12;
|
||||||
hourstr = hourstr == 0 ? 12 : houstr;
|
hourstr = hourstr == 0 ? 12 : hourstr;
|
||||||
var minstr = now.getMinutes();
|
var minstr = now.getMinutes();
|
||||||
minstr = minstr.length < 2 ? "0"+minstr : minstr;
|
minstr = minstr.length < 2 ? "0"+minstr : minstr;
|
||||||
var nowstr = hourstr + ":" + minstr + (now.getHours() >= 12 ? " pm" : " am");
|
var nowstr = hourstr + ":" + minstr + (now.getHours() >= 12 ? " pm" : " am");
|
||||||
|
var tempstr = Math.round(temp*100) / 100;
|
||||||
$("#current_time").text(nowstr);
|
$("#current_time").text(nowstr);
|
||||||
$("#current_temp").text(this.temp_prefix+temp+this.temp_suffix);
|
$("#current_temp").text(this.temp_prefix+tempstr+this.temp_suffix);
|
||||||
|
|
||||||
//Adjust x and ylims
|
//Adjust x and ylims
|
||||||
if (now > this.last().time) {
|
if (now > this.last().time) {
|
||||||
|
@ -49,8 +50,14 @@ var tempgraph = (function(module) {
|
||||||
|
|
||||||
//update the output slider and text, if necessary
|
//update the output slider and text, if necessary
|
||||||
if (data.output !== undefined) {
|
if (data.output !== undefined) {
|
||||||
$("#current_output_text").text(data.output*100+"%");
|
if (data.output == -1) {
|
||||||
$("#current_output").val(data.output*1000);
|
$("#current_output_text").text("Off");
|
||||||
|
$("#current_output").val(0);
|
||||||
|
} else {
|
||||||
|
var outstr = Math.round(data.output*10000) / 100;
|
||||||
|
$("#current_output_text").text(outstr+"%");
|
||||||
|
$("#current_output").val(data.output*1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.Monitor.prototype.update_UI = function(packet) {
|
module.Monitor.prototype.update_UI = function(packet) {
|
||||||
|
@ -96,10 +103,56 @@ 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.setState = function(name) {
|
module.Monitor.prototype.set_state = function(name) {
|
||||||
|
if (name == "Lit") {
|
||||||
|
$("#ignite_button").addClass("disabled");
|
||||||
|
$("#current_output").removeAttr("disabled");
|
||||||
|
$("#stop_button").removeClass("disabled");
|
||||||
|
$("#stop_button_navbar").removeClass("hidden disabled");
|
||||||
|
} else if (name == "Idle" || name == "Cooling") {
|
||||||
|
$("#ignite_button").removeClass("disabled");
|
||||||
|
$("#current_output").attr("disabled", "disabled");
|
||||||
|
$("#stop_button").addClass("disabled");
|
||||||
|
$("#stop_button_navbar").addClass("hidden disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
module.Monitor.prototype._bindUI = function() {
|
module.Monitor.prototype._bindUI = function() {
|
||||||
|
$("#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));
|
||||||
|
|
||||||
|
$("#ignite_button").click(function() {
|
||||||
|
this._disable_all();
|
||||||
|
$.getJSON("/do/ignite", function(data) {
|
||||||
|
if (data.type == "error")
|
||||||
|
alert(data.msg, data.error);
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$("#stop_button, #stop_button_navbar").click(function() {
|
||||||
|
this._disable_all();
|
||||||
|
$.getJSON("/do/stop", function(data) {
|
||||||
|
if (data.type == "error")
|
||||||
|
alert(data.msg, data.error);
|
||||||
|
else if (data.type == "success") {
|
||||||
|
$("#current_output").val(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$("#current_output").mouseup(function(e) {
|
||||||
|
$.getJSON("/do/set?value="+(e.target.value / 1000), function(data) {
|
||||||
|
if (data.type == "error")
|
||||||
|
alert(data.msg, data.error);
|
||||||
|
else if (data.type == "success")
|
||||||
|
$("#current_output_text").text(e.target.value/10 + "%");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
|
@ -107,14 +160,17 @@ var tempgraph = (function(module) {
|
||||||
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.update_temp(data);
|
||||||
|
else if (data.type == "state") {
|
||||||
|
this.set_state(data.state);
|
||||||
|
}
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
$("#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.Monitor.prototype._disable_all = function() {
|
||||||
|
$("button").addClass("disabled");
|
||||||
|
$("input").attr("disabled", "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
module.temp_to_C = function(temp) { return temp; }
|
module.temp_to_C = function(temp) { return temp; }
|
||||||
module.temp_to_F = function(temp) {
|
module.temp_to_F = function(temp) {
|
||||||
|
@ -126,8 +182,3 @@ var tempgraph = (function(module) {
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
}(tempgraph || {}));
|
}(tempgraph || {}));
|
||||||
|
|
||||||
d3.json("temperature.json", function(error, data) {
|
|
||||||
monitor = new tempgraph.Monitor(data);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -43,13 +43,7 @@
|
||||||
height:500px;
|
height:500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
rect.pane {
|
#stop_button_navbar {
|
||||||
cursor: move;
|
|
||||||
fill: none;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
#stop-button {
|
|
||||||
margin-left:10px;
|
margin-left:10px;
|
||||||
margin-right:10px;
|
margin-right:10px;
|
||||||
}
|
}
|
||||||
|
@ -92,12 +86,12 @@
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
<li class="active"><a href="#" id="temp_scale_F">°F</a></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_C">°C</a></li>
|
||||||
<li><a href="#" id="temp_scale_cone">Δ</a></li>
|
<!--<li><a href="#" id="temp_scale_cone">Δ</a></li>-->
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#" id="current_time">Time</a></li>
|
<li><a href="#" id="current_time">Time</a></li>
|
||||||
<li><a href="#" id="current_output_text">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>
|
<li><button id="stop_button_navbar" class="btn btn-primary navbar-btn hidden" href="#">Stop</button></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!--/.nav-collapse -->
|
</div><!--/.nav-collapse -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,13 +106,13 @@
|
||||||
|
|
||||||
<div class="btn-group btn-group-justified row-space">
|
<div class="btn-group btn-group-justified row-space">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-primary disabled"><span class="glyphicon glyphicon-stop"></span> Off</button>
|
<button id="stop_button" type="button" class="btn btn-primary disabled"><span class="glyphicon glyphicon-stop"></span> Off</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group output-slider">
|
<div class="btn-group output-slider">
|
||||||
<input id="current_output" type="range" min=0 max=1000 class="btn btn-default" />
|
<input id="current_output" type="range" min=0 max=1000 value=0 class="btn btn-default" disabled="disabled" />
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-fire"></span> Ignite</button>
|
<button id="ignite_button" type="button" class="btn btn-danger"><span class="glyphicon glyphicon-fire"></span> Ignite</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -159,5 +153,12 @@
|
||||||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
<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_graph.js"></script>
|
||||||
<script type="text/javascript" src="temp_monitor.js"></script>
|
<script type="text/javascript" src="temp_monitor.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var monitor;
|
||||||
|
d3.json("temperature.json", function(error, data) {
|
||||||
|
monitor = new tempgraph.Monitor(data);
|
||||||
|
monitor.set_state("{{ state }}");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import random
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
@ -23,7 +24,7 @@ tempsample = namedtuple("tempsample", ['time', 'temp'])
|
||||||
class MAX31850(object):
|
class MAX31850(object):
|
||||||
def __init__(self, name="3b-000000182b57", smooth_window=4):
|
def __init__(self, name="3b-000000182b57", smooth_window=4):
|
||||||
self.device = "/sys/bus/w1/devices/%s/w1_slave"%name
|
self.device = "/sys/bus/w1/devices/%s/w1_slave"%name
|
||||||
self.temps = deque(maxlen=smooth_window)
|
self.history = deque(maxlen=smooth_window)
|
||||||
self.last = None
|
self.last = None
|
||||||
|
|
||||||
def _read_temp(self):
|
def _read_temp(self):
|
||||||
|
@ -41,9 +42,8 @@ class MAX31850(object):
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
"""Blocking call to retrieve latest temperature sample"""
|
"""Blocking call to retrieve latest temperature sample"""
|
||||||
temp = self._read_temp()
|
self.history.append(self._read_temp())
|
||||||
self.last = time.time()
|
self.last = time.time()
|
||||||
self.history.append(temp)
|
|
||||||
return self.temperature
|
return self.temperature
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -54,16 +54,26 @@ class MAX31850(object):
|
||||||
return tempsample(self.last, sum(self.history) / float(len(self.history)))
|
return tempsample(self.last, sum(self.history) / float(len(self.history)))
|
||||||
|
|
||||||
class Simulate(object):
|
class Simulate(object):
|
||||||
def __init__(self, regulator):
|
def __init__(self, regulator, smooth_window=4):
|
||||||
self.regulator = regulator
|
self.regulator = regulator
|
||||||
|
self.history = deque(maxlen=smooth_window)
|
||||||
|
self.last = None
|
||||||
|
|
||||||
|
def _read_temp(self):
|
||||||
|
time.sleep(.8)
|
||||||
|
return max([self.regulator.output, 0]) * 1000. + 15+random.gauss(0,.2)
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
time.sleep(.8)
|
self.history.append(self._read_temp())
|
||||||
|
self.last = time.time()
|
||||||
return self.temperature
|
return self.temperature
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def temperature(self):
|
def temperature(self):
|
||||||
return tempsample(time.time(), self.regulator.output * 1000. + 15)
|
if self.last is None or time.time() - self.last > 5:
|
||||||
|
return self.get()
|
||||||
|
|
||||||
|
return tempsample(self.last, sum(self.history) / float(len(self.history)))
|
||||||
|
|
||||||
class Monitor(threading.Thread):
|
class Monitor(threading.Thread):
|
||||||
def __init__(self, cls=MAX31850, **kwargs):
|
def __init__(self, cls=MAX31850, **kwargs):
|
||||||
|
|
Ładowanie…
Reference in New Issue