Anfängerin - Grundsatzfrage - Datetime Mute Einstellung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sunshineh
User
Beiträge: 22
Registriert: Dienstag 3. Januar 2017, 11:21

Hallo,

ich habe vorab den Post mit der Hausaufgabe, die hier nicht eingestellt werden soll usw. gelesen.
Nun ich habe keine Hausaufgabe mehr zu machen. Früher hab ich mal C und C++ gelernt, das ist aber ne Weile her und nun möchte ich als Hobby für mich ein Projekt auf dem Raspberry umsetzen (Sensoren auslesen, Werte in Datenbanken schreiben und auswerten).

Ich habe gemerkt, dass in Python vieles für mich außergewöhnlich anders und somit auch schwierig ist. Ich habe auch etwas Angst davor, dass ich Probleme total falsch angehe, da ich mir alles selbst zusammensuche und entsprechend zusammenflicke bis es passt. Ein komplettes Fachbuch durchzuarbeiten und mit Grundbeispielen zu starten, dafür fehlt mir aktuell leider die Zeit.

Nun gut, soweit zum Hintergrund.
Mein aktueller Stand ist, dass ich zwei Sensoren - je in einem String - auslesen kann. Aus dem jeweiligen String habe ich die Zeit in folgendem Format extrahiert:
2018-07-07 12:40:11
Da die Sensoren mehrmals - sogar innerhalb einer Sekunde - anschlagen, möchte ich mit der Variable "sec_pir1_mute" usw. festlegen, dass die nächste Erkennung dieses Wertes z.b. erst nach 60 Sekunden erfolgend soll.

Gibt es hier bereits Projekte / Bibliotheken, die ich mit nutzen kann, oder muss ich mir dies Schritt für Schritt selbst überlegen?

Denn Sensoraufruf möchte ich in eine SQLite Tabelle eintragen. Hole ich mir also bei jedem neuen Eintrag aus der Tabelle die letzte Uhrzeit heraus, oder speicher ich diese Uhrzeit lokal in einer Variable zwischen?

Wie geschrieben, ich möchte nicht, dass mir wer Arbeit abnimmt, sondern eher ein paar Tipps und Ratschläge (vielleicht auch Empfehlungen für ein gutes Tutorial), so dass ich nicht mir was Zusammenstöpsel, was so eigentlich nicht "OK" ist.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

So ganz wird mir nicht klar, was Du eigentlich machen willst. Die Uhrzeit kommt ja vom System und nicht von irgendwelchen Sensoren. Wenn Du im Sekundentakt Sensoren auslesen willst, dann mußt Du das eben tun, indem Du mit time.time, bzw. time.monotonic, Zeiträume mißt und mit time.sleep dann auf den nächsten Zeitpunkt wartest.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich denke die Zeit für die Grundlagen solltest Du Dir nehmen. Es muss ja kein dickes Buch sein. In der Python-Dokumentation ist ein Tutorial das man IMHO mal durchgearbeitet haben sollte. Wenn man schon eine oder mehr Programmiersprachen kann, dann kommt man das meiner Erfahrung nach auch recht zügig durch. Und es ist ja nicht nur trockener Lesestoff, sondern viel mit selber in der Python-Shell interaktiv ausprobieren. Wenn einem etwas nicht ganz klar ist, kann man das oft durch live herumspielen mit den Objekten herausfinden und beheben. Ich habe immer eine Python-Shell offen (verwende IPython) wo ich wenn im Programm mal etwas nicht klappt, live ausprobieren kann wo genau es hakt, schauen welche Funktionen/Methoden es gibt, und gegebenfalls auch schnell mal in den Quelltext schauen kann.

Ich denke das mit der Zeitverzögerung ist schneller selbst implementiert als das man sich eine Bibliothek dafür sucht. Eventuell ist bei Wraptor etwas dabei. Allerdings willst Du ja abhängig vom Ergebnis verzögern wenn ich das richtig verstanden habe.

Ich würde die letzte Zeit im Programm merken. Die Daten liegen dort ja bereits vor, warum also noch mal aus der Datenbank holen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@sunshineh: Ob Du den Zeitpunkt der letzten Messung benötigst, hängt auch von der Genauigkeit ab, mit der Du arbeiten möchtest. Im Prinzip könntest Du sowas machen:

Code: Alles auswählen

while True:
    hole sensordaten
    bearbeite und speichere sensordaten
    warte 60 Sekunden

Falls das nicht genau genug ist, dann kann es sinnvoll sein, den Zeitpunkt des Lesens der Sensordaten im Programm zu speichern (wie __blackjack__ schon schrieb), um ggf. Korrekturen vornehmen zu können.
sunshineh
User
Beiträge: 22
Registriert: Dienstag 3. Januar 2017, 11:21

Ich habe es nun mal so umgesetzt. Soweit ich es sehe funktioniert es.
Ist das vom "Programmierstil so OK?

Code: Alles auswählen

reactive_pir = 60  #Zeit nach der der Sensor wieder aktiv ist
last_time_pir1 = time.time()-reactive_pir
    
def execute(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline()   
        if nextline == '' and process.poll() is not None:
            break
        
        if nextline.find(" : 53534")>0: #PIR1 Nachricht gefunden
            nextline = nextline[11:30] + " PIR1 \n"
            time_pir1 = time.time()
            global last_time_pir1
            if time_pir1 - last_time_pir1 > reactive_pir:
                print("%.10f seconds" % (time_pir1 - last_time_pir1 ))
                sys.stdout.write(nextline)
                sys.stdout.flush()
                last_time_pir1 = time.time()

Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Konstanten werden KOMPLETT_GROSS_GESCHRIEBEN. ``global`` solltest Du ganz schnell wieder vergessen. Alles was eine Funktion oder Methode ausser Konstanten braucht, wird als Argument(e) übergeben, Ergebnisse als Rückgabewerte an den Aufrufer zurückgegeben. Wenn man über globale Variablen kommuniziert, muss man am Ende den gesamten Code auf einmal im Blick haben und verstehen, weil man ja nie weiss wo und wann die globale Variable von irgendwo verändert wird.

``shell=True`` sollte man bei `Popen` nur verwenden wenn man *tatsächlich* eine Shell zwischen dem eigenen Programm und dem was man da ausführen will, benötigt. Was eher selten der Fall ist.

`nextline` sollte einfach nur `line` heissen.

Die ``while True:``-Schleife könnte sehr wahrscheinlich eine ``for``-Schleife über die Zeilen in `process.stdout` sein.

Um zu testen ob eine Zeichenkette in einer anderen enthalten ist, gibt es den ``in``-Operator, also ``a in b`` statt ``b.find(a) > 0``.

`last_time_pir1` würde ich `previous_time_pir1` nennen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sunshineh
User
Beiträge: 22
Registriert: Dienstag 3. Januar 2017, 11:21

Vielen Dank!!

Ich habe alles berücksichtigt und geändert, nur weiß ich grad nicht, wie ich die "globale" Variable verhindern kann.
Ich möchte ja eigentlich die Variable "previous_time_pir1" nur einmalig initialisieren. Kann ich das auch in der "execute" machen?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sunshineh: Ob Du `previous_time_pir1` komplett in die Funktion ziehen kannst, hängt davon ab wie sich das über Aufrufe der Funktion hinweg verhalten soll. Wenn die Semantik so bleiben soll wie jetzt, kannst Du das in der Hauptfunktion initialisieren und dann an die `execute()`-Funktion übergeben und am Ende den aktuellen Wert an den Aufrufer zurück geben. Der kann den dann beim nächsten Aufruf dann wieder übergeben.

Eventuell stellt sich das Problem so auch nicht wenn man die Funktion dort etwas auseinander nimmt und die Messungen von der Verarbeitung trennt. Generatoren/Generatorfunktionen sind was ganz tolles um eine Schleife auf mehrere Funktionen zu verteilen. Du hast da ja einen Iterator über Zeilen. Aus dem kannst Du die Messungen rausfiltern und einen Iterator über die Messergebnisse erstellen. Und die kannst Du dann an anderer Stelle weiterverarbeiten.

Last but not least: Wenn man zu viele Argumente in der Gegend herum reicht um globalen Zustand zu vermeiden, kann man überlegen was man an Zustand und Funktionalität sinnvoll in Klassen zusammenfassen kann.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nur mal so als Anregung was mit Generatorfunktionen möglich ist (ungetestet):

Code: Alles auswählen

import subprocess
import sys
import time
from contextlib import closing

REACTIVE_PIR = 60  # Zeit nach der der Sensor wieder aktiv ist.


def execute(command):
    process = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )
    try:
        for line in process.stdout:
            if ' : 53534' in line:
                yield line[11:30]
    finally:
        if not process.poll():
            process.terminate()
        process.wait()


def throttle(iterable, seconds):
    previous_time = time.time() - seconds
    for item in iterable:
        now = time.time()
        if now - previous_time >= seconds:
            print('{0:.10f} seconds'.format(now - previous_time))
            yield item
            previous_time = now
        

def main():
    with closing(execute(['some_command', 'argument'])) as pir_events:
        for line in throttle(pir_events, seconds):
            sys.stdout.write(line + ' PIR1\n')
            sys.stdout.flush()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sunshineh
User
Beiträge: 22
Registriert: Dienstag 3. Januar 2017, 11:21

Vielen herzlichen Dank für diese Hilfe!

Allerdings wird das Programm sofort beendet und ich weiß nicht was ich ändern muss, damit das Programm läuft und auf ein Signal wartet. Ich denke, das ich diesen Teil ändern muss:

Code: Alles auswählen

def main():
    with closing(execute(['some_command', 'argument'])) as pir_events:
        for line in throttle(pir_events, seconds):
            sys.stdout.write(line + ' PIR1\n')
            sys.stdout.flush()
Ursprünglich , sieht die Main-Funktion so aus:

Code: Alles auswählen

def main():     # Program start from here
        setup()
        try:
                execute('rtl_433 -G -F json') 
        except KeyboardInterrupt:  # When 'Ctrl+C' is pressed, the child program destroy() will be  executed.
                pass
        finally:
                destroy()

if __name__ == '__main__':     # Program start from here
        main()
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sunshineh: Ich kenne/kannte das externe Programm und dessen Argumente ja nicht, darum muss aus dem ``['some_command', 'argument']`` natürlich eine Liste für den tatsächlichen Aufruf werden (``['rtl_433', '-G', '-F', 'json']``) und das ``if __name__ == '__main__':``-Idiom für den Aufruf fehlte natürlich noch damit `main()` auch tatsächlich ausgeführt wird.

Jetzt wo der externe Aufruf bekannt ist, würde ich aber auch die Verarbeitung der Programmausgabe dringend anpassen, denn wenn dieses Program JSON Lines ausspuckt, dann wird klar das die Verarbeitung als Textzeilen nicht robust ist. Um den ':' müssen keine Leerzeichen sein und die Reihenfolge von Schlüssel/Wert-Paaren ist bei JSON auch nicht garantiert. Da also etwas zwischen absolut angegebenen Indizes auszuschneide ist gar keine gute Idee.

Es gibt in der Standardbibliothek ein `json`-Modul das man dafür einsetzen kann.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten