DIY Klingel für Haus programmieren - Robustheit

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.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Idee war es, die for-Schleife zu vereinfachen, war dann aber zu einfach.
Du mußt also nur die Funktion verstehen, um sie zu reparieren. Raten ist aber kein guter Weg.

Wegen der zwei Prozesse: Du hast ein Timing-Problem, das mal auftritt und mal nicht, funktioniert hat das also nie wirklich richtig.
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Homer-S: Welchen Wert muss denn `id` an der Stelle haben wo es am Ende benutzt wird? Und wo bekommst Du diesen Wert her? Ganz sicher nicht durch ein Schlüsselwort was man mal eben davor schreibt. Der Code ist logisch falsch angeordnet. Das muss man durch nachdenken und korrigieren lösen, nicht durch Googlen und/oder raten.

Das mit den beiden Prozessen hat in Deiner Version genau so funktioniert wie jetzt auch. Es wird ein Prozess gestartet der als Server auf einem Port lauscht und fast gleichzeitig einer der sich mit diesem Port verbindet. Das kann immer ”funktionieren”, oder ”nie”, oder mal ja, mal nein. Es ist letztlich Glückssache ob der erste Prozess zu dem Zeitpunkt wo der zweite sich verbinden will, schon soweit ist, oder eben noch nicht. Eine klassische Wettlaufsituation („race condition“) bei nebenläufiger Programmierung. Ich würde da einfach nach dem starten des ersten Prozesses in einer Schleife eine Weile versuchen mich mit dem Port zu verbinden und erst den zweiten Prozess starten, wenn das funktioniert hat. Oder wenn das innerhalb ein oder zwei Sekunden nicht geklappt hat, das Programm mit einer Fehlermeldung abbrechen. Natürlich nach dem beenden des ersten Prozesses.

Und das ``time.sleep(31)`` ist falsch/überflüssig, weil man *erst* auf den `convert`-Prozess warten muss und dann erst den `video`-Prozess terminieren darf. Ja, theoretisch könnte auch das ”immer” funktionieren, muss es aber nicht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

Ich glaube den 'id' Fehler konnte ich lösen. Ganz sicher bin ich mir nict, aber es erscheint mir logisch:

Code: Alles auswählen

def send_message(chatroomids, message=None, photo=None):
    """ versendet die Klingelbenachrichtigung """
    files = {}
    for id in chatroomids:
        params = {"chat_id": id}
        if message:
            params["text"] = message
            cmd = "sendMessage"
        if photo:
            files = {"photo": photo}
            cmd = "sendPhoto"
        url = TELEGRAM_URL + BOT_TOKEN + "{}{}/{}".format(TELEGRAM_URL, BOT_TOKEN, cmd)
        r = requests.get(url, params=params, files=files)
        result = r.json()
        print(result)
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

Hallo,

ich muss leider schon wieder schreiben. Ich habe mir den Punkt mit den zwei Prozessen zu Herzen genommen.

Gibt es eine Funktion/Lib, mit der man die Webseite sowas wie anpingen kann und dann den anderen Prozess zu starten?

Dann ist mir (langsam erkenne ich auch etwas mehr) aufgefallen, dass die count() funktion nicht definiert ist.

Code: Alles auswählen

def main():
    try:
        setup()
        for ring_count in count():
            if DEVELOPERMODE or gpio.wait_for_edge((OPTOKOPPLER), gpio.FALLING):
                record(ring_count)
wenn ich das richtig verstanden habe, ist das nicht das, was ich will, oder?
Mein Ansinnen ist, dass jedes Klingeln in einer Variable gespeichert wird und in der Message an mich als 1tes mal 2tes mal usw angezeigt wird.

Oder ersetzt diese for Schleife mein "while"?

Danke
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Lies dir doch mal die Dokumentation zu count() durch. Das beantwortet hoffentlich die Frage zur Schleife.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Natürlich ist `count` definiert, wird nämlich aus itertools importiert. Und was es macht, steht in der Dokumentation dazu.

Prüfen, ob der Server schon bereit ist, würde ich ja `lsof` benutzen, oder halt direkt was von psutil.
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

Tut mir Leid, wenn ich hier nochmal nachfragen muss, ich steig nur nicht durch.

Was genau bedeutet diese Zeile?

for ring_count in count():

wenn ich alles, was ich gelesen habe richtig verstehe, ist count() dafür da, Anzahl Zeichen oder sowas zu zählen, bzw eine Zahlenreihe zu generieren.
Hier ist es aber ja, mehr oder weniger, None.

dann würde man sagen wiederhole für jeden ring_count in count() ...
also 0 in none??

Sorry, da bin ich echt vor einer Mauer, gedanklich.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Was verstehst Du an der Dokumentation nicht?
Hast Du schon ausprobiert, was es tut? Das wäre das einfachste.
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

@Homer-S: Wenn du sagst "es aber ja, mehr oder weniger, None", meinst du dann eher 2/3 None? 1/6 None? Und was ist der Rest, wenn es nur mehr oder weniger None ist? So funktioniert Programmieren in der Regel nicht. Du musst nicht raten was das ist, du kannst es einfach ausprobieren. Sind ja nur 3 Zeilen Code itertools zu importieren, den Schleifenkopf zu schreiben und dann in der Schleife ausgeben zu lassen, was ring_count für einen Wert hat.

Wenn der Entwickler es hinterlegt hat, kommt man übrigens mit help(Objekt) an eine Hilfe im interaktiven Interpreter. Nur für den Fall, dass du die Dokumentation suchst.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Gib einfach

Code: Alles auswählen

from itertools import count 
for i in count():
     print(i)
ein und schau was passiert.
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

sparrow hat geschrieben: Mittwoch 15. Mai 2019, 07:11 @Homer-S: Wenn du sagst "es aber ja, mehr oder weniger, None", meinst du dann eher 2/3 None? 1/6 None? Und was ist der Rest, wenn es nur mehr oder weniger None ist? So funktioniert Programmieren in der Regel nicht. Du musst nicht raten was das ist, du kannst es einfach ausprobieren. Sind ja nur 3 Zeilen Code itertools zu importieren, den Schleifenkopf zu schreiben und dann in der Schleife ausgeben zu lassen, was ring_count für einen Wert hat.

Wenn der Entwickler es hinterlegt hat, kommt man übrigens mit help(Objekt) an eine Hilfe im interaktiven Interpreter. Nur für den Fall, dass du die Dokumentation suchst.
Wenn ich mich so vorsichtig ausdrücke, liegt es daran, dass ich hier nicht Behauptungen aufstellen will die sich dann als komplett Falsch raus stellen.

Was das Resultat ist hab ich schon raus gefunden, aber WARUM funktioniert das. Das kapiere ich nicht.

Das was ich aus der Doku lese ist, dass count() etwa zählt, was in den Klammern steht. wie weiß das ding, dass es die ring_count hochzählen soll. :shock:
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Homer-S: Das weiss das Ding gar nicht, das ist ihm auch völlig egal. Das liefert einfach Zahlen. Das die dann dem Namen `ring_count` zugewiesen werden, ist die Aufgabe der ``for``-Schleife. Wenn Du da ``for vorname in ['Peter', 'Paul', 'Mary']:`` schreibst, dann weiss die Liste mit den Namen ja auch nicht das die Namen an `vorname` gebunden werden. Das macht die ``for``-Schleife. Genau so weiss bei ``for i in count():` das Objekt das von `count()` erzeugt wird, nichts von dem `i`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Homer-S hat geschrieben: Mittwoch 15. Mai 2019, 09:36Das was ich aus der Doku lese ist, dass count() etwa zählt, was in den Klammern steht. wie weiß das ding, dass es die ring_count hochzählen soll. :shock:
Aus welcher Dokumentation hast du das denn gelesen? Kannst du die mal verlinken? Das ist nämlich auf keinen Fall das, was in der Dokumentation der Funktion steht.
Und wenn du in meinen vorherigen Beitrag schaust: Da habe ich dir doch geschrieben, wie man auf eine ganz einfache Weise an Hilfe für die Funktion kommt (falls der Entwickler sie sauber im Code hinterlegt hat, was in diesem Fall so ist.)
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

__blackjack__ hat geschrieben: Mittwoch 15. Mai 2019, 09:46 @Homer-S: Das weiss das Ding gar nicht, das ist ihm auch völlig egal. Das liefert einfach Zahlen. Das die dann dem Namen `ring_count` zugewiesen werden, ist die Aufgabe der ``for``-Schleife. Wenn Du da ``for vorname in ['Peter', 'Paul', 'Mary']:`` schreibst, dann weiss die Liste mit den Namen ja auch nicht das die Namen an `vorname` gebunden werden. Das macht die ``for``-Schleife. Genau so weiss bei ``for i in count():` das Objekt das von `count()` erzeugt wird, nichts von dem `i`.
Das hat geholfen!

das vorname ein .... ist die Art wie ich es kenne/verstehe. Ich konnte nicht erkennen, dass ein count() einfach bei 0 das zählen beginnt bis zum Sankt Nimmerleinstag ...
Besten Dank

Ich bin gerade in der Arbeit und kann schlecht testen. Nachdem, was ich gestern Abend mit psutil.net_connections() ausgegeben bekommen habe müsste eine einfach Abfrage den laufenden Prozess erfragen können:
Der Ausgabe Port der cam ist 9000

if '9000' in str(psutil.net_connections()):
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Homer-S: nein, die String-Repräsentation ist nicht dafür gedacht, darin wild nach Zahlen zu suchen.
Schau Dir doch mal genau an, was diese Funktion als Rückgabewert hat, wie Du einen einzelnen Eintrag darin nach dem Port fragen kannst und baue das dann in eine Schleife ein.
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Homer-S: Nein, man wandelt keine Listen in Zeichenketten um um darin etwas zu suchen. Das muss in Deinem Fall a) nicht funktionieren, denn es ist nirgends garantiert wie die `repr()`-Darstellung der einzelnen Elemente in dieser Liste aussehen, und b) weisst Du doch gar nicht das die Ziffernfolge '9000' in so einer Liste sich tatsächlich auf den Port 9000 bezieht. Der Test trifft zum einen auch auf eine Portnummer 19000 zu. Zum anderen sind in der Zeichenkettendarstellung auch Prozess-IDs – eine gefundene '9000' kann also auch eine PID sein. Und selbst wenn es der Port 9000 ist – lokal oder remote? Denn es werden dort ja auch Portnummern auf der anderen Seite der Verbindung aufgelistet. Wenn also irgend ein anderer Prozess eine Verbindung zu einem anderen Rechner auf Port 9000 hat, könnte Dein Test auch positiv ausfallen.

Erst einmal brauchst Du gar nicht *alle* Verbindungen auf Deinem Rechner auflisten, denn vom `Popen()`-Objekt kannst Du die Prozess-ID von dem `mjpeg_streamer`-Prozess abfragen. Und damit dann ein `psutil.Process`-Objekt erstellen um dann nur diesen einen Prozess nach seinen Verbindungen zu fragen. Da kannst Du beim Aufruf auch schon nach TCP-Verbindungen über IPv4 filtern. Und wenn da bei den Ergebnissen dann eine Verbindung dabei ist die als lokalen Port 9000 hat und den Status `psutil.CONN_LISTEN`, dann ist der Server bereit Verbindungen anzunehmen.

Mal ein bisschen live in einer IPython-Shell mit einem Bottle-Webserver herum gespielt:

Code: Alles auswählen

In [29]: bottle = subprocess.Popen(['bottle.py', 'test'])

In [30]: Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

In [30]: 

In [30]: bottle.pid
Out[30]: 14221

In [31]: p = psutil.Process(bottle.pid)

In [32]: p
Out[32]: psutil.Process(pid=14221, name='bottle.py', started='11:36:37')

In [33]: p.connections('tcp4')
Out[33]: [pconn(fd=3, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_S
TREAM: 1>, laddr=addr(ip='127.0.0.1', port=8080), raddr=(), status='LISTEN')]

In [34]: p.connections('tcp4')[0].laddr
Out[34]: addr(ip='127.0.0.1', port=8080)

In [35]: p.connections('tcp4')[0].laddr.port
Out[35]: 8080

In [36]: c = p.connections('tcp4')[0]

In [37]: c.laddr.port == 8080 and c.status == psutil.CONN_LISTEN
Out[37]: True
Eine Testfunktion ob ein Prozess mit einer gegebenen PID an einer gegebenen Adresse lauscht, könnte also so aussehen:

Code: Alles auswählen

def server_is_listening(pid, ip, port):
    return any(
        (
            connection.laddr.ip == ip
            and connection.laddr.port == port
            and connection.status == psutil.CONN_LISTEN
        )
        for connection in psutil.Process(pid).connections('tcp4')
    )
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

Damit komm ich gerade nicht weiter.

Code: Alles auswählen

for connection in psutil.Process(pid).connections('tcp4')
den Befehl gibt es so nicht. Wenn ich das Process(pid) weg lasse, geht es zwar weiter, aber das ist glaub ich nicht was wir wollen ...
die Doku sagt auch nur von den kinds wie tcp4. auf ein pid kann ich dort in der Klammer nicht eingrenzen ...
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Homer-S: Da muss die PID von dem Prozess rein, von dem man die Verbindungen haben will. Schau doch mal in dem Code-Block davor wo ich die für den Bottle-Prozess her habe.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Homer-S
User
Beiträge: 30
Registriert: Freitag 1. Dezember 2017, 22:30

__blackjack__ hat geschrieben: Freitag 17. Mai 2019, 07:13 @Homer-S: Da muss die PID von dem Prozess rein, von dem man die Verbindungen haben will. Schau doch mal in dem Code-Block davor wo ich die für den Bottle-Prozess her habe.
Hab ich drin mit pid = video.pid
wird auch korrekt ausgegeben. hatte ich vergessen mit anzugeben.

Es wirft mir trotzdem einen Fehler raus, Process kein connections kennt ...

Mein zweiter Ansatz war psutil.net_connections zu nutzen, da ich die pid ja habe, aber da kann ich (vielleicht nur ich :) nicht die Einschränkung/Filter pid setzen. Der kennt nur die kind's ....
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Homer-S: `pid` ist keine Einschränkung oder ein Filter sondern das Argument was man halt braucht um ein `psutil.Process`-Objekt zu erstellen. Und die `connections()`-Methode gibt es auf den Objekten auch mindestens seit 2013, davor hiess sie `get_connections()`. Wie alt ist denn Dein `psutil`‽
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten