Working profile running, events still need work

master
James Gao 2014-11-29 20:27:48 -08:00
rodzic 42af7b85aa
commit 79e1403f03
12 zmienionych plików z 2097 dodań i 1465 usunięć

Wyświetl plik

@ -18,7 +18,7 @@
#define NO_PORTC_PINCHANGES
#define DISABLE_PCINT_MULTI_SERVICE
#include <Stepper.h>time.strftime('%Y-%m-%d_%I:%M%P.log')
#include <Stepper.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_MAX31855.h>
@ -48,7 +48,7 @@ float next_step;
unsigned long next_temp;
unsigned char motor_active = false;
unsigned long stepper_target = 0;
unsigned int n_clicks = 0; //Number of full rotations
int n_clicks = 0; //Number of full rotations
boolean limit_state = false;
unsigned long limit_last;
@ -165,6 +165,7 @@ void i2c_action(int nbytes) {
break;
case 'I':
analogWrite(PIN_IGNITE, buffer[0]);
status.ignite = buffer[0];
break;
}

Wyświetl plik

@ -1,7 +1,7 @@
class pushbutton : public CallBackInterface
{
public:
uint8_t n_clicks;
int n_clicks;
uint8_t pin;
unsigned int interval;
unsigned long last;
@ -11,8 +11,6 @@ class pushbutton : public CallBackInterface
n_clicks = 0;
last = 0;
init();
state = digitalRead(pin);
};
void cbmethod() {
last = millis();
@ -31,12 +29,11 @@ class pushbutton : public CallBackInterface
private:
int dir;
boolean state;
void init () {
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
PCintPort::attachInterrupt(pin, this, FALLING);
PCintPort::attachInterrupt(pin, this, CHANGE);
};
};

Wyświetl plik

@ -10,7 +10,8 @@ import logging
import states
import PID
logger = logging.getLogger("kiln.manager")
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class TempLog(object):
def __init__(self, history, interval=60, suffix=""): #save data every 60 seconds
@ -71,7 +72,7 @@ class Manager(threading.Thread):
def __getattr__(self, name):
"""Mutates the manager to return State actions
If the requested attribute is a function, wrap the function
such that returned objects which are States indicate a state change
such that returned obejcts which are States indicate a state change
"""
attr = getattr(self.state, name)
if hasattr(attr, "__call__"):
@ -86,10 +87,12 @@ class Manager(threading.Thread):
self.state = output(self)
self.state_change.set()
self.notify(dict(type="state", state=output.__name__))
logger.info("Switching to state '%s'"%output.__name__)
elif isinstance(output, tuple) and issubclass(output[0], states.State):
newstate, kwargs = output
self.state = newstate(self, **kwargs)
self.notify(dict(type="state", state=newstate.__name__))
logger.info("Switching to state '%s'"%newstate.__name__)
elif isinstance(output, dict) and "type" in output:
self.notify(output)
elif output is not None:
@ -103,11 +106,13 @@ class Manager(threading.Thread):
self.running = False
self.state_change.set()
class Profile(threading.Thread):
"""Performs the PID loop required for feedback control"""
def __init__(self, schedule, therm, regulator, interval=1, start_time=None, callback=None,
Kp=.025, Ki=.01, Kd=.001):
super(Profile, self).__init__()
self.daemon = True
self.schedule = schedule
self.therm = therm
self.regulator = regulator
@ -146,7 +151,7 @@ class Profile(threading.Thread):
setpoint = frac * (temp1 - temp0) + temp0
self.pid.setPoint(setpoint)
temp = self.therm.temperature
temp = self.therm.temperature.temp
pid_out = self.pid.update(temp)
if pid_out < 0: pid_out = 0
if pid_out > 1: pid_out = 1

Wyświetl plik

@ -35,8 +35,10 @@ class MainHandler(ManagerHandler):
files = os.listdir(paths.profile_path)
fixname = lambda x: cone_symbol.sub(r'Δ\1', 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__,
state_data=json.dumps(self.manager.state.status),
profiles=profiles,
)
@ -129,7 +131,7 @@ class WebApp(object):
if __name__ == "__main__":
try:
import manager
kiln = manager.Manager(simulate=False)
kiln = manager.Manager(simulate=True)
app = WebApp(kiln)
kiln._send = app.send

Wyświetl plik

@ -1,6 +1,7 @@
"""Based on the pattern provided here:
http://python-3-patterns-idioms-test.readthedocs.org/en/latest/StateMachine.html
"""
import json
import time
import traceback
import manager
@ -10,6 +11,10 @@ class State(object):
def __init__(self, manager):
self.parent = manager
@property
def status(self):
return dict()
def run(self):
"""Action that must be continuously run while in this state"""
ts = self.parent.therm.get()
@ -25,7 +30,7 @@ class Idle(State):
_ignite(self.parent.regulator, self.parent.notify)
return Lit, dict(history=self.history)
def start(self, schedule, start_time=None, interval=5):
def start_profile(self, schedule, start_time=None, interval=5):
_ignite(self.parent.regulator, self.parent.notify)
kwargs = dict(history=self.history,
schedule=json.loads(schedule),
@ -46,7 +51,7 @@ class Lit(State):
except:
return dict(type="error", msg=traceback.format_exc())
def start(self, schedule, start_time=None, interval=5):
def start_profile(self, schedule, start_time=None, interval=5):
kwargs = dict(history=self.history,
schedule=json.loads(schedule),
start_time=float(start_time),
@ -54,7 +59,7 @@ class Lit(State):
)
return Running, kwargs
def stop(self):
def shutoff(self):
_shutoff(self.parent.regulator, self.parent.notify)
return Cooling, dict(history=self.history)
@ -79,7 +84,7 @@ class Cooling(State):
_ignite(self.parent.regulator, self.parent.notify)
return Lit, dict(history=self.history)
def start(self, schedule, start_time=None, interval=5):
def start_profile(self, schedule, start_time=None, interval=5):
_ignite(self.parent.regulator, self.parent.notify)
kwargs = dict(history=self.history,
schedule=json.loads(schedule),
@ -91,11 +96,15 @@ class Cooling(State):
class Running(State):
def __init__(self, parent, history, start_time=None, **kwargs):
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)
self.start_time = self.profile.start_time
self.history = history
@property
def status(self):
return dict(start_time=self.start_time, schedule=self.profile.schedule)
def _notify(self, therm, setpoint, out):
self.parent.notify(dict(
type="profile",
@ -107,7 +116,8 @@ class Running(State):
def run(self):
if self.profile.completed:
self.parent.notify(dict(type="profile",status="complete"))
#self.parent.notify(dict(type="profile",status="complete"))
print "Profile complete!"
_shutoff(self.parent.regulator, self.parent.notify)
return Cooling, dict(history=self.history)
@ -117,11 +127,13 @@ class Running(State):
self.profile.stop()
return Lit, dict(history=self.history)
def stop(self):
def stop_profile(self):
self.profile.stop()
_shutoff(self.parent.regulator, self.parent.notify)
return Cooling, dict(history=self.history)
def shutoff(self):
return self.stop_profile()
def _ignite(regulator, notify):
try:

Wyświetl plik

@ -100,8 +100,8 @@ var tempgraph = (function(module) {
return {x:new Date(d.time*1000), y:this.scalefunc.scale(d.temp)};
}
module.Monitor.prototype.setState = function(name) {
if (name == "Lit") {
module.Monitor.prototype.setState = function(name, data) {
if (name == "Lit" || name =="Running") {
$("#ignite_button").addClass("disabled");
$("#current_output").removeAttr("disabled");
$("#stop_button").removeClass("disabled");
@ -113,8 +113,14 @@ var tempgraph = (function(module) {
$("#stop_button").addClass("disabled");
$("#stop_button_navbar").addClass("hidden disabled");
$("#profile_select").removeClass("disabled");
} else if (name == "Profile") {
}
if (name == "Running") {
if (data !== undefined) {
this.setProfile(data.schedule, new Date(data.start_time*1000));
}
this.profile.setState(true);
} else if (this.profile === undefined) {
this.profile.setState(false);
}
}
module.Monitor.prototype._bindUI = function() {
@ -134,7 +140,7 @@ var tempgraph = (function(module) {
$("#stop_button, #stop_button_navbar").click(function() {
this._disable_all();
$.getJSON("/do/stop", function(data) {
$.getJSON("/do/shutoff", function(data) {
if (data.type == "error")
alert(data.msg, data.error);
else if (data.type == "success") {

Wyświetl plik

@ -1,11 +1,10 @@
var tempgraph = (function(module) {
module.Profile = function(graph, scale, schedule, start_time, running) {
module.Profile = function(graph, scale, schedule, start_time) {
var end = schedule[schedule.length-1][0];
this.length = end;
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;
@ -291,7 +290,6 @@ var tempgraph = (function(module) {
}
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);
@ -325,14 +323,22 @@ var tempgraph = (function(module) {
}
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();
var start_time = this.time_start instanceof Date ? this.time_start : new Date();
var params = {
schedule:JSON.stringify(this.schedule),
start_time:start_time.getTime() / 1000.,
interval:1,
};
$.post("/do/start_profile", params, function(resp){
var obj = JSON.parse(resp);
if (obj.type == "error") {
alert(obj.msg);
} else {
this.setState(true);
this.time_start = start_time;
}
}.bind(this));
}
return module;
}(tempgraph || {}));

Wyświetl plik

@ -152,7 +152,7 @@ class StepperSim(object):
time.sleep(1)
def stop(self):
print "stopping"
print "stopping simulated regulator"
class Regulator(threading.Thread):
def __init__(self, maxsteps=4500, minsteps=2480, speed=150, ignite_pin=None, flame_pin=None, simulate=False):
@ -216,8 +216,8 @@ class Regulator(threading.Thread):
def off(self, block=True):
logger.info("Shutting off gas")
#self.stepper.step(-self.current, self.speed, block=block)
self.stepper.home()
self.stepper.step(-self.current, self.speed, block=block)
#self.stepper.home()
self.current = 0
def set(self, value, block=False):
@ -251,11 +251,12 @@ class Breakout(object):
self.max = maxsteps
def exit():
if self.output != 0:
if self.device.motor != 0:
self.off()
atexit.register(exit)
def ignite(self, start=2800, delay=10):
logger.info("Igniting system")
self.device.ignite = 255
time.sleep(delay)
self.device.motor = start
@ -279,3 +280,4 @@ class Breakout(object):
def off(self):
self.device.motor = 0
logger.info("Shutting off regulator")

Wyświetl plik

@ -140,8 +140,7 @@
var monitor;
d3.json("temperature.json", function(error, data) {
monitor = new tempgraph.Monitor(data);
monitor.setState("{{ state }}");
monitor.setState("{{ state }}",{% autoescape None %} JSON.parse('{{ state_data }}'));
});
</script>
</body>

Wyświetl plik

@ -54,7 +54,7 @@ class MAX31850(object):
return tempsample(self.last, sum(self.history) / float(len(self.history)))
class Simulate(object):
def __init__(self, regulator, smooth_window=8):
def __init__(self, regulator, smooth_window=120):
self.regulator = regulator
self.history = deque(maxlen=smooth_window)
self.last = None

Wyświetl plik

@ -1,4 +1,4 @@
use <../lib/utils.scad>;
use <utils.scad>;
use <../lib/28byj.scad>;
thick = 4;
@ -44,8 +44,8 @@ module holder() {
module slot(length) {
hull() {
cylinder(r=1, h=6);
translate([length,0]) cylinder(r=1, h=6);
cylinder(r=1.5, h=6, $fn=16);
translate([length,0]) cylinder(r=1.5, h=6, $fn=16);
}
}
@ -61,16 +61,16 @@ module holder() {
//switch tab
intersection() {
translate([0,0,-3.3-thick]) scale([1, 1.4]) difference() {
cylinder(r=36+thick, h=16+34+3, $fn=128);
translate([0,0,-1]) cylinder(r=36, h=16+32+3-2,$fn=128);
cylinder(r=36+10+thick, h=16+34+3, $fn=128);
translate([0,0,16]) cylinder(r=36+10, h=32+3-2,$fn=128);
}
translate([36+thick-20, -width/2, -3.3-thick]) cube([50, 20, 16+32+4]);
translate([36+thick-15, -width/2, -3.3-thick]) cube([50, 20, 16+32+4]);
}
}
//Screw slots for switch
translate([20,-width/2+10-9.5/2, -3.3-thick+16+32-1]) {
translate([3,0]) slot(5);
translate([3,9.5]) slot(5);
translate([25,-width/2+10-9.5/2, -3.3-thick+16+32-1]) {
translate([3,0]) slot(10);
translate([3,9.5]) slot(10);
}
//Slot for wire to ensure no gear tangling

Plik diff jest za duży Load Diff