Sziasztok!
Ti hogyan raknátok be egy adatrögzítő eredményeit egy SQL DB-be Python programmal?
Van esetleg valakinek erre bevált profi megoldása?
Az adatok kb. így jönnek soros porton keresztül:
[]
['DHTxx', 'test!']
[]
[]
['Humidity:', '44.80%', 'Temperature:', '24.70°C', '76.46°F', 'Heat', 'index:', '24.40°C', '75.91°F']
[]
['Humidity:', '44.80%', 'Temperature:', '24.70°C', '76.46°F', 'Heat', 'index:', '24.40°C', '75.91°F']
[]
['Humidity:', '44.80%', 'Temperature:', '24.70°C', '76.46°F', 'Heat', 'index:', '24.40°C', '75.91°F']
[]
['Humidity:', '44.80%', 'Temperature:', '24.70°C', '76.46°F', 'Heat', 'index:', '24.40°C', '75.91°F']
Sajnos persze lehet adatvesztés, korrupt betűk stb..
Jelenleg ez a kis script-et tákoltam össze. Kiírja az adatokat, de nem tudom hogy lehet ezt szépen átrakni egy DB-be, hogy
- megmaradjon a két tizedes + a mértékegységek is
- Hiba esetén csak dobja el a sort, ne rögzítsen semmit.
- User/Pass login a DB-be.
#!/usr/bin/env python3
import serial
import time
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.flushInput()
humidity = 0
temperature = 0
heatIndex = 0
while True:
ser_bytes = ser.readline()
#decoded_bytes = float(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
decoded_bytes = ser_bytes[0:len(ser_bytes)-2].decode("utf-8")
#print(decoded_bytes)
list = decoded_bytes.split()
print(list)
time.sleep(5)
#humidity = list[0:2]
#temperature = list[3]
#heatIndex = list[6]
#print('Páratartalom:' + humidity)
#print('Hőmérséklet:' + temperature)
#print('HeatIndex:' + heatIndex)
Később majd a DB-ből szeretnénk web-es chartot rajzolni az adatokból.
- 962 megtekintés
Hozzászólások
Szia!
Először is nem hagyhatom ki a szokásos stackoverflow választ:
Nem jó amit akarsz, ilyen adatokat kimondottan idősoros adatok tárolására való DB-be szoktak rakni (TSDB). (A valóság pedig az, hogy sokszor bőven jó ezt SQL-ben is, szóval ezzel nincs para.)
Hogy konstruktív legyek:
először is készíts egy adatbázis sémát amibe be tudod ezt illeszteni: vedd külön a számértéket a mértékegységtől, gondold át, hogy kell-e egyeltalán mértékegység (ha mindig csak celsiusban jön hőmérséklet, tök felesleges elrakni a mértékegységet, max odaírod az oszlop leírásába, hogy ezek itt celsius adatok)
Aztán a beérkező sorra érdemes lehet egy reguláris kifejezést írni, ez két okból is hasznos:
- ha a sor hibás, vagy nem tartalmaz minden adatot, akkor eleve megy a kukába, tehát ezzel már nem kell foglalkozni
- egyből ki tudod nyerni az értékeket változókba, amit aztán rögtön írhatsz a DB-be.
Végül, attól függően, hogy milyen SQL az az SQL, válassz egy python library-t amivel tudod kezelni, PostgreSQL-hez pl. ott a psycopg
- A hozzászóláshoz be kell jelentkezni
És ne felejtsen el a bejövő adathoz valamilyen ellenőrzőösszeget odatenni, hogy látszódjon ha korrupt.
- A hozzászóláshoz be kell jelentkezni
Időpontot (timestampet) honnan párosítassz a mért adatokhoz? Jön valami dedikált deviceról, vagy a gépről, ahol a szkript fut?
Azért a konkrét projekttől nagyon függ a megoldás, pl ha több szenzorról gyűjtessz adatot, akkor kell valami szenzor ID, ha több locationről jön az adat, akkor át kell gondolni, hogy melyik időzónában van a timestamp, mennyire kritikus, hogy minden méréshez meglegyen minden adat, vagy fontosabb, hogy a részleges adatból is elmentődjön ami használható. Legyen e dedikálkt táblája a méréstípusoknak (esetleg mert másik szenzorról másmilyen adatok jönnek be), vagy akarod e generalizálni az adatstruktúrát: lásd tidy data
Ilyesmikre gondolok.
Csaba
- A hozzászóláshoz be kell jelentkezni
DB schema az első, ahogy ezt előttem is jelezték.
Mivel a Celsius / Fahrenheit egyértelműen konvertálható, elég csak az egyiket tárolni.
Én ezt tárolnám be:
- time: timestamp vagy datetime
- humidity: float
- temperature: float
- heat: float
Persze lehet csicsázni...
Python kód (write only módban, nincs tesztelve, mysql backenddel)
sqlconnect = {"host": db_host, "user": db_user, "password": db_pwd, "database": db_database}
def on_message(...):
try:
...
sql = "INSERT INTO %s ('time', 'humidity', 'temperature', 'heat') VALUES('%s', '%f', '%f', '%f')" % ("tableName", timeStr, humidity, temperature, heat)
db = my.connect(**sqlconnect)
cursor = db.cursor()
cursor.execute(sql)
except my.Error as e:
...
finally:
if (db.is_connected()):
cursor.close()
db.close()
- A hozzászóláshoz be kell jelentkezni
Hello.
Én is éppen ilyesmivel szórakozom. :-)
SQLite adatbázist ajánlom, mert egyszerű pythonból kezelni, és ehhez tényleg nem kell semmi extra az adatbázismotortól. Én egyenlőre még .txt fájlban gyűjtöm az adatokat (csak a láthatóság miatt, hogy egyszerűen lehessen rápillantani bárhonnan) de ha lesz egy kis időm átirányítom az adatbázisba. Amit már elkészítettem, teszteltem csak az éles adatok nem oda mennek, egyenlőre manuálisan töltöm bele az adatokat.
Adatbázisban most csak számokat fogsz tárolni, a megfelelő formátumokban. Mértékegységet az oszlopok nevében lehet megadni csicsázás képpen. Aztán persze kiíratásnál lehet bármit utána írni...
SQLite adatbázis generálás, és írás bele, fájlból:
import sqlite3
from sqlite3 import Error
from readlines import read_data
def create_connection(db_file):
""" create a database connection to the SQLite database
specified by db_file
:param db_file: database file
:return: Connection object or None
"""
conn = None
try:
conn = sqlite3.connect(db_file)
except Error as e:
print(e)
return conn
def insertmany_data(conn, data):
sql = '''INSERT INTO bme280_data (datetime_int, temperature, pressure, humidity)
VALUES (strftime('%s', ?), ?, ?, ?);'''
cur = conn.cursor()
cur.executemany(sql, data)
return cur.lastrowid
if __name__ == '__main__':
database = r"home_temp.db"
wtf = read_data('weather_BME280_sensor_data.txt')
print(wtf[0], "...", "\n", "...", len(wtf), "...", "\n", "...", wtf[-1])
sql = '''INSERT INTO bme280_data (datetime_int, temperature, pressure, humidity)
VALUES (strftime('%s', ?), ?, ?, ?);'''
# create a database connection
conn = sqlite3.connect("home_temp.db")
cur = conn.cursor()
cur.executemany(sql, wtf)
conn.commit()
conn.close()
Egyéb infok: #https://www.sqlitetutorial.net/sqlite-python/
- A hozzászóláshoz be kell jelentkezni
egyenlore a pizzat vagod (felbe), itt viszont "egyelore" kellene.
- A hozzászóláshoz be kell jelentkezni
Sehogy. Erre valo barmelyik timeseries DB, mint Influxdb, Prometheus, Graphite, etc. (Influxdb ajanlott, mert a Prometheus nem rendelkezik reliable storage-el hosszu tavra; ha kell az adatok hosszu tavu tarolsa). Esetleg barmelyik nyers noSQl columnar store. De semmi esetre sem relacios adatbazis.
- A hozzászóláshoz be kell jelentkezni
WARNING: https://www.influxdata.com/products/editions/ influxdb cluster kepes verzioja,
nem ingyenes . Ill mint lathato `nyilt core rendszer` flagje van, nem teljesen nyilt .
Egy 9600 baundos soros portos cummohoz, meg szinte barmi jo, semmi fancy nem kell.
Relacios adatbazsi is jo!
Csak, ne feledd az idexet az ido belyegen...
Amit nem lehet megirni assemblyben, azt nem lehet megirni.
- A hozzászóláshoz be kell jelentkezni
Nem beszéltem clusteringrol. Reliable storage megoldásról beszéltem. Azt sajnos maga a Prometheus mögött álló team is elismeri, hogy olyat ők nem tudnak nyújtani.
A 9600-as baudot elpazarolni sql-re meg igazan butaság lenne, ha van egyszerubb megoldás ami még sávszélességet is spórol. Mondjuk graphite.
Az biztos hogy timeseriest sql-be rakni menő dolog, csak értelme nincs. :)
- A hozzászóláshoz be kell jelentkezni
"Nem beszéltem clusteringrol. Reliable storage megoldásról beszéltem. Azt sajnos maga a Prometheus mögött álló team is elismeri, hogy olyat ők nem tudnak nyújtani. "
Talan resetnel tobb hetes adat sorkat dobal el ?
Nem hinnem.
Amije nincs prometheusnak az a beepitett cluster / HA classikus ertelemben.
Storage reszt teheted HA -va es mesz Active-passive fele.
Illetve futatathatsz tobb peldanyt ami ugyan abbol az adatforrasbol taplalkozik (active-active).
Nem feltetlen lesz zsir ugyanaz mindegyik peldany adata, de mindegyik ~ perckent vett
adat sort fog tartalmazni, csak nem feltetlen ugyan azokbol nanosecundumokbol.
Szugseg esten lekerdezeskor ossze is fesulheted.
Ha egy node nem bir ell az osszes scrapperel, szet lehet dobni tobb nodra,
ami szinten `explicit` user config vagy automalizacio, nem beepitett feature .
prometheus szinten nincs felkeszulve nagyon nagy adat sorokra,
tipikusan 2~4 hetes , ~10 sec felbontassal meg elbirja .
"Az biztos hogy timeseriest sql-be rakni menő dolog, csak értelme nincs. :)"
Azt hittem manapsag minden trendi ami nem SQL, meg ha hulyeseg is . ;-)
Ha valami kicsi egyszerure kene, talan:
https://oss.oetiker.ch/rrdtool/ (letezik python binding)
Amit nem lehet megirni assemblyben, azt nem lehet megirni.
- A hozzászóláshoz be kell jelentkezni
Egyetertunk, de gondolom a kerdezo nem akar storage HA-t csinalni Prometheus ala.
Egy sime egynodos Influxdb viszont hosszu tavra megoldja a tarolast a columnar storage-ben. Sajna nalunk volt mar, hogy a kethetes adatot sem tudtuk visszakerdezni a Prometheusbol, mert egyszeruen szetcseszodott valahogy. Torles segitett, csak igy nullarol kezdhettuk adott pillanatban gyujteni a logokat.
Na basszus a jo regi kis megbizhato rrd-t meg el is felejti az ember a timeseries miatt. Bahh, meg en is oregszem. :D
A lenyeg, hogy megint nem tudjuk mi a cel, csak azt tudjuk hogy o az adott eszkozokkel akarjha elerni akar jo az ugy akar nem. :D
- A hozzászóláshoz be kell jelentkezni
Meg egy erekesseg:
https://www.elastic.co/blog/elasticsearch-as-a-time-series-data-store
elasticsearch cluster epithethot baratibb licenssel (open source)
es szinten kezdodhet egy nodeal es bovitheto nagyon naggya.
Amit nem lehet megirni assemblyben, azt nem lehet megirni.
- A hozzászóláshoz be kell jelentkezni
Mi mondjuk pont az elastic-ot hasznaljuk mindenhol erre, de pssszt... :D
Mondjuk azert erdemes odafigyelni arra, hogy az elastic nem kimondottan timeseries-re van, ugye a storage engine a lucene a reverse indexing-re van (nem eveletlenul full-text search db-nek hivjak), azaz inkabb document store (persze van columnar resze is...de ez nem az). Persze lehet hasznalni es mondjuk aggregaciora messze jobb, mint a graphite, de ha nincs mas oka (ML, stb.) akkor en inkabb valamilyen columnar noSQL-t valasztanek, mert az pont erre jo/lett kitalalva.
Az is igaz, hogy en az ES-hez retek jobban ugyhogy valoszinuleg akkor is azt hasznalnam, ha tudom mas jobb lenne. :D
- A hozzászóláshoz be kell jelentkezni
RRDtool amúgy miben másabb mint egy sqlite?
Máshogy menti le az adatokat?
- A hozzászóláshoz be kell jelentkezni
Igen, fix az adatbazis merete, sosem fogy el a hely, mert ez egy ring buffer.
- A hozzászóláshoz be kell jelentkezni
Nekem most egy ilyenem van:
rrdtool.create( rrdFile,
'--step', str( 300 ),
'--no-overwrite',
'DS:temperature:GAUGE:'+str(measure_interval*2)+':-273:5000',
'DS:humidity:GAUGE:'+str(measure_interval*2)+':0:100',
'RRA:AVERAGE:0.5:1:576', # 48h of 5min data
'RRA:AVERAGE:0.5:12:1440',# 1440 * 1 hour blocks = 60 days
'RRA:AVERAGE:0.5:288:1825', # 1824 * 1 day = 5 years
'RRA:AVERAGE:0.5:8640:600' # 600 * 1 Month = 50 years
)
Az egyedi értékeket ez nem is menti le, hanem csak az átlagokat állítgatja?
- A hozzászóláshoz be kell jelentkezni
Ennyi adat bőven belefér a DynamoDB free tier-be, amit boto-val kényelmesen tudsz használni Python-ból. A Time to Live funkcióval pedig a régieket automatikusan tudod törölni.
Gond letudva.
- A hozzászóláshoz be kell jelentkezni
ha kicsit többet akarsz Pythonnal a jovoben adatbazisozni, szvsz megeri az SQLAlchemyt hasznalni
“Any book worth banning is a book worth reading.”
- A hozzászóláshoz be kell jelentkezni
En a peewee-t ajanlom mind ketto ugyan arra jo. Normalisan tudsz modelleket definialni meg adatbazis strukturat.
--
"ssh in a for loop is not a solution" – Luke Kanies, Puppet developer
- A hozzászóláshoz be kell jelentkezni
Mellékszál, de küldjed ide (is): https://iotguru.cloud és kapsz belőle szép grafikonokat, mint ez: https://iotguru.cloud/field/gy8rYTk1GxtKm6cgAM0R5w/temperature
- A hozzászóláshoz be kell jelentkezni
en is timeseries db-t javasolnek. ezt hasznalom itthon ilyesmire: https://graphiteapp.org/ . akar pythonnal is feedelheted, de van sok mas tool, pl collectd, amivel egyutt tud mukodni.
- A hozzászóláshoz be kell jelentkezni