Я пытаюсь сделать что-то вроде State Machine во Flask.Когда мое приложение запускается, я запускаю фоновую задачу, которая начинается с функции «state_0 ()» и последовательно переходит к другим функциям «state_1 ()», затем «state_2 ()», а затем возвращается к «state_0 ()».
Этот s0 -> s1 -> s2 -> s0 -> s1 -> ... постоянно работает с самого начала, когда запускается веб-приложение, и я хочу сделать это "socketio.emit ('refresh',state) "внутри этих функций, чтобы можно было соответствующим образом обновить FrontEnd.
Для этого я использую поток с @ app.before_first_request, который в основном выполняет" state_0 () "с использованием контекста приложения.
Проблема в том, что emit работает только для функции state_0 ().
Мой текущий код можно упростить следующим образом:
myapp.py
from flask import Flask, render_template, current_app
from flask_socketio import SocketIO, emit
import threading
import time, json
DATA = {'state': '0'}
N = 5 # Fake pooling
# initialize Flask
app = Flask(__name__)
socketio = SocketIO(app)
def state_0():
# Update DATA
global DATA
DATA['state'] = '0'
socketio.emit('refresh', json.dumps(DATA), broadcast=True)
print("State " + DATA['state'] + "!")
# Dummy pooling simulation
i = 0
while i < N:
print("Dummy workload " + DATA['state'] + "...")
time.sleep(3)
i += 1
state_1()
def state_1():
# Update DATA
global DATA
DATA['state'] = '1'
socketio.emit('refresh', json.dumps(DATA), broadcast=True)
print("State " + DATA['state'] + "!")
# Dummy pooling simulation
i = 0
while i < N:
print("Dummy workload " + DATA['state'] + "...")
time.sleep(3)
i += 1
print("Alarm(s) detected!")
state_2()
def state_2():
# Update DATA
global DATA
DATA['state'] = '2'
socketio.emit('refresh', json.dumps(DATA), broadcast=True)
print("State " + DATA['state'] + "!")
# Dummy pooling simulation
i = 0
while i < N:
print("Dummy workload " + DATA['state'] + "...")
time.sleep(3)
i += 1
state_0()
@app.before_first_request
def activate_job():
def run_job():
with app.app_context():
state_0()
thread = threading.Thread(target=run_job)
thread.start()
@app.route('/')
def index():
return render_template('myindex.html')
# Every time Client refresh the web, we broadcast the current DATA
@socketio.on('connect')
def on_connect():
print("[Server.on_connect]: A new connection!")
emit('refresh', json.dumps(DATA), broadcast=True)
if __name__ == '__main__':
socketio.run(app, debug=True)
myindex.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>State Machine</title>
</head>
<body>
<p>State machine...</p>
<form action="">
<input id ="s0" type="radio" name="state0"> State 0<br>
<input id ="s1" type="radio" name="state1"> State 1<br>
<input id ="s2" type="radio" name="state2"> State 2<br>
</form>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = io.connect('http://' + document.domain + ':' + location.port);
// verify our websocket connection is established
socket.on('connect', function () {
console.log('Websocket connected!');
});
socket.on('refresh', function (DATA) {
var DATA = JSON.parse(DATA);
console.log('[refresh] Frontend has updated info! Now we are in state ' + DATA.state);
console.log('[refresh] All variables are:');
console.log(DATA);
switch (DATA.state) {
case '0':
document.getElementById('s2').checked = ""
document.getElementById('s0').checked = "checked"
break;
case '1':
document.getElementById('s0').checked = ""
document.getElementById('s1').checked = "checked"
break;
case '2':
document.getElementById('s1').checked = ""
document.getElementById('s2').checked = "checked"
break;
default:
// code block
}
});
</script>
</body>
</html>
Я также пытался сделать это:
@app.before_first_request
def activate_job():
def run_job():
with app.app_context():
state_0() # this function emits to frontend!
state_1() # but this function does not emit nothing
thread = threading.Thread(target=run_job)
thread.start()
Я пытался использовать с app.app_context () здесь и там, но работает только с функцией state_0 ().
Надеюсь, мой вопрос понятен.Обратите внимание, что я хочу, чтобы все было как можно проще (именно поэтому я использую глобальную переменную для сохранения состояния и почему я не использую Celery для выполнения фоновых задач).Это не для производства.
Надеюсь, что кто-то может предоставить помощь!
Примечание. Чтобы протестировать этот пример в Ubuntu, создайте «virtualenv env», а затем «pip3 install колба flashc-socketio eventlet»"и запустите" python3 myapp.py "