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.
Homer-S
User
Beiträge: 24
Registriert: Freitag 1. Dezember 2017, 22:30

Mittwoch 15. Mai 2019, 09:36

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: 3845
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

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`.
“There's also a certain pleasure in actually getting things to work in Java, somewhat like the pleasure, I imagine, of building ships in bottles.”
— David Cook in c.l.p
Benutzeravatar
sparrow
User
Beiträge: 1280
Registriert: Freitag 17. April 2009, 10:28

Mittwoch 15. Mai 2019, 09:58

Homer-S hat geschrieben:
Mittwoch 15. Mai 2019, 09:36
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:
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: 24
Registriert: Freitag 1. Dezember 2017, 22:30

Mittwoch 15. Mai 2019, 10:05

__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: 10211
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 15. Mai 2019, 10:23

@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: 3845
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 15. Mai 2019, 10:55

@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')
    )
“There's also a certain pleasure in actually getting things to work in Java, somewhat like the pleasure, I imagine, of building ships in bottles.”
— David Cook in c.l.p
Homer-S
User
Beiträge: 24
Registriert: Freitag 1. Dezember 2017, 22:30

Freitag 17. Mai 2019, 06:25

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: 3845
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

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.
“There's also a certain pleasure in actually getting things to work in Java, somewhat like the pleasure, I imagine, of building ships in bottles.”
— David Cook in c.l.p
Homer-S
User
Beiträge: 24
Registriert: Freitag 1. Dezember 2017, 22:30

Freitag 17. Mai 2019, 08:41

__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: 3845
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Freitag 17. Mai 2019, 12:03

@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`‽
“There's also a certain pleasure in actually getting things to work in Java, somewhat like the pleasure, I imagine, of building ships in bottles.”
— David Cook in c.l.p
Homer-S
User
Beiträge: 24
Registriert: Freitag 1. Dezember 2017, 22:30

Freitag 17. Mai 2019, 17:19

__blackjack__ hat geschrieben:
Freitag 17. Mai 2019, 12:03
@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`‽
oh man, sorry. Ich hatte zwei Syntax gemischt. das wirft nun keinen Fehler mehr raus
das return any müsste True/False Wert liefern, oder?

könnte ich also mit einer
if server_is_listening(pid, '192.168.100.111', '9000'):
die Streamaufnahme starten?

was mache ich im else Fall?
Benutzeravatar
__blackjack__
User
Beiträge: 3845
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Freitag 17. Mai 2019, 18:23

@Homer-S: Portnummern sind Zahlen und keine Zeichenketten. Du musst das so lange machen bis der Server da ist, beziehungsweise bis Du sicher bist, dass Du aufgeben willst. Kann ja sein, dass sich der Server gar nicht starten lässt, dann sollte man natürlich nicht bis in alle Ewigkeit warten.
“There's also a certain pleasure in actually getting things to work in Java, somewhat like the pleasure, I imagine, of building ships in bottles.”
— David Cook in c.l.p
Homer-S
User
Beiträge: 24
Registriert: Freitag 1. Dezember 2017, 22:30

Sonntag 14. Juli 2019, 19:56

Hallo zusammen,
ich habe mich lange nicht mehr hier gemeldet, weil es auch gut lief.
Durch den Wechsel von "ich schalte den Stream erst an" zu ich Streame immer und überwache das mit dem Watchdog, konnte ich das Programm etwas vereinfachen und es hat wirklich gut funktioniert. Leider hat mein alter RPi, den ich benutzte dafür, den Geist aufgegeben.
Hab jetzt einen RPi 3+ und leider funktioniert das Programm nicht mehr.

Ich bekomme einen Fehler

Code: Alles auswählen

Aufnahme start
/home/pi/cam/20190714_204746.mkv
['ffmpeg', '-i', 'http://192.168.100.130:9000/?action=stream', '-f', 'image2', '-r', '1/5', '-frame_pts', '1', '/home/pi/cam/%d.jpg', '-vcodec', 'copy', '-an', '-bufsize', '2048k', '-t', '00:00:30', '-y', '/home/pi/cam/20190714_204746.mkv']
Traceback (most recent call last):
  File "ring.py", line 90, in <module>
    main()
  File "ring.py", line 85, in main
    record(ring_count)
  File "ring.py", line 66, in record
    record = subprocess.Popen(VIDEO_RECORD_COMMAND + [videoname] )
  File "/usr/lib/python2.7/subprocess.py", line 390, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1024, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
Das Verzeichnis cam existiert mit 775 Rechten ...
Danke schon mal für die Hilfe, warum die Fehlermeldung plötzlich beim neuen RPi kommt ..

Hier noch der Programm code:

Code: Alles auswählen

#!/usr/bin/env python
#coding: utf8

import time
import os
import subprocess
import datetime
from itertools import count
import requests
import RPi.GPIO as gpio
import glob
#import psutil

DEVELOPERMODE = True

# PIN festlegen an dem das Signal kommt 
OPTOKOPPLER = 18
RELAIS = 11
gpio.setwarnings(False)


#Telegram konfiguration
TELEGRAM_URL ="https://api.telegram.org/bot"
BOT_TOKEN = "7ichbindertokencode"
B_ID = "123456789"
A_ID = "123456788"
CHAT_IDS = [A_ID, B_ID]

BASE_PATH = '/home/pi/cam/'

VIDEO_RECORD_COMMAND = ['ffmpeg',
    '-i', "http://192.168.100.130:9000/?action=stream",
    '-f', 'image2', '-r', '1/5', '-frame_pts', '1', '/home/pi/cam/%d.jpg',
    '-vcodec', 'copy', '-an', '-bufsize', '2048k', '-t', '00:00:30', '-y']

def send_message(chatroomids, message=None, photo=None):
    files = {}
    for id in chatroomids:
        params = {"chat_id": id}
        if message:
            params["text"] = message
            cmd = "sendMessage"
        if photo:
            files = {"photo": photo}
            cmd = "sendPhoto"
        url = "{}{}/{}".format(TELEGRAM_URL, BOT_TOKEN, cmd)
        r = requests.get(url, params=params, files=files)
        result = r.json()
        print(result)

def setup():
    # Pin optokopller als Eingang, relais als Ausgang festlegen
    gpio.setwarnings(False)
    gpio.setmode(gpio.BOARD)
    gpio.setup(OPTOKOPPLER, gpio.IN, pull_up_down = gpio.PUD_UP)
    gpio.setup(RELAIS, gpio.OUT)
    # Klingelrelais generell anschalten
    gpio.output(RELAIS, gpio.HIGH) 


def record(ring_count):
    videoname = os.path.join(BASE_PATH, "{:%Y%m%d_%H%M%S}.mkv".format(datetime.datetime.now()))  
    print("Aufnahme start")
    print(videoname)
    print(VIDEO_RECORD_COMMAND + [videoname])
    record = subprocess.Popen(VIDEO_RECORD_COMMAND + [videoname] )
    time.sleep(31)
    record.terminate()
    record.wait()
    print("Aufzeichnung beendet")
    message = "Es hat gerade das {}. mal geklingelt.".format(ring_count)
    send_message(CHAT_IDS, message=message)
    for filename in sorted(glob.glob(os.path.join(BASE_PATH, '*.jpg'))):
        with open(filename, 'rb') as data:
            photo = data.read()
        send_message(CHAT_IDS, photo=photo)
        time.sleep(1)
        os.unlink(filename)

def main():
    try:
        setup()
        for ring_count in count(start=1):
            if DEVELOPERMODE or gpio.wait_for_edge((OPTOKOPPLER), gpio.FALLING):
                record(ring_count)
    finally:
        gpio.cleanup()

if __name__ == '__main__':
    main()
Benutzeravatar
sparrow
User
Beiträge: 1280
Registriert: Freitag 17. April 2009, 10:28

Sonntag 14. Juli 2019, 20:14

Ich würde sagen, der Befehl ffmpeg wird nicht gefunden.
Antworten