Я делаю веб-приложение в python, которое читает температуру и влажность. Необходимо каждую минуту считывать датчик и сохранять значения в базе данных в sqlite. На веб-странице я представляю данные в виде графика, используя javascript (диаграмма. js). Цель, которой я не достиг, состоит в том, чтобы график обновлялся по минутам. Таким образом, каждое измерение обновляет график, который я вижу на веб-странице.
Как есть, это работает, но я думаю, что я неправильно использую соединения с базой данных, поскольку у меня было много ошибок параллелизма с базой данных, но я все еще не не знаю, как правильно реализовать это. Я знаю, что не могу использовать базу данных из разных потоков, но единственное обнаруженное мною исправление - запуск сеанса каждый раз, когда я что-то ищу.
Итак, мои вопросы: есть ли хороший способ использовать База данных действовать аналогично тому, что я имею? И самое главное, как мне отправлять данные на javascript каждый раз, когда я делаю измерение? Я нашел веб-сокеты, опросы различных типов и Quart в качестве альтернативы - flask, а также socketIO и . Насколько я понял из многих вопросов здесь, Long Polling устарел (но ответ на многие вопросы), и веб-розетки - это путь к go. Должен ли я использовать socketIO или Quart или просто постоянный javascript опрос для этого простого приложения?
Это то, что я использую в настоящее время:
#!/usr/bin/python3
# Web Server Gateway WSGI
from flask import Flask, render_template, make_response, request #, redirect, url_for
from databasis import Databasis
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from flask import Flask, render_template, request, redirect, url_for
from datetime import datetime, timedelta, timezone
# _________
# SETUP:
# Flask webserver app
app = Flask(__name__, static_url_path='/static')
# Scheduler Setup
jobStores = { 'default': SQLAlchemyJobStore(url='sqlite:////home/thermostat/alarms.db'), }
scheduler = BackgroundScheduler(jobstores=jobStores)
scheduler.start()
# measurement database setup
databasis = Databasis()
# _________
# server routes
@app.route("/")
def index():
meas = databasis.getLastNmeasures(10)
temps = [pt.temperature for pt in meas]
hums = [pt.humidity for pt in meas]
datetimes = [pt.timestamp for pt in meas]
templateData = {
'time' : meas[0].timestamp,
'temps' : temps,
'humidities' : hums,
'datetimes' : datetimes,
}
return render_template('index.html', **templateData)
def measureTH():
global databasis
temp = 25 # Here goes a sensor
hum = 55
databasis.addTHMeasurement(temp,hum)
meas = databasis.getLastNmeasures(5)
print("temperature {:.3f} deg C".format(meas[0].temperature))
print("humidity {:.3f} %".format(meas[0].humidity))
return 'hello world'
scheduler.add_job(measureTH, 'cron',\
day_of_week='*',hour='*',minute='*',\
id=str(1), replace_existing=True,\
args=[])
# _________
# main function
if __name__ == "__main__":
app.debug = True
app.use_debugger = True
app.use_reloader = False
app.run(host='0.0.0.0', port=80, threaded=False)
# _________
# on exit
и класс данных:
#!/usr/bin/python3
from sqlalchemy import Column, Integer, Float, DateTime, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from datetime import datetime, timedelta
Base = declarative_base()
class Databasis():
def __init__(self):
# Create an engine that stores data in the .db file.
engine = create_engine('sqlite:////home/Documents/projects/measurements.db')
Base.metadata.bind = engine
Base.metadata.create_all(engine)
DBSession = sessionmaker(bind=engine)
Session = scoped_session(DBSession)
self.session = Session
def addTHMeasurement(self,temp,hum):
entry = TempHumMeasurement(\
timestamp=datetime.now(),\
temperature=temp,\
humidity=hum,\
location='room1')
session = self.session()
session.add(entry)
session.commit()
self.session.remove()
return
def getLastNDays(self,n):
starttime = datetime.now() - timedelta(days=n)
return self.session.query(TempHumMeasurement).\
filter(TempHumMeasurement.timestamp>starttime).all()
def getLastNmeasures(self,n):
return self.session.query(TempHumMeasurement).\
order_by(TempHumMeasurement.timestamp.desc()).\
limit(n).all()
# Define a Table in the database
class TempHumMeasurement(Base):
__tablename__ = 'temphummeasurement'
id = Column(Integer, primary_key = True)
timestamp = Column(DateTime(timezone=True))
temperature = Column('temperature', Float)
humidity = Column('humidity', Float)
location = Column('location', String(32))
и, наконец, индекс. html
<!DOCTYPE html>
<head>
<title>Temperature & Humidity History</title>
<link rel="stylesheet" type="text/css" href='../static/style.css'/>
<script src="static/Chart.min.js"></script>
</head>
<body>
<div class="content">
<h1>Temperature</h1>
<h3> Last measurement {{ temps[0] }} deg at {{ time.strftime("%d %m %Y, %H:%M") }}
<a href="/" class="button">REFRESH</a></h3>
</div>
<div class="content">
<canvas id="temperature" width="600" height="400"></canvas>
</div>
<script>
var chartData = {
labels : [
{% for item in datetimes %}
"{{ item }}",
{% endfor %}
],
datasets : [{
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
data : [
{% for item in temps %}
"{{ item }}",
{% endfor %}
]
}
]
}
var ctx = document.getElementById("temperature").getContext("2d");
steps = 4
max = 3
//draw chart
var temperature = new Chart(ctx, {
type: 'line',
data: chartData,
});
function addData(chart, label, data) {
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
}
</script>
</body>
</html>