Gnuplot, RRDTool, Python

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
wusa
User
Beiträge: 40
Registriert: Dienstag 18. Februar 2014, 11:08

Hallo Zusammen,

ich stehe vor einem Problem. Ich lese einen DS18B20 Sensor über diese Datei aus:

Code: Alles auswählen

import sys
import os
from time import *

lt = localtime()

tempfile = open("/sys/bus/w1/devices/28-0000053c5e59/w1_slave")
thetext = tempfile.read()
tempfile.close()
tempdata = thetext.split("\n")[1].split(" ")[9]
temp  = float(tempdata[2:])
temp = temp / 1000
temp = round(temp,2)
temp = str(temp)
Datum = strftime("%d.%m.%Y")
Uhrzeit = strftime ("%H:%M:%S")
fobj_out = open ("buero.txt","a")
fobj_out.write(Datum + ", " + Uhrzeit + ", " + temp + "\n")
fobj_out.close()

os.system("gnuplot /home/pi/Desktop/programme/temp.plt")
sys.exit
Das ganz funktioniert auch wunderbar. Ich schreibe dann Datum, Uhrzeit und die Temperatur in eine weitere Datei.
Diese frage ich dann über Gnuplot ab und erzeuge eine Grafik.

Code: Alles auswählen

set title 'Temperaturverlauf'
set xdata time
set timefmt "%H:%M:%S"
set format x "%H:%M"
set ylabel 'Grad Celsius'
set yrange [-5:40]
plot "buero.txt" using 2:3 with line t "test 1", "temp-daten.txt" using 2:3 with line t "test2"
set output "temperatur.jpg"
set terminal jpeg
replot
Ich würde nun gerne die Datei so anpassen, dass ich verschiedene Anzeigen hab. Wie zum Beispiel: 6h, Tag, Monat.

Ich frage mich, ob es vielleicht Sinnvoller ist, eine RRD Datenbank zu verwenden?
Nur leider habe ich überhaupt keine Ahnung von dieser Datenbank. Ich weiß auch nicht wie ich mein Script dazu verwenden kann, die Datenbank zu füllen.
Vielleicht kann mir hier jemand weiterhelfen.

Danke!
BlackJack

@wusa: Erst einmal ein wenig Kritik am bisherigen Programm:

Sternchenimporte sollte man vermeiden. Damit verschleiert man woher Namen kommen und wenn man das öfter anwendet besteht zudem die Gefahr von Namenskollisionen.

Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden. Der Code der das Hauptprogramm ausmacht steckt üblicherweise in einer Funktion die `main()` heisst und über folgendes Idiom aufgerufen wird:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Dann kann man das Modul sowohl als Programm ausführen als auch importieren ohne das das Hauptprogramm automatisch ausgeführt wird. Zum Beispiel um einzelne Funktionen manuell oder automatisiert zu testen, oder in anderen Modulen zu verwenden. Und einige Werkzeuge zum Beispiel zur Extraktion von Dokumentation erwarten dieses Verhalten ebenfalls.

Namen sollte man nicht Abkürzen solange die Abkürzung nicht allgemein bekannt ist. Das betrifft `lt` und `fobj_out`. Und `temp` und `tempdata`, was insbesondere irreführend sein kann weil `temp` sehr oft als Abkürzung für „temporary” verwendet wird, hier aber für „temperature” steht.

Grossbuchstaben verwendet man konventionell nur bei Konstanten, die komplett gross geschrieben werden, und bei Klassennamen. Näheres steht im Style Guide for Python Code.

`lt` wird zudem zwar definiert, aber nirgends benutzt. Was stattdessen gemacht wird, ist ein Programmfehler, denn wenn man für Datum- und Zeitangabe jeweils erneut die aktuelle Zeit ermitteln lässt, besteht um Mitternacht herum die Gefahr das diese beiden Werte überhaupt nicht zusammen passen, wenn man nämlich das Datum am einen Tag ermittelt und die Zeit vom Nächsten, dann können die beiden Werte zusammengenommen fast 24 Stunden vom korrekten Wert abweichen.

Dateien würde man in aktuellem Python-Code nach Möglichkeit immer mit der ``with``-Anweisung zusammen öffnen. Dann ist sichergestellt, dass die Datei auch wieder geschlossen wird, egal aus welchem Grund der Block der ``with``-Anweisung verlassen wird.

Das `the` bei `thetext` hat keinen Mehrwert für den Leser und sollte weggelassen werden.

Man muss nicht jede einzelne Teil-Operation einer Berechnung an einen Namen binden. Und besser auch nicht alles an den gleichen Namen wenn sich dabei der Typ ändert.

`round()` benötigt man nur sehr selten, nämlich wenn man vor dem weiterrechnen mit einem Wert runden möchte. Die Funktion ist nicht uneingeschränkt für das beschränken eines Wertes auf eine Anzahl von Nachkommastellen geeignet, denn nicht alle Zahlen können genau dargestellt werden. Kleines Beispiel 0.1 mit 50 Nachkommastellen angezeigt, einmal direkt und einmal auf eine Nachkommastelle gerundet:

Code: Alles auswählen

In [11]: '%0.50f' % 0.1
Out[11]: '0.10000000000000000555111512312578270211815834045410'

In [12]: '%0.50f' % round(0.1, 1)
Out[12]: '0.10000000000000000555111512312578270211815834045410'
Genauer an 0.1 kommt man halt nicht heran.

Wenn man die Anzahl der Nachkommastellen für die *Anzeige* beschränken möchte, dann macht man das nicht mit `round()` sondern mit den entsprechenden Formatanweisungen bei den Platzhaltern für `format()` auf Zeichenketten oder für den ``%``-Operator auf Zeichenketten.

Womit wir beim Zusammensetzen von Zeichenketten und Werten wären: Das macht man in Python nicht mit wiederholtem ``+`` und der `str()`-Funktion sondern eben mit `format()` und Platzhaltern. Hier wäre es dann auch von Vorteil wenn man statt `time` das `datetime`-Modul verwendet, weil die Datentypen dort über Formatangaben in den Platzhaltern formatiert werden können und man keine zusätzliche Funktion verwenden muss.

`os.system()` hat einige besonderheiten und Probleme. Zum Ausführen von externen Programmen sollte man das `subprocess`-Modul verwenden.

Der Aufruf von `sys.exit()` am Ende eines Programms ist nicht notwendig, und vor allem müsste man die Funktion auch tatsächlich *aufrufen* und nicht nur einfach das Funktionsobjekt referenzieren.

Ich lande dann ungefähr bei so etwas (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function

from datetime import datetime as DateTime
from subprocess import call


SENSOR_FILENAME = '/sys/bus/w1/devices/28-0000053c5e59/w1_slave'
TEMPERATURES_FILENAME = 'buero.txt'
PLOT_FILENAME = '/home/pi/Desktop/programme/temp.plt'


def main():
    now = DateTime.now()

    with open(SENSOR_FILENAME) as sensor_file:
        temperature = (
            int(sensor_file.read().split('\n')[1].split(' ')[9]) / 1000
        )

    with open(TEMPERATURES_FILENAME, 'a') as out_file:
        out_file.write(
            '{0:%d.%m.%Y}, {0:%H:%M:%S}, {1:.2f}\n'.format(now, temperature)
        )

    call(['gnuplot', PLOT_FILENAME])


if __name__ == '__main__':
    main()
Beim Lesen der Temperatur machst Du es Dir wahrscheinlich ein wenig zu einfach. Wenn ich mich recht erinnere ist in den Daten die man über diese Schnittstelle auslesen kann auch eine Prüfsumme enthalten und die Information ob diese mit den Daten übereinstimmt. Es kann also durchaus vorkommen das an der vom Code in eine Zahl umgewandelte Stelle gar keine gültige Temperatur steht, sondern irgendein undefinierter Wert.

Bezüglich RRDTool: Das ist im Grunde keine Python-Frage weil die Python-Anbindung dafür bei den Aufrufen im Grund den Aufrufen der Kommandozeilenwerkzeuge von RRDTool enpsricht, man sich also damit auseinandersetzen müsste.
Antworten