Ajax 用烧瓶实时更新网页上的传感器数据
Ajax with flask for real time esque updates of sensor data on webpage
我一直在尝试让这个 flask 服务器在用户通过网页上的按钮调用时,使用 .py 脚本上 运行 循环生成的数据来更新自身。我一直在研究推荐的解决方案,并看到出现了 websockets (sockets.io)、ajax、nodejs。我知道我需要在我的项目中实现某种形式的 js,ajax 看起来是最简单的(我是这么认为的)。我在 python 中只有大约 3 周的编程经验。主要是我寻找接近我想要的例子,然后尝试修改它以满足我的需要,但还没有找到我正在寻找的任何例子。即便如此,我对编程的总体陌生程度意味着我 "tack on" 的示例越多,我就越有可能降低我已经完成的工作的整体结构。
目标
目标是在不重新加载的情况下更新页面上显示的值,而是让 js 每秒更新一次值。该值是从我的 .py 文件中的 x=x+1 计数器生成的。这将被稍后从我的 Rpi 收集的传感器输入所取代。
实际结果
当我运行当前代码时,
我的 html 元素被重复发布,所以我看到了我在 index.html 文件中输入的内容两次,尽管第二个按钮元素实际上没有响应点击,
我的终端也收到源源不断的帖子window.
单击按钮元素不再执行我在 .py 文件中的循环,而是显示 "Method not allowed"
我试过的
我尝试在我的 html 文件中实施 setTmeout 作为回调 python 应用程序并每秒获取更新值 (x=x+1) 的一种方式。我已经阅读了有关使用 setTimeout 作为使用 setInterval 处理问题的方法的帖子。由于我看到 ajax 调用的方式多种多样,而且学习资源主要针对表单、数据库和聊天应用程序构建,所以我的大部分搜索都没有给我带来任何新的东西,让我可以从中学习帮助。我目前正在做 ajax 教程,希望能遇到一些我可以使用的东西,我们将不胜感激。
ajaxTest.py 我的 python 烧瓶文件
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)
我的index.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>
Picture of results
我创建了最少的代码,它使用 AJAX
每 1 秒获取一个新值。
我用setInterval
每1秒重复一次。我还使用 function(){ $.ajax ... }
创建函数,该函数不会立即执行,但 setInterval
将每 1 秒调用一次。没有 function(){...}
代码 $.ajax
在开始时执行,其结果用作每 1 秒执行一次的函数 - 但它 returns 什么都没有 - 所以最后它只更新一次值(在开始时)和以后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
我一直在尝试让这个 flask 服务器在用户通过网页上的按钮调用时,使用 .py 脚本上 运行 循环生成的数据来更新自身。我一直在研究推荐的解决方案,并看到出现了 websockets (sockets.io)、ajax、nodejs。我知道我需要在我的项目中实现某种形式的 js,ajax 看起来是最简单的(我是这么认为的)。我在 python 中只有大约 3 周的编程经验。主要是我寻找接近我想要的例子,然后尝试修改它以满足我的需要,但还没有找到我正在寻找的任何例子。即便如此,我对编程的总体陌生程度意味着我 "tack on" 的示例越多,我就越有可能降低我已经完成的工作的整体结构。
目标
目标是在不重新加载的情况下更新页面上显示的值,而是让 js 每秒更新一次值。该值是从我的 .py 文件中的 x=x+1 计数器生成的。这将被稍后从我的 Rpi 收集的传感器输入所取代。
实际结果
当我运行当前代码时,
我的 html 元素被重复发布,所以我看到了我在 index.html 文件中输入的内容两次,尽管第二个按钮元素实际上没有响应点击,
我的终端也收到源源不断的帖子window.
单击按钮元素不再执行我在 .py 文件中的循环,而是显示 "Method not allowed"
我试过的
我尝试在我的 html 文件中实施 setTmeout 作为回调 python 应用程序并每秒获取更新值 (x=x+1) 的一种方式。我已经阅读了有关使用 setTimeout 作为使用 setInterval 处理问题的方法的帖子。由于我看到 ajax 调用的方式多种多样,而且学习资源主要针对表单、数据库和聊天应用程序构建,所以我的大部分搜索都没有给我带来任何新的东西,让我可以从中学习帮助。我目前正在做 ajax 教程,希望能遇到一些我可以使用的东西,我们将不胜感激。
ajaxTest.py 我的 python 烧瓶文件
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)
我的index.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>
Picture of results
我创建了最少的代码,它使用 AJAX
每 1 秒获取一个新值。
我用setInterval
每1秒重复一次。我还使用 function(){ $.ajax ... }
创建函数,该函数不会立即执行,但 setInterval
将每 1 秒调用一次。没有 function(){...}
代码 $.ajax
在开始时执行,其结果用作每 1 秒执行一次的函数 - 但它 returns 什么都没有 - 所以最后它只更新一次值(在开始时)和以后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