Как использовать socketio.emit внутри множества функций потока - PullRequest
0 голосов
/ 17 февраля 2019

Я пытаюсь сделать что-то вроде 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 "

...