Problem Threading mit Tornado

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Tobi_L
User
Beiträge: 2
Registriert: Mittwoch 27. November 2024, 12:43

Hallo zusammen,

um es vorweg zunehmen ich bin in Sachen Phyton und Raspberry blutiger anfänger. (Bis jetzt hab ich nur Codesys im Strukturiertem Text programmiert).

Ich möchte über einen Webbrowser einen Schrittmotor steuern und hab mir dazu Tornado usw. auf dem Raspi installiert. (Das Programm hab ich im Internet gefunden und dieses ist auch schon gelaufen, jedoch mit Phyton 2.) Ich hab jetzt Pyton 3 und hab die Syntaxfehler die ich soweit gefunden habe ausgebessert. Jedoch bekomme ich eine Fehlermeldung das anscheinend noch ein Event loop fehlt. Ich komme hier absolut nicht weiter.

Vielleicht kann mir jemand von euch helfen. Falls ihr noch die Seite braucht auf der das Grundprogramm war kann ich das hier gerne verlinken, Jedoch weiß ich nicht ob das gewünscht ist. :-) Sorry.

Anbei wäre das Programm und die Fehlermeldung. Das HTML Script hab ich jetzt nicht runter geladen, da das mit der Fehlermeldung denke ich nichts zu tun hat da der Fehler sofort beim Codeausführen kommt.

Hier ist der Code. (Die Fehlermeldung dazu ist unten in Blau)

Code: Alles auswählen

# Schrittmotorsteuerung mit Kamerabildanzeige im Browser über Tornado Websocket
#------------------------------------------------------------------------------
 
# Bibliotheken
#--------------------
# Einbindung der notwendigen Grundbibliotheken
import os, sys, time, threading
 
# Einbindung GPIO's
import RPi.GPIO as gp
 
# Einbindung der notwendigen Bibliotheken für den Tornado Websocket
import tornado.httpserver, tornado.websocket, tornado.ioloop, tornado.web, datetime, json

 
# Einbindung der Kamera
import picamera
 
# Deklarationen
#--------------------
# Global für Programmstatus
programmStatus = 1 
# Datenbank mit der gespeicherten Stellung der Schrittmotorstellung
sqlitemotorStellungDBName = "/var/www/html/sqlite/motorstellung.db" 
 
 
# Global für Schrittmotor
schrittMotorStellung = 0 # Stellung des Schrittmotors
aktuelleScheibe = 1 # Aktuelle Scheibe auf Streifen (1-10)
 
# Global für Videostream
dateiNameStream = '' # zur Übergabe des aktuellen Dateinamen an die Weboberfläche
Kamera_Stream_aktiv = False # als Erweiterung der folgenden Streaming-Option per Socket
kameraStreamStatus = False # notwendig für die Kamerasteuerung
bilderStreamPfad = "/var/www/html/streamtmp/" # Ram-Disk
 
# Websocket
#----------
class TornadoThread (threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        application = tornado.web.Application([(r'/ws', WSHandler),])
        http_server = tornado.httpserver.HTTPServer(application)
        http_server.listen(8888)
 
    def run(self):
        print ("Tornado Websocketdienst gestartet.\nWarte auf Verbindung.\n...\n")
        tornado.ioloop.IOLoop.instance().start()
        
 
class WSHandler(tornado.websocket.WebSocketHandler):
    def check_origin(self, origin):
        return True
    
    def open(self):
        global alterDatenUebergabeWert
        alterDatenUebergabeWert = ''
        self.connected = True
        print ("Verbindung mit WEB-Oberfläche aufgebaut.\n")
        self.timeout_loop()
 
    def on_message(self, message):
        global Kamera_Stream_aktiv
 
        print ("Übergabewert von WEB-Interface: ", message)
 
        if message == "page_2":
            print ("User befindet sich auf motor.html - setze Kamera_Stream_aktiv auf True")
            Kamera_Stream_aktiv = True
            message = ""
        elif message == "cam-left":
            print ("Schrittmotor in linke Richtung drehen lassen")
            schrittmotor("positiv")
        elif message == "cam-right":
            print ("Schrittmotor in rechte Richtung drehen lassen")
            schrittmotor("negativ")
        else:
            print ("unbekannter Befehl")
                    
    def on_close(self):
        self.connected = False
        print ("Verbindung zum WEB-Interface geschlossen.")
        self.timeout_loop()
 
    def timeout_loop(self):
        global Kamera_Stream_aktiv, dateiNameStream
 
        if self.connected:
            if Kamera_Stream_aktiv == True:
                self.write_message(dateiNameStream)
            tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=.1), self.timeout_loop)
 
# Funktionen
#-----------------------------
 
def gpioInitalisieren():
    # GPIO zur Verwendung vorbereiten
    try:
        gp.setmode(gp.BCM) # BCM Nummern verwenden
        gp.setwarnings(False) # Warnmeldungen abschalten
        # GPIO Ports für das Pollin L298 Schrittmotorboard initalisieren
        gp.setup(17, gp.OUT) # Pin11 - BCM Nummer 17 - GPIO17 wird Ausgang = IN1
        gp.output(17, gp.LOW)
        gp.setup(18, gp.OUT) # Pin12 - BCM Nummer 18 - GPIO18 wird Ausgang = IN2
        gp.output(18, gp.LOW)
        gp.setup(22, gp.OUT) # Pin15 - BCM Nummer 22 - GPIO22 wird Ausgang = IN3
        gp.output(22, gp.LOW)
        gp.setup(27, gp.OUT) # Pin13 - BCM Nummer 27 - GPIO22 wird Ausgang = IN4
        gp.output(27, gp.LOW)
    except:
        # Fehler bei der GPIO-Initialisierung
        programmStatus =  102
 
def schrittmotor(richtung):
    global schrittMotorStellung
    global aktuelleScheibe
    # damit die aktuelle Position nach einem Neustart bekannt ist, wird die Position abgespeichert
    # damit es evtl. keine Probleme mit den Kabelverbindungen gibt, wird die Bewegung eingschränkt
    # bewegungsStop = 130
    print ("aktuelle Scheibe = ", aktuelleScheibe)

    if aktuelleScheibe <= 9:
        vorschub = 20
    else:
        vorschub = 80

    if richtung == "negativ":
        print ("Negativ")
        i = 1
        zukuenftigeSchrittMotorStellung = schrittMotorStellung - 16
        if zukuenftigeSchrittMotorStellung > -bewegungsStop:
            while i < 16 :
                schrittmotorStepNegativ()
                i = i + 1
            schrittMotorStellung = schrittMotorStellung - i
        else:
            print ("keine Bewegung da außerhalb gesetzter Position")
    elif richtung == "positiv":
        print ("Positiv")
        i = 1
        zukuenftigeSchrittMotorStellung = schrittMotorStellung + vorschub
        if zukuenftigeSchrittMotorStellung < bewegungsStop:
            while i < vorschub :
                schrittmotorStepPositiv()
                i = i + 1
            schrittMotorStellung = schrittMotorStellung + i
            if aktuelleScheibe < 10:
                aktuelleScheibe = aktuelleScheibe + 1
            elif aktuelleScheibe == 10:
                aktuelleScheibe = 1
                schrittMotorStellung = 0
        else:
            print ("keine Bewegung da außerhalb gesetzter Position")
    sql = 'UPDATE schrittmotorstellung SET position = "' + str(schrittMotorStellung) + '"'
    motorStellung_db_update(sql)
 
def schrittmotorStepPositiv():
    zeit = 0.01
    #Step 1
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    #Step 2
    gp.output(27, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 3
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(22, gp.LOW)
    #Step 4
    gp.output(18, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 5
    gp.output(18, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    #Step 6
    gp.output(17, gp.HIGH)
    gp.output(18, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    gp.output(18, gp.LOW)
    #Step 7
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    #Step 8
    gp.output(27, gp.HIGH)
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    gp.output(17, gp.LOW)
 
def schrittmotorStepNegativ():
    zeit = 0.01
    #Step 8
    gp.output(27, gp.HIGH)
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    gp.output(17, gp.LOW)
    #Step 7
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    #Step 6
    gp.output(17, gp.HIGH)
    gp.output(18, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    gp.output(18, gp.LOW)
    #Step 5
    gp.output(18, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    #Step 4
    gp.output(18, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 3
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(22, gp.LOW)
    #Step 2
    gp.output(27, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 1
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)

 
#GPIO initialisieren
gpioInitalisieren()
 
# Festlegung Tornado Websocket
TornadoThread = TornadoThread()
TornadoThread.start()
print ("Programminformation: es sind Insgesamt ", threading.active_count(), " Threads aktiv.")
print ("Aktueller Thread:", threading.current_thread())
 
# Hauptroutine
i = 0
while programmStatus == 1:
    if Kamera_Stream_aktiv == True:
        if kameraStreamStatus == False:
            # Kamera initialisieren
            cam = picamera.PiCamera()
            cam.resolution = (640, 480) # Auflösung für Bildspeicherung
            print ("Bildaufnahme für Stream beginn")
            kameraStreamStatus = True
        cam.start_preview()
        dateiName = bilderStreamPfad + str(i) + ".jpg"
        time.sleep(0.05)
        cam.capture(dateiName)
        dateiNameStream = "stream|streamtmp/" + str(i) + ".jpg"
        cam.stop_preview()
        i = i + 1
        if i == 100:
            i = 0
    else:
        if kameraStreamStatus == True:
            cam.close() # damit die Kamera nicht ständig in Betrieb ist
            kameraStreamStatus = False
            i = 0   
               
# Programmende durch Veränderung des programmStatus
# GPIO Einstellungen löschen
gp.cleanup()
# Ausgabe der Nachricht für das Programmende
print ("Programm wurde wegen eines Fehlers beendet. Fehlercode: ", programmStatus)
if programmStatus == 102:
    print ("Die Initialisierung des GPIO ist fehlgeschlagen.")
else:
    print ("Der Fehlercode wurde nicht hinterlegt.")
    


Tornado Websocketdienst gestartet.
Warte auf Verbindung.
...

Exception in thread Thread-1:
Programminformation: es sind Insgesamt 2 Threads aktiv.
Traceback (most recent call last):
Aktueller Thread: <_MainThread(MainThread, started 1996167552)>
File "/usr/lib/python3.9/threading.py", line 954, in _bootstrap_inner
self.run()
File "/home/tobi/Desktop/MotorProgramm.org", line 52, in run
tornado.ioloop.IOLoop.instance().start()
File "/usr/lib/python3/dist-packages/tornado/ioloop.py", line 199, in instance
return IOLoop.current()
File "/usr/lib/python3/dist-packages/tornado/ioloop.py", line 263, in current
loop = asyncio.get_event_loop()
File "/usr/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-1'.




Vielen Dank schon mal und schöne Woche noch.
Benutzeravatar
__blackjack__
User
Beiträge: 13684
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Tobi_L: Ich denke das macht als Anfänger wenig Sinn ein altes Programm von Python 2 auf Python 3 zu portieren, vor allem wenn sich die verwendete Bibliothek (Tornado) ja auch weiterentwickelt hat, und mitllerweile intern auch etwas anders funktioniert weil es `asyncio` von Python 3 verwendet.

Der Quelltext macht auch keinen wirklich guten Eindruck. ``global`` ohne Threads ist schon unschön, aber mit Threads wird das alles ja noch unübersichtlicher. Und wenn Klassen verwendet werden und dann in *Methoden* ``global`` steht, dann hat der Autor von dem Code ganz offensichtlich objektorientierte Programmierung nicht verstanden.
„Incorrect documentation is often worse than no documentation.“ — Bertrand Meyer
Tobi_L
User
Beiträge: 2
Registriert: Mittwoch 27. November 2024, 12:43

Vielen Dank für die Antwort, ich kenn mich wie gesagt leider hier noch überhaupt nicht aus. Ist threading dann hier überhaupt die richtige Wahl oder geht das mittlerweile einfacher?

Ich hab das Programm mal auf das für mich nötigste umkopiert und es läuft auch solala. Vielleicht können Sie mir sagen wie ich es am besten machen kann das der Prozess mit dem Websocket und die Schrittmotorsteuerung parallel laufen.

Anbei ist der Code der momentan läuft. Für anregungen bin ich echt dankbar, weil ich bin wie gesagt noch relativ frisch in der Materie und kenn mich mit Threading und dem allgemeinen Programmaufruf noch nicht wirklich aus.

Vielen Dank schon mal und schöne Woche noch.

Code: Alles auswählen

# Bibliotheken
#--------------------
# Einbindung der notwendigen Grundbibliotheken
import os, sys, time 

# Einbindung der notwendigen Bibliotheken für den Tornado Websocket
import tornado.httpserver, tornado.websocket, tornado.ioloop, tornado.web, datetime, json

# Einbindung GPIO's
import RPi.GPIO as gp
 
from gpiozero import Button
from picamera import PiCamera
from datetime import datetime
from signal import pause

button = Button(26)
camera = PiCamera()
datenpfad =''

# Global für Schrittmotor
schrittMotorStellung = 0 # Stellung des Schrittmotors
aktuelleScheibe = 1 # Aktuelle Scheibe auf Streifen (1-10)
Merker = False

 
class WebSocketHandler(tornado.websocket.WebSocketHandler):

    def check_origin(self, origin):
        return True

    def open(self):
        print("WebSocket geöffnet")
 
    def on_message(self, message):
        if message == "Vorwärts":
            print ("Vorwärts")
            capture()
            datenpfad = (f'/streamtmp/{datetime.now():%Y-%m-%d-%H-%M-%S}.jpg')
            self.write_message(datenpfad)
            Merker = True
            schrittmotor("positiv")
        elif message == "Rückwärts":
             print ("Rückwärts")
             schrittmotor("negativ")
        else:
            self.write_message("Du hast gesagt: " + message)
            print ("Eingabe des Users: " + message)
 
    def on_close(self):
        print("WebSocket geschlossen")
 
app = tornado.web.Application([
    (r"/ws", WebSocketHandler),
])

def capture():
	#camera.start_preview()
	# Camera warm-up time
	#time.sleep(2)
    
	camera.capture(f'/var/www/html/streamtmp/{datetime.now():%Y-%m-%d-%H-%M-%S}.jpg')
    #camera.close() # damit die Kamera nicht ständig in Betrieb ist
	print('Aufnahme gespeichert')
    

 


# Funktionen
#-----------------------------
 
def gpioInitialisieren():
    # GPIO zur Verwendung vorbereiten
    try:
        gp.setmode(gp.BCM) # BCM Nummern verwenden
        gp.setwarnings(False) # Warnmeldungen abschalten
        # GPIO Ports für das Pollin L298 Schrittmotorboard initalisieren
        gp.setup(17, gp.OUT) # Pin11 - BCM Nummer 17 - GPIO17 wird Ausgang = IN1
        gp.output(17, gp.LOW)
        gp.setup(18, gp.OUT) # Pin12 - BCM Nummer 18 - GPIO18 wird Ausgang = IN2
        gp.output(18, gp.LOW)
        gp.setup(22, gp.OUT) # Pin15 - BCM Nummer 22 - GPIO22 wird Ausgang = IN3
        gp.output(22, gp.LOW)
        gp.setup(27, gp.OUT) # Pin13 - BCM Nummer 27 - GPIO22 wird Ausgang = IN4
        gp.output(27, gp.LOW)
    except:
        # Fehler bei der GPIO-Initialisierung
        programmStatus =  102
        
            
 
def schrittmotor(richtung):
    global schrittMotorStellung
    global aktuelleScheibe
    # damit die aktuelle Position nach einem Neustart bekannt ist, wird die Position abgespeichert
    # damit es evtl. keine Probleme mit den Kabelverbindungen gibt, wird die Bewegung eingschränkt
    bewegungsStop = 130
    print ("aktuelle Scheibe = ", aktuelleScheibe)

    if aktuelleScheibe <= 9:
        vorschub = 20
    else:
        vorschub = 80

    if richtung == "negativ":
        print ("Negativ")
        i = 1
        zukuenftigeSchrittMotorStellung = schrittMotorStellung - 16
        if zukuenftigeSchrittMotorStellung > -bewegungsStop:
            while i < 16 :
                schrittmotorStepNegativ()
                i = i + 1
            schrittMotorStellung = schrittMotorStellung - i
        else:
            print ("keine Bewegung da außerhalb gesetzter Position")
    elif richtung == "positiv":
        print ("Positiv")
        i = 1
        zukuenftigeSchrittMotorStellung = schrittMotorStellung + vorschub
        if zukuenftigeSchrittMotorStellung < bewegungsStop:
            while i < vorschub :
                schrittmotorStepPositiv()
                i = i + 1
            schrittMotorStellung = schrittMotorStellung + i
            if aktuelleScheibe < 10:
                aktuelleScheibe = aktuelleScheibe + 1
            elif aktuelleScheibe == 10:
                aktuelleScheibe = 1
                schrittMotorStellung = 0
            
                
def schrittmotorStepPositiv():
    zeit = 0.01
    #Step 1
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    #Step 2
    gp.output(17, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 3
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(22, gp.LOW)
    #Step 4
    gp.output(18, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 5
    gp.output(18, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    #Step 6
    gp.output(18, gp.HIGH)
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    gp.output(27, gp.LOW)
    #Step 7
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    #Step 8
    gp.output(17, gp.HIGH)
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    gp.output(27, gp.LOW)

    
    print("I foh Fire")
    
def schrittmotorStepNegativ():
    zeit = 0.03
    #Step 8
    gp.output(27, gp.HIGH)
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    gp.output(17, gp.LOW)
    #Step 7
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(27, gp.LOW)
    #Step 6
    gp.output(18, gp.HIGH)
    gp.output(27, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    gp.output(27, gp.LOW)
    #Step 5
    gp.output(18, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    #Step 4
    gp.output(18, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(18, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 3
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(22, gp.LOW)
    #Step 2
    gp.output(17, gp.HIGH)
    gp.output(22, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)
    gp.output(22, gp.LOW)
    #Step 1
    gp.output(17, gp.HIGH)
    time.sleep(zeit)
    gp.output(17, gp.LOW)

#GPIO initialisieren
gpioInitialisieren()
    
if __name__ == "__main__":
    
        
     
    button.when_pressed = capture
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

    
Antworten