Ajax с flask для оперативного обновления данных датчика на веб-странице - PullRequest
0 голосов
/ 17 января 2020

Я пытался заставить этот flask сервер обновлять себя данными, сгенерированными из oop, который запускается по сценарию .py, когда пользователь вызывает его с помощью кнопки pu sh на веб-странице. Я искал рекомендуемые решения и видел websockets (sockets.io), ajax, nodejs подходят. Я понимаю, что мне нужно реализовать некоторую форму js в моем проекте, и ajax выглядело наиболее простым (так я думал). У меня всего около 3 недель опыта программирования на python. В основном я ищу примеры, близкие к тому, что я хочу, а затем пытаюсь изменить их в соответствии со своими потребностями, но не нашел примеров того, что я ищу. Даже тогда моя общая новизна в программировании означает, что чем больше примеров я «прихватываю», тем больше у меня шансов ухудшить общую структуру того, чего я уже достиг.

Цель

Цель состоит в том, чтобы обновить значение, отображаемое на странице, без перезагрузки, но вместо этого js обновляет значение каждую секунду. Значение генерируется из счетчика ax = x + 1 в моем .py файле. Это будет заменено входами датчиков, собранными из моего Rpi позже.

Фактические результаты

Когда я запускаю текущий код,

  1. мои html элементы публикуются дважды, поэтому я вижу то, что я поместил в файл index. html дважды, хотя вторые элементы кнопки фактически не реагируют на нажатия,

  2. Я также получаю бесконечный поток сообщений в моем терминале window.

  3. Нажатие на элементы кнопки больше не выполняет мой l oop в файле .py и вместо этого отображает «Метод не разрешен»

Что я пробовал

Я пытался реализовать setTmeout в моем файле html как способ перезвонить приложению python и получить обновленное значение ( х = х + 1) каждую секунду. Я читал сообщения об использовании setTimeout как способ решения проблем с помощью setInterval. Из-за разнообразия способов, которыми я видел ajax используемые вызовы и учебные ресурсы, в основном ориентированные на формы, базы данных и приложения чата, большинство моих поисков не приносят мне ничего нового, что могло бы помочь в этом. В настоящее время я делаю ajax учебные пособия, надеясь найти что-то, что я могу использовать, любая помощь будет принята с благодарностью.

ajaxTest.py My python flask file

import threading
import time
from flask import Flask, render_template, jsonify, request
import RPi.GPIO as GPIO
import datetime
from datetime import datetime
from datetime import timedelta
app = Flask(__name__)

bioR_on = False
ledGrnSts = 0
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
air = 21
light = 20
waste = 16
feed = 12
water = 26
pinList = [21,20,16,12,26] 

def pump(pin):
        GPIO.output(pin, GPIO.LOW)
        print(pin,'on')
        time.sleep(1)
        GPIO.output(pin, GPIO.HIGH)
        print(pin, 'off')
        time.sleep(1)

def on(pin):
    GPIO.output(pin, GPIO.LOW)

@app.route("/")
def index(): 
    templateData = {
              'title' : 'Bioreactor output Status!',
              'ledGrn'  : ledGrnSts,
        }
    return render_template('index.html', **templateData)

@app.route('/<deviceName>/<action>', methods = ["POST"])
def start(deviceName, action):
    # script for Pi Relays
    def run():
        if action == "on":
            alarm = datetime.now() + timedelta(seconds =10)
            global bioR_on
            bioR_on = True

            while bioR_on:

                tday = datetime.now()
                time.sleep(1)
                #feed(tday, alarm)
                x=x+1
                return jsonify(x)
                GPIO.setmode(GPIO.BCM)

                for i in pinList:
                    GPIO.setup (i, GPIO.OUT)
                    GPIO.output(i, GPIO.HIGH)
                on(air)
                on(light)
                print(tday)
                if tday >= alarm:
                    print('alarm activated')
              #  run = False
                    pump(waste)
                    print('waste activated')
                    pump(feed)
                    print('feed on')
                    GPIO.cleanup()
                    alarm = datetime.now() + timedelta(seconds =10)
                    print('next feeding time ', alarm)
                    time.sleep(1)
        if action == 'off':
            bioR_on = False
            #return "off"
            GPIO.cleanup()

    thread = threading.Thread(target=run)
    thread.start()

    templateData = {
            'ledGrn'  : ledGrnSts,
    }
    return render_template('index.html', **templateData)


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True, threaded=True)

Мой индекс. html файл

<!DOCTYPE html>
   <head>
      <title>BioReactor Control</title>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <link rel="stylesheet" href='../static/style.css'/>
   </head>
   <body>
        <h1>Actuators</h1>
        <h2> Status </h2>
        <h3> GRN LED ==>  {{ ledGrn  }}</h3>
        <br>
        <h2> Commands </h2>
        <h3> 
            Activate Bioreactor Ctrl ==> 
            <a href="/bioR/on" class="button">TURN ON</a>  
            <a href="/bioR/off"class="button">TURN OFF</a>
        </h3>
        <h3>
            Current Count 
        </h3>
        <p id="demo"></p>
        <script>
                    setTimeout($.ajax({
                        url: '/<deviceName>/<action>',
                        type: 'POST',
                        success: function(response) {
                            console.log(response);
                            $("#num").html(response);
                        },
                        error: function(error) {
                            console.log(error);
                        }
                    }), 1000);


        </script>
        <h1>Output</h1>
        <h1 id="num"></h1>
   </body>
</html>

Изображение результатов

1 Ответ

0 голосов
/ 17 января 2020

Я создал минимальный код, который использует AJAX для получения нового значения каждую 1 секунду.

Я использую setInterval, чтобы повторять его каждую 1 секунду. Я также использую function(){ $.ajax ... } для создания функции, которая не выполняется сразу, но setInterval будет вызывать ее каждую 1 секунду. Без function(){...} код $.ajax выполнялся при запуске, и его результат использовался как функция, выполняемая каждую 1 секунду - но он ничего не возвращает - поэтому в итоге он обновил значение только один раз (при запуске), а позже setInterval ничего не выполнял.

Я добавил текущее время, чтобы посмотреть, работает ли оно по-прежнему.

buttons функция запуска '/<device>/<action>', которая запускает и останавливает поток, но AJAX использует /update для получения текущего значения.

Я использую render_template_string, чтобы весь код находился в одном файле, чтобы другие люди могли легко скопировать и протестировать его.

Я уменьшил HTML до минимума. Чтобы убедиться, что я поставил <h1> перед скриптом, которому нужны эти теги.

Я не тестировал его с global=True, который может запускать его в новых потоках, и это может вызвать проблемы.

from flask import Flask, request, render_template_string, jsonify
import datetime
import time
import threading

app = Flask(__name__)

running = False # to control loop in thread
value = 0       

def rpi_function():
    global value

    print('start of thread')
    while running: # global variable to stop loop  
        value += 1
        time.sleep(1)
    print('stop of thread')


@app.route('/')
@app.route('/<device>/<action>')
def index(device=None, action=None):
    global running
    global value

    if device:
        if action == 'on':
            if not running:
                print('start')
                running = True
                threading.Thread(target=rpi_function).start()
            else:
                print('already running')
        elif action == 'off':
            if running:
                print('stop')
                running = False  # it should stop thread
            else:
                print('not running')

    return render_template_string('''<!DOCTYPE html>
   <head>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   </head>
   <body>
        <a href="/bioR/on">TURN ON</a>  
        <a href="/bioR/off">TURN OFF</a>
        <h1 id="num"></h1>
        <h1 id="time"></h1>
        <script>
            setInterval(function(){$.ajax({
                url: '/update',
                type: 'POST',
                success: function(response) {
                    console.log(response);
                    $("#num").html(response["value"]);
                    $("#time").html(response["time"]);
                },
                error: function(error) {
                    console.log(error);
                }
            })}, 1000);
        </script>
   </body>
</html>
''')

@app.route('/update', methods=['POST'])
def update():
    return jsonify({
        'value': value,
        'time': datetime.datetime.now().strftime("%H:%M:%S"),
    })

app.run() #debug=True
...