Last active
June 3, 2017 21:03
-
-
Save imbellish/cb6cc9fc570f156e1c27accc7816aa99 to your computer and use it in GitHub Desktop.
inspired by pycon 2017 talk: https://www.youtube.com/watch?v=MtHscXjWbVs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import print_function | |
import automat | |
import os, sys, logging, json, time | |
from flask import Flask, request | |
from Queue import Queue | |
from threading import Thread | |
class BaseMachine(object): | |
_machine = automat.MethodicalMachine() | |
q = Queue() | |
def __init__(self): | |
self._enabled = True # set your enable condition here | |
if self._enabled: | |
self.enable() | |
else: | |
self.disable() | |
# set up a single consumer for running tasks | |
worker = Thread(target=self.worker, args=(self.q,)) | |
worker.setDaemon(True) | |
worker.start() | |
def worker(self, q): | |
"A worker method that executes tasks" | |
while True: | |
method = q.get() | |
print(method.__name__, 'returned', method()) | |
q.task_done() | |
########## | |
# STATES # | |
########## | |
@_machine.state(serialized='init', initial=True) | |
def initial(self): | |
"Initializing state" | |
@_machine.state(serialized='disabled') | |
def disabled(self): | |
"Disabled state" | |
@_machine.state(serialized='busy') | |
def busy(self): | |
"Busy doing core things" | |
@_machine.state(serialized='idle') | |
def idle_state(self): | |
"waiting for command or task" | |
################################ | |
# INPUTS - METHODS YOU CAN USE # | |
################################ | |
@_machine.input() | |
def enable(self): | |
"Enter an enabled state" | |
@_machine.input() | |
def idle(self): | |
"Sets to an idle state" | |
@_machine.input() | |
def disable(self): | |
"Enter a disabled state" | |
@_machine.input() | |
def hello_world(self): | |
"Say hello" | |
######################## | |
# OUTPUTS - WORKHORSES # | |
######################## | |
@_machine.output() | |
def _disabled(self): | |
self._enabled = False | |
self._since = time.time() | |
return True | |
@_machine.output() | |
def _enabled(self): | |
self._enabled = True | |
self._since = time.time() | |
return True | |
@_machine.output() | |
def _busy(self): | |
self._busy = True | |
self._since = time.time() | |
return True | |
@_machine.output() | |
def _idle(self): | |
self._busy = False | |
self._since = time.time() | |
return True | |
@_machine.output() | |
def _hello_world(self): | |
print("Hello world!") | |
# reset the state to idle after finishing, | |
# or use an input to move to a new task | |
self.idle() | |
############################ | |
# SERIALIZER - FOR CLIENTS # | |
############################ | |
@_machine.serializer() | |
def get_state(self, state): | |
return { 'state': state, | |
'since': self._since } | |
# set initial state | |
initial.upon(enable, enter=idle_state, outputs=[_enabled]) | |
initial.upon(disable, enter=disabled, outputs=[_disabled]) | |
# allow enable, disable at any time, regardless of enabled/disable state | |
idle_state.upon(disable, enter=disabled, outputs=[_disabled]) | |
idle_state.upon(enable, enter=idle_state, outputs=[_enabled]) | |
disabled.upon(enable, enter=idle_state, outputs=[_enabled]) | |
disabled.upon(disable, enter=disabled, outputs=[_disabled]) | |
# only idle() is able to switch from busy to idle_state | |
busy.upon(idle, enter=idle_state, outputs=[_enabled]) | |
# add your methods here | |
idle_state.upon(hello_world, enter=busy, outputs=[_hello_world]) | |
if __name__ == '__main__': | |
mgr = BaseMachine() | |
from flask import Flask, request | |
app = Flask(__name__) | |
@app.route('/') | |
def status(): | |
return json.dumps(mgr.get_state()) | |
@app.route('/<input>', methods=['GET', 'POST']) | |
def command(input): | |
""" | |
Corresponds to a machine input methods | |
""" | |
if request.method == 'POST': | |
if input not in dir(mgr): | |
return json.dumps({'error': 'Invalid command'}) | |
method = getattr(mgr, input) | |
mgr.q.put(method) | |
time.sleep(0.1) | |
return json.dumps(mgr.get_state()) | |
else: | |
return json.dumps(mgr.get_state()) | |
print("Try these commands in a separate terminal:") | |
print("import requests") | |
print("# Get the status of the state machine...") | |
print("requests.get('http://localhost:5000/').json()") | |
print("# Disable the state machine...") | |
print("requests.post('http://localhost:5000/disable').json()") | |
print("# Re-enable the machine...") | |
print("requests.post('http://localhost:5000/enable').json()") | |
print("# Say hello") | |
print("requests.post('http://localhost:5000/hello_world').json()") | |
app.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment