Problem: multiprocessing websocket

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Moin

    Ich arbeite zZt an einem Projekt für einen Forumuser, um eine art "Ausleih"-Server zu realisieren. Dabei wird ein Barcode-Scanner verwendet um Strichcodes von Gegenständen einzulesen sowie eine Web-Oberfläche die der Ausleiher bedienen kann... Soviel zur groben Beschreibung ;)

    Ich nutze multiprocessing.Queue's um sowohl Eingaben als auch die Barcodes zu handhaben, als auch zur Logausgabe..

    Habe 3 verschiedene Scripts erstellt:

    • Main.py: Haupt-Process von dem aus die anderen beiden Initialisiert werden und rückführend die Daten verarbeitet werden.

    Spoiler anzeigen
    • PHP
      #!/usr/bin/python3# coding: utf-8## Werkzeug Verleih fuer JumpY# 12.01.2016  Copyright (C) by meigrafd (meiraspi@gmail.com) published under the MIT License#import multiprocessing as mpimport timefrom Barcode import *from WebSocket import *#-------------------------------------------------------------------HID_device = '/dev/hidraw0'WebSocketHost = "0.0.0.0"WebSocketPort = 7070#-------------------------------------------------------------------# Main processif __name__ == '__main__':    # Create instance of queues and processes    logQueue = mp.Queue()    BarcodeQueue = mp.Queue()    WebSocketQueue = mp.Queue()        BarcodeProcess = ProcessBarcode(BarcodeQueue, logQueue)    WebSocketProcess = ProcessWebSocket(WebSocketQueue, logQueue)        processList = [mp.Process(target=BarcodeProcess.Run, args=(HID_device,)),                   mp.Process(target=WebSocketProcess.Run, args=(WebSocketPort,WebSocketHost))]# starting child processes    for i in range(0, len(processList)):        processList[i].start()# Endless loop of main program    try:        while True:            if not logQueue.empty():                print(logQueue.get())if not WebSocketQueue.empty():                request = WebSocketQueue.get()                request = request.strip()                returnlist = ""                requestsplit = request.split(':')                requestsplit.append("dummy")                print("processing command: {}".format(command))                command = requestsplit[0]                value = requestsplit[1]                if value == "dummy":                    value = "0"                if command == "localping":                    returnlist += "\n localping:ok"elif command == "get_user":                    ID = BarcodeQueue.get()                    returnlist += "\n get_user:" + str(ID)if returnlist:                    WebSocketProcess.send_msg(returnlist)                        time.sleep(0.1)    except (KeyboardInterrupt, SystemExit):        print("Schliesse Programm..")[/php][/spoiler][/list][list]
      [/spoiler]
    Spoiler anzeigen
    • PHP
      [b]Barcode.py[/b]: Wartet permanent darauf das der Ausleiher ein Strichcode einliegt, und fügt diesen dann in ein Queue ein.[spoiler][code=php]# coding: utf-8import time, sysclass ProcessBarcode:    def __str__(self):        self.nachricht = "Process Barcode"        return self.nachrichtdef __init__(self, BarcodeQueue, logQueue):        self.BQueue = BarcodeQueue        self.lQueue = logQueuedef Run(self, Device):        import random        while True:            time.sleep(10)            bla=0            for x in range(0,10):                bla += (random.randint(1,200))            self.BQueue.put(bla)            self.lQueue.put("Barcode: {}".format(bla))            time.sleep(10)return[/php][/spoiler][/list][list]
      [/spoiler]
    Spoiler anzeigen
    • [/spoiler]
    • Spoiler anzeigen
      PHP
      websocket_server.py: Ist nicht von mir. Beinhaltet eine WebSocket_Server Klasse. Quelle: https://github.com/Pithikos/python-websocket-server[/list]Zum debuggen gibt Barcode.py nur zufällige Zahlen aus. Main.py soll eigentlich auch noch Verbindung zu einer MySQL-Datenbank aufnehmen, aber das nutze ich auch erst mal nicht da nicht weiter wichtig fürs debuggen...So, nun aber zum Fehler:Sobald das Web-Interface eine Anfrage "get_user" stellt und Main.py eine Antwort zurück schicken soll, erhalte ich eine Fehlermeldung:[code]pi@raspberrypi:~/Verleih$ ./Main.py    Listening on port 7070 for clients..Barcode: 1118WebSocket: New client connected and was given id 1processing command: get_userTraceback (most recent call last):  File "./Main.py", line 62, in <module>    WebSocketProcess.send_msg(returnlist)  File "/home/pi/Verleih/WebSocket.py", line 34, in send_msg    self.server.send_message(message)AttributeError: 'ProcessWebSocket' object has no attribute 'server'

      ...an diesem Problem kracksel ich jetzt schon seit 3 Stunden rum aber kriegs nicht hin =(

      Weiß jemand Rat?

      :danke_ATDE:

  • Mein lieber Herr Gesangsverein. Dazu faellt mir eine *Menge* ein. Habe ich aber nicht genug Zeit jetzt.

    Das Hauptproblem ist deine komplette Selbstverwirrung mit multiprocessing. Du mischst OO mit dem starten von Prozessen. Das klappt zwar, aber streng genommen sollte das target-Argument eines Prozesses aber nur ein "simples" callable, also eine Funktion, sein. Siehe jetzt:

    Der Grund ist offensichtlich: das WebSocketProcess-Objekt (das nicht den Python-Namenskonventionen folgt, und damit aeusserst verwirrend ist - es sieht aus wie eine Klasse, ist aber eine Instanz!) wird im Main.py-Prozess erzeugt, die Run-Methode (auch wieder Namenskonvention...) als target definiert, und ein Prozess gestartet.

    Daraufhin forked multiproecessing deinen Prozess - und fortan leben *zwei* WebSocketProcess-Objekte, jeweils in ihrem Prozess. Du versuchst die jetzt aber so zu behandeln, als ob es *eines* waere - im Kindprozess erzeugst du eine client-connection & den dazugehoerigen Server (dass self.server eine einzige Instanz fuer potentiell beliebig viele clients ist, faellt dir vielleicht auch irgendwann mal auf die Fuesse) - und updates dessen WebSocketProcess.

    Aber im Main.py ist davon nichts zu sehen, logischerweise. Ist ein anderer Prozess, ein anderes Objekt. Trotzdem willst du mit send_msg darauf zugreifen. Das klappt natuerlich nicht.

    Stattdessen musst du ein Kommando in eine Queue stopfen, auf die wiederum der websocket-Prozess lauscht, und dann was auch immer send_msg verschicken will, verschickt.

    Ich muss los, eine Schlussbemerkung: schmeiss diese logging-queue raus. Ein logging-System soll dir Hilfe zum debuggen sein, wenn es aber davon lebt, dass der Main.py genau das macht, was jetzt gerade zum logging notwendig ist, schlaegt das fehl - denn da koente ja ein Bug sein. Entweder setzt du ein logging pro Prozess auf, oder benutzt die multiprocessing-eigen logger-Infrastruktur, das ist auch dokumentiert.

  • IMHO ist ``multiprocessing`` hier sowieso falsch, da der RPi eh nur ein Kern hat. Auch wenn der RPi2 verwendet würde, müssten vier Barcodescanner zur exakt gleichen Zeit Code einlesen und verarbeiten so dass ``multiprocessing`` für mich einen Sinn ergäbe.

    Nach der Projektbeschreibung wäre es ggf. viel einfacher, ein Mikro-Rahmenwerk wie bottle zu verwenden und dann auch bottle um die Verarbeitung der Daten kümmern lassen.

  • Ich habe mich dabei an dem Code von Wolfgang Glück orientiert und hantiere zum ersten mal mit multiprocessing.

    Habe es zuvor bereits auf 3 anderen Wegen versucht die ebenfalls nicht wie gewünscht funktioniert haben, mit multiprocessing funktioniert es aber weitaus besser als es zuvor der Fall war - würde also eigentlich schon gerne bei diesem Weg nun bleiben.

    Leider versteh ich bei deiner Antwort @deets nur Bahnhof. Könntest du das bitte noch mal mit etwas weniger Fachchinesisch beschreiben? :huh:

  • ich habe gestern mit Python angefangen, also bitte nicht hauen für blöde Antworten :auslachen:

    Mir fällt halt nur auf, dass in ProcessWebSocket.send_msg auf self.server zugegriffen wird. Deshalb würde ich erstmal prüfen, ob das wirklich derselbe server (vom Typ "WebsocketServer") ist, der bei new_client gespeichert wurde (id?)

    Wirst du aber längst schon geprüft haben..

    Ich durchschaue auch diesen new_client Mechanismus nicht. Muss man da mit set_fn_new_client selbst ein Callback installieren? Aber laut deiner Ausgabe wird das ja offenbar aufgerufen..
    (Edith: ich hab's entdeckt in ProcessWebSocket.Run ;)
    Außerdem wird das von WebsocketServer so aufgerufen: self.new_client(client, self) - also als Parameter ein WebsocketServer. In deinem Code steht
    def new_client(self, client, server):
    => :s
    (Edith: okok, ich hab's gelernt - dass Methoden defs immer self als ersten Pseudo-Parameter deklarieren müssen)
    Ich werde mich wohl auch noch ein bissel mit dieser angeblich so klar strukturierten und leicht lesbaren Sprache beschäftigen müssen..

    Auf jeden Fall würde ich wegen "AttributeError: 'ProcessWebSocket' object has no attribute 'server'" erstmal self.server untersuchen.
    In ProcessWebSocket.Run wird das ja ohne self referenziert - ob das eine Rolle spielt?

    Einmal editiert, zuletzt von Zoppo Trump (12. Januar 2016 um 14:26)

  • meigrafd

    Das du deinen Code von Wolfgang hast inspirieren lassen hab' ich mir schon gedacht. Da sind so ein paar Idiosynchratien drin, die mir bekannt vorkommen.

    Weniger fachchinesisch... wie das Modul multiprocessing schon im Namen sagt, verteilt es dein Programm auf mehrerer Prozesse. Jeder dieser Prozesse beinhaltet seinen eigenen Zustand, der auch nicht beeinflussbar ist von einem anderen Prozess (1).

    Der Weg, wie das unter UNIX funktioniert ist, dass man dort den Systemaufruf "fork" benutzt - der erzeugt eine exakte Kopie eines Prozesses, und dieser ist ein Kind des Elternprozesses. Du rufst das zwar direkt nicht auf (gibt es auch unter os.fork), aber es ist der dem multiprocessing unterliegende Mechanismus.

    Das heisst ab diesem Zeitpunkt divergiert jedwede Zustandsaenderung zwischen den beiden Prozessen.

    Nun waere es ja relativ wertlos, wenn man zwei Prozesse haette, welche sich ueberhaupt nicht mehr beeinflussen koennen. Deswegen gibt es da die Moeglichkeit, bestimmte Kommunikationsprimitive zu benutzen. Auch das fuehrt in voller Breite zu weit, die einfachsten und passensten sind ueblicherweise Queues, die du ja auch verwendest. Mit diesen koennen Elternteil und Kind Nachrichten aneinander verschicken.

    Was heisst das nun fuer dich? Du hast ein Objekt "ProcessWebSocket", von dem du eine gebundene Methode - Run - als Einstiegspunkt in einen neuen Prozess ausgewaehlt hast. Das entscheidende ist aber nun, dass *ab diesem Zeitpunkt* auf deinem Computer *zwei* ProcessWebSocket-Instanzen leben, in zwei getrennten Prozessen. Nennen wir sie WS-Main und WS-Socket. WS-Socket lebt im Kind, WS-Main im Elternprozess. Sie reden ueber die WebSocketQueue. In WS-Socket kommt nun eine Browserverbindung an, und legt - uA - ein Instanzattribut server an. Also self.server = <irgendwas>.

    Diese Zustandsveraenderung ist aber *nur* in WS-Socket passiert! *NICHT* in WS-Main! In deinem Elternprozess versuchst du aber nun, WS-Main.send_msg aufzurufen - in der irrigen Annahme, es haette die Serververbindung. Und das fliegt dir um die Ohren.

    Was du stattdessen machen musst, ist vom Elternprozess via WebSocketQueue eine Nachricht an den Kindprozess zu schicken, welche dort dann empfangen und ausgewertet wird - zB wird dann eben WS-Socket.send_msg aufgerufen.

    Diese Architektur hat natuerlich auch wieder dramatische Konsequenzen fuer die Argumente, welche du als Nachrichten verschicken kannst - sie muessen eben serialisierbar, sprich "picklebar" sein, weil sie zu einem Bytestrom werden, welcher dann via PIPE in den anderen Prozess geschoben wird. Dort werden sie wieder zB zu einem String oder einer Liste, und weiterverarbeitet. Aber auch hier gilt: es sind ab diesem Zeitpunkt entkoppelte Objekte!! Ein liste.append wird nur in jeweils einem Prozess eine Zustandsveraenderung nach sich ziehen! (2)

    Ich hoffe, das ist etwas klarer geworden.

    Natuerlich steht es dir frei, die Klasse "ProcessWebSocket" so zu veraendern, dass "send_msg" hinter den Kulissen so aussieht:

    Code
    def send_msg(self, msg):
          self.WebSocketQueue.put("send_msg", msg)

    Damit sieht die Benutzung von Main.py aus "normal" aus wie ein Methodenaufruf, aber hinter den Kulissen werkelt der Interprozessmechanismus.


    Und jetzt noch ein Nachtrag, im Sinne von bootsmann: ich halte diesen multiprocessing Ansatz fuer dein gegebenes Problem fuer voellig ueberkandidelt, und er macht dir nur Probleme, ohne welche zu loesen. Du neigst in meinen Augen dazu, zu viele Threads fuer Dinge einzusetzen (siehe die lange Diskussion mit Moira und das abschmierende Skript nach 20 Minuten - weniger ist mehr!)

    Und auch hier machst du das wieder - *3* Prozesse, von denen einer nur billige Synchronisation zwischen den beiden anderen macht ist nur Aerger, und selbst wenn die beiden anderen notwendig waeren zu viel des Guten.


    bootsmann hat Recht - ich wuerde das in einem Prozess machen, und entweder komplett single-threaded (mit der Ausnahme von GPIO-waits, weil das nunmal nicht anders geht), oder bestenfalls mit einem Thread pro Aufgabe (also Main-Thread fuer Webrequests, Hintergrundthread fuer den Barcode-Reader).

    Ich bin allerdings fuer Websocket-basierte Entwicklung eher ein Freund von Tornado, weil bottle da nur mit gevent etwas zu bieten hat & IMHO dadurch weniger klar wird.

    (1) Das ist nicht ganz richtig, es gibt shared memory. Das ist aber hier nicht wirklich relevant, vor allem weil es nur "simple" Datentypen beinhalten kann, bestenfalls serialisierbare. Es sind aber *keine* "reichen" Objekte mit Methoden, die dann Fernwirkung entfalten.

    (2) Es gibt mit dem Manager eine Moeglichkeit, komfortabel Datenstrukturen zwischen zwei Prozessen zu synchronisieren. Das ist aber in meinen Augen ein eher schwieriges Feature, weil es suggeriert, zwei Prozesse koennten bequem denselben Zustand teilen - obwohl sie in Wahrheit ziemlich viel hin- und herkommunizieren muessen dafuer, inklusive blockierender Synchronisation via Semaphoren - d.h. am Ende gewinnt man nichts gegenueber einer Single-Prozess und ggf. Thread-Loesung, wenn man das Feature nicht vorsichtig benutzt. Fuer den Anfang daher eher nicht benutzen, bis man es komplett verstanden hat.

  • Man kann eigene Callback's definieren, was ich auch in der Run() mache:
    [code=php]
    server.set_fn_new_client( partial(ProcessWebSocket.new_client, self) )
    server.set_fn_client_left( partial(ProcessWebSocket.client_left, self) )
    server.set_fn_message_received( partial(ProcessWebSocket.message_received, self) )
    [/php](mittlerweile ohne self.)

    Dort übergebe ich auch das "self" meiner Class an die Callbacks, sonst funktionieren die nicht wie von mir gewollt.

    Den Callbacks werden vom websocket_server.py Script "client" und "server" übergeben, nur ist das leider nicht bei "send_message" der Fall :( => https://github.com/Pithikos/python-websocket-server#methods
    Die "send_message" kann ich aber auch nicht direkt aus der Main.py aufrufen, weshalb ich mir dachte einen Wrapper dafür zu setzen, aber wie man sieht hab ich da irgendwie noch einen Denkfehler.... pcdau.gif

    Es funktioniert wie gesagt eigentlich auch alles, nur nicht das zurückschicken an den Client.

    Noch mal der derzeitige Code:

    • Main.py

      Spoiler anzeigen

      [code=php]
      #!/usr/bin/python3
      # coding: utf-8
      #
      # Werkzeug Verleih fuer JumpY
      # 12.01.2016 Copyright (C) by meigrafd (meiraspi@gmail.com) published under the MIT License
      #
      import multiprocessing as mp
      import time

      from Barcode import *
      from WebSocket import *
      #-------------------------------------------------------------------

      HID_device = '/dev/hidraw0'
      WebSocketHost = "0.0.0.0"
      WebSocketPort = 7070

      #-------------------------------------------------------------------

      # Main process
      mp.log_to_stderr(logging.DEBUG)
      # Establish communication queues
      BarcodeQueue = mp.Queue()
      WebSocketQueue = mp.Queue()

      # Start Child Processes
      BarcodeProcess = ProcessBarcode()
      WebSocketProcess = ProcessWebSocket()

      PB = mp.Process(target=BarcodeProcess.Run, args=(HID_device,BarcodeQueue))
      PW = mp.Process(target=WebSocketProcess.Run, args=(WebSocketPort,WebSocketHost,WebSocketQueue))
      PB.start()
      PW.start()

      # Endless loop of main program
      try:
      while True:
      if not WebSocketQueue.empty():
      request = WebSocketQueue.get()
      request = request.strip()
      returnlist = ""
      requestsplit = request.split(':')
      requestsplit.append("dummy")
      print("processing command: {}".format(command))
      command = requestsplit[0]
      value = requestsplit[1]
      if value == "dummy":
      value = "0"
      if command == "localping":
      returnlist += "\n localping:ok"

      elif command == "get_user":
      ID = BarcodeQueue.get()
      returnlist += "\n get_user:" + str(ID)

      if returnlist:
      WebSocketProcess.send_msg(returnlist)

      time.sleep(0.1)
      except (KeyboardInterrupt, SystemExit):
      print("Schliesse Programm..")
      [/php]

    • Barcode.py

      Spoiler anzeigen

      [code=php]
      # coding: utf-8
      import time, sys

      class ProcessBarcode(object):
      def __str__(self):
      return "Process Barcode"

      def run(self, Device, BarcodeQueue):
      self.Device = Device
      self.BQueue = BarcodeQueue

      import random
      try:
      while True:
      time.sleep(10)
      bla=0
      for x in range(0,10):
      bla += (random.randint(1,200))
      self.BQueue.put(bla)
      uhrzeit = time.strftime("%H:%M:%S")
      print("[{}] Barcode: {}".format(uhrzeit, bla))
      time.sleep(10)
      except (KeyboardInterrupt, SystemExit):
      sys.exit()
      return
      [/php]

    • WebSocket.py

      Spoiler anzeigen

      [code=php]
      # coding: utf-8
      import time, sys
      from websocket_server import WebsocketServer
      from functools import partial

      class ProcessWebSocket(object):
      def __str__(self):
      return "Process WebSocket"

      # Called for every client connecting (after handshake)
      def new_client(self, client, server):
      uhrzeit = time.strftime("%H:%M:%S")
      print("[%s] WebSocket: New client connected and was given id %d" % (uhrzeit, client['id']))
      self.client = client
      self.server = server
      self.server.send_message(client, "You are connected")

      # Called for every client disconnecting
      def client_left(self, client, server):
      if client['id']:
      uhrzeit = time.strftime("%H:%M:%S")
      print("[%s] WebSocket: Client(%d) disconnected" % (uhrzeit, client['id']))
      self.client = None

      # Called when a client sends a message
      def message_received(self, client, server, message):
      self.WSQueue.put(message)
      uhrzeit = time.strftime("%H:%M:%S")
      print("[{}] WebSocket: {}".format(uhrzeit, message))

      def send_msg(self, message):
      self.server.send_message(self.client, message)

      def run(self, WebSocketPort, WebSocketHost, WebSocketQueue):
      self.WSQueue = WebSocketQueue
      server = WebsocketServer(host=WebSocketHost, port=WebSocketPort)
      server.set_fn_new_client( partial(ProcessWebSocket.new_client, self) )
      server.set_fn_client_left( partial(ProcessWebSocket.client_left, self) )
      server.set_fn_message_received( partial(ProcessWebSocket.message_received, self) )
      server.run_forever()
      [/php]


    Ich hatte angenommen das durch "self.server = server" in new_client() dann auch in "send_msg" nutzbar ist, aber anscheint doch irgendwie nicht :s


    //EDIT: @deets: Danke für deine Mühe :thumbs1:
    Ich hatte es zuvor mit threading aber das lief nicht wie gewünscht. Soweit ich gelesen habe ist das aber auch langsamer (seite 24) und Wunschdenken... Vorher hatte ich es ebenfalls mit tornado versucht aber das verwendet auch gevent und blockiert das Script in dem es gestartet wird.

    Wenn ich in WS-Socket eine while starte um ein Send-Queue auszulesen, dann blockiere ich das ganze doch, oder nicht? Dann kann er doch auch nicht mehr auf die anderen Funktionen reagieren? :-/

  • Ich habe so ewtas in der Art ( Barcodeleser Serienummern von Devices) einfach mittels eines 15€ USB Scanner gelöst.
    Webseite zeigt "Bitte Scannen" und ein Eingabefeld hat immer den Focus.

    Wenn du einen RS232 Scanner nutzten musst sieht es anders aus.
    Allerdings stelle ich dann die Frage warum die GUI nicht gleich komplett in Python sondern der Mix aus PHP und Python.

    Offizieller Schmier und Schmutzfink des Forum.
    Warum einfach wenn's auch schwer geht ?

    Kein Support per PN !
    Fragen bitte hier im Forum stellen. So hat jeder etwas davon.

  • (ich habe erst jetzt gesehen, dass das Problem von __deets__ ausführlich erschlagen wurde, daher vergiss das unten Geschriebene)

    Die Fehlermeldung wird offenbar ausgeworfen von ProcessWebSocket.new_client: 'self.server.send_message(client, "You are connected")', denn diese Meldung kommt ja nicht.

    Ich weiß jetzt nicht, wie Python einen fehlenden Parameter handelt, aber wenn ich
    server.set_fn_new_client( partial(ProcessWebSocket.new_client, self) )
    anschaue, wird dort nur self (=ProcessWebSocket) übergeben. Der Parameter "server" kommt doch gar nicht an, oder?
    Also nach wie vor der Knackpunkt: welchen Wert/ID hat server in ProcessWebSocket.new_client?

    Könnte eine heiße Spur sein - kann aber auch sein, dass das in der Magie der (IMHO) krückenhaften OOP-Implementation von Python begründet ist. Aber wie heißt es so schön in "Zen of Python": "Simple is better than complex." :D
    Die Fehlermeldung wird offenbar ausgeworfen von ProcessWebSocket.new_client: 'self.server.send_message(client, "You are connected")', denn diese Meldung kommt ja nicht.

    Ich weiß jetzt nicht, wie Python einen fehlenden Parameter handelt, aber wenn ich
    server.set_fn_new_client( partial(ProcessWebSocket.new_client, self) )
    anschaue, wird dort nur self (=ProcessWebSocket) übergeben. Der Parameter "server" kommt doch gar nicht an, oder?
    Also nach wie vor der Knackpunkt: welchen Wert/ID hat server in ProcessWebSocket.new_client?

    Könnte eine heiße Spur sein - kann aber auch sein, dass das in der Magie der (IMHO) krückenhaften OOP-Implementation von Python begründet ist. Aber wie heißt es so schön in "Zen of Python": "Simple is better than complex." :D

    Einmal editiert, zuletzt von Zoppo Trump (12. Januar 2016 um 15:00)

  • Danke an meigrafd, dass er sich der Sache so extrem annimmt, das war eigentlich nicht in diesem Ausmaß geplant :lol:


    Ich habe so ewtas in der Art ( Barcodeleser Serienummern von Devices) einfach mittels eines 15€ USB Scanner gelöst.
    Webseite zeigt "Bitte Scannen" und ein Eingabefeld hat immer den Focus.

    Wenn du einen RS232 Scanner nutzten musst sieht es anders aus.
    Allerdings stelle ich dann die Frage warum die GUI nicht gleich komplett in Python sondern der Mix aus PHP und Python.

    Es wird ein USB Barcodescanner genutzt. Das System ist aber deutlich komplexer als nur das einfache Scannen von Barcodes (andere Codeteile sind hier nicht zu sehen). Die Verarbeitung übernimmt Python, die Anzeige PHP/Javascript/JQuery in verschiedensten Seiten. Das System ist dadurch auch von außerhalb als Verwaltung sichtbar und nicht nur auf dem Pi.

  • zoppo: schoen, dass du schon eine Meinung zur OOP-Implementierung von Python hast, ohne die dazu notwendige Erfahrung zu besitzen. Schnell gewonnenen Bauchgefuehle sind ja eine der Eckpfeiler gelungener Softwarentwicklung! Du moechtest nicht, dass man dich hier gleich in der Luft zerreisst, weil du noch Anfaenger mit Python bist? Gerne. Aber wenn du hier kraeftige Meinungen ausdrueckst, dann gibt's auch keinen Welpenschutz, ok? Such dir aus, was es sein soll...

    meigrafd: threading ist theoretisch langsamer, weil immer nur ein Thread gleichzeitig laufen kann. Das ist aber fuer deine Problematik irrelevant, denn der Barcode-Einlese-Thread blockiert zum lesen einer Nachricht, tut dann kurz etwas, und blockiert danach wieder. Das ist halt als Programmiermodell fuer Unerfahrene etwas einfacher - aber es geht auch in nur einem Thread mit Tornado. Dann muss man allerdings die multiprocessing.Pipe benutzten, weil man dann im ioloop von Tornado einen Filedeskriptor injizieren muss, damit auf den mit gewartet wird (wie eben auch auf Sockets und Timer). Siehe in meinem Code analog fuer nanomsg hier: https://github.com/deets/balanceb…n/server.py#L91. Das Tornado gevent benutzt waere mir jetzt neu, dann braechte es eigentlich den ioloop so nicht - mag aber sein.

  • Ich werfe noch folgendes in den Raum - sofern eine reine Python-Lösung angestrebt werden soll - denn alles andere halte ich persönlich für ineffizient:

    http://aiohttp.readthedocs.org/en/stable/

    Edit:
    Ob die Daten stimmen, kann ich nicht sagen - interessant ist es aber:

    http://klen.github.io/py-frameworks-bench/

  • Ich glaube ich habs dank deets Hinweis, nun endlich hingekriegt.

    Und zwar mache ich das jetzt so dass ich ein WSqueueIN und ein WSqueueOUT habe. Sobald eine Nachricht über WebSocket eingeht, wird diese in WSqueueIN eingefügt und anschließend wird sowas wie "server.send_message(client, self.WSout.get())" gemacht, was dazu führt das der Process an dieser Stelle solange stehen bleibt bis etwas ins WSqueueOUT eingefügt wurde - und das geschieht in der Main.py an der Stelle wo ich zuvor "send_msg" stehen hatte.

    Aktueller sieht der Code also so aus:

    • Main.py

    Spoiler anzeigen
    • PHP
      #!/usr/bin/python3# coding: utf-8## Werkzeug Verleih fuer JumpY# 12.01.2016  Copyright (C) by meigrafd (meiraspi@gmail.com) published under the MIT License#import multiprocessing as mpimport time, loggingfrom Barcode import *from WebSocket import *#-------------------------------------------------------------------HID_device = '/dev/hidraw0'WebSocketHost = "0.0.0.0"WebSocketPort = 7070#-------------------------------------------------------------------# Main processif __name__ == '__main__':    #mp.log_to_stderr(logging.DEBUG)    # Establish communication queues    BarcodeQueue = mp.Queue()    WSqueueIN = mp.Queue()    WSqueueOUT = mp.Queue()    # Start Child Processes    BarcodeProcess = ProcessBarcode()    WebSocketProcess = ProcessWebSocket()    PB = mp.Process(target=BarcodeProcess.Run, args=(HID_device,BarcodeQueue))    PW = mp.Process(target=WebSocketProcess.Run, args=(WebSocketPort,WebSocketHost,WSqueueIN,WSqueueOUT))    PB.start()    PW.start()# Endless loop of main program    try:        while True:            if not WSqueueIN.empty():                request = WSqueueIN.get()                request = request.strip()                returnlist = ""                requestsplit = request.split(':')                requestsplit.append("dummy")                command = requestsplit[0]                value = requestsplit[1]                print("processing command: {}".format(command))                if value == "dummy":                    value = "0"                if command == "localping":                    returnlist += "localping:ok"elif command == "get_user":                    ID = BarcodeQueue.get()                    returnlist += "get_user:" + str(ID)if returnlist:                    WSqueueOUT.put(returnlist)            time.sleep(0.1)    except (KeyboardInterrupt, SystemExit):        print("Schliesse Programm..")[/php][/spoiler][/list][list]
      [/spoiler]
    Spoiler anzeigen
    • [/spoiler]
    • Spoiler anzeigen
      PHP
      [b]WebSocket.py[/b][spoiler][code=php]# coding: utf-8import time, sysfrom websocket_server import WebsocketServerfrom functools import partialclass ProcessWebSocket(object):    def __str__(self):        return "Process WebSocket"    # Called for every client connecting (after handshake)    def new_client(self, client, server):        print("[%s] WebSocket: New client connected and was given id %d" % (time.strftime("%H:%M:%S"), client['id']))        self.client = client        self.server = server        self.server.send_message(client, "You are connected")    # Called for every client disconnecting    def client_left(self, client, server):        if client['id']:            print("[%s] WebSocket: Client(%d) disconnected" % (time.strftime("%H:%M:%S"), client['id']))        self.client = None    # Called when a client sends a message    def message_received(self, client, server, message):        self.WSin.put(message)        print("[{}] WebSocket received: {}".format(time.strftime("%H:%M:%S"), message))        outmsg = self.WSout.get()        server.send_message(client, outmsg)        print("[{}] WebSocket send: {}".format(time.strftime("%H:%M:%S"), outmsg))    def Run(self, WebSocketPort, WebSocketHost, WSqueueIN, WSqueueOUT):        self.WSin = WSqueueIN        self.WSout = WSqueueOUT        server = WebsocketServer(host=WebSocketHost, port=WebSocketPort)        server.set_fn_new_client( partial(ProcessWebSocket.new_client, self) )        server.set_fn_client_left( partial(ProcessWebSocket.client_left, self) )        server.set_fn_message_received( partial(ProcessWebSocket.message_received, self) )        server.run_forever()[/php][/spoiler][/list]Wenn ich nun Main.py starte und im Web-Interface auf "Connect" klicke, passiert folgendes:[b]WebIf[/b][code]Connection opened!Sent message: >>>get_user:0<<<Received message: >>>You are connected<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Received message: >>>get_user:725<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Received message: >>>get_user:1159<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<Sent message: >>>get_user:0<<<

      (das webif schickt alle 2 sek eine Anfrage raus)

      Main.py

      Code
      pi@raspberrypi:~/Verleih$ ./Main.py         Listening on port 7070 for clients..[16:43:47] WebSocket: New client connected and was given id 1[16:43:47] WebSocket received: get_user:0processing: get_user[16:43:55] Barcode: 725[16:43:55] WebSocket send: get_user:725[16:43:55] WebSocket received: get_user:0processing: get_user[16:44:15] Barcode: 1159[16:44:15] WebSocket send: get_user:1159[16:44:15] WebSocket received: get_user:0processing: get_user

      :bravo2:


      Vielen Dank an Alle für die Hilfe :thumbs1:

  • Kleine Anmerkungen: partial(Klasse.methode, instanz) ist deutlich umstaendlicher zu dem semantisch äquivalenten instanz.methode - letzteres liefert einfach eine "BoundMethod", das ist quasi schon ein eingebautes partialling mit dem self-Argument.

    Ich wuerde dir WIRKLICH, WIRKLICH, WIRKLICH den PEP8 Styleguide ans Herzen legen. Ich habe grosse Schwierigkeiten, deinen Code zu lesen. Und Code wird ueblicherweise deutlich mehr gelesen, als geschrieben. Es gibt ein paar sehr simple, aber wichtige Konventionen bezueglich Variablennamen, Klassennamen, Funktions/Methodennamen, (Pseudo)Konstantennamen, die du verletzt - und damit machst du es anderen, die PEP8 gewohnt sind, schwerer, deinen Code zu lesen, und dir zu helfen. Was ja gelegentlich notwendig ist fuer uns alle :) Vor allem sind deine Namenskonventionen wild gemischt - warum self.client, self.send_something, aber nicht self.ws_in?

    Das __str__ ist schon etwas besser, weil es jetzt nicht voellig sinnlos Zustand auf der Instanz erzeugt (und schlimmstenfalls ueberschreibt!), aber brauchen tust du es nicht - also weg damit. Besser ist eh __repr__ zu ueberschreiben, aber wenn das sinnvoll sein soll, dann sollte es sich auch durch Zustand der ausgegeben wird auszeichnen. Python ist von alleine schon gut darin, Klassennamen anzuzeigen.

  • Ich benutze den nicht, aber ein Kollege schwoert drauf. Ich verwende pylint, das formatiert zwar nicht, warnt aber - dann korrigiere ich natuerlich entsprechend. Und es warnt natuerlich vor noch einer ganzen Reihe semantischer Fehler, wenn es kann - fehlgeschriebenen Namen und so.


  • Ich benutze den nicht, aber ein Kollege schwoert drauf. Ich verwende pylint

    schön wäre auch ein guter Online-Formatter. Braucht man ja nicht so oft, denn der eigene Code ist ja immer perfekt :^^:
    Einen Online-Checker gibt es hier; ob der taugt, kann ich im Moment nicht beurteilen.
    Aber die Online-Formatter werfen so Sachen aus wie

    Code
    if (width == 0 and height == 0 and color == 'red'
       and emphasis == 'strong'
       or highlight > 100): raise ValueError("sorry, you lose")


    oder

    Code
    if width == 0 and height == 0 and color == 'red' and emphasis \
       == 'strong' or highlight > 100:
       raise ValueError('sorry, you lose')


    => unbrauchbar

    Im Moment kämpfe ich mit der Installation von autopep8.
    Aber ich scheitere schon an der Zeile

    Code
    pip install --upgrade autopep8
    Code
    pi@raspi-1:~/Download/git/pep8 $ pip
    -bash: pip: Kommando nicht gefunden.
    pi@raspi-1:~/Download/git/pep8 $ python --version
    Python 2.7.9


    Angeblich soll [font="Courier New"]pip[/font] in Python 2.7.9 enthalten sein?
    Naja, ich werd's schon noch durchschauen..

    (Edith: ich hatte mich darauf verlassen, dass pip schon installiert ist). Aber nach

    Code
    sudo apt-get install python-pip

    war's dann da :^^: )

    Einmal editiert, zuletzt von Zoppo Trump (12. Januar 2016 um 23:45)

  • Durchsuch mal bitte deine Pakete nach "python" und "pip" (aptitude search python | grep pip) - ich wette, das gibt's als extra Paket. Was bei Python dabei ist, und was dann wiederum ein Linux Paketverwalter daraus macht - das sind zwei Paar Schuhe.


  • Durchsuch mal bitte deine Pakete nach "python" und "pip" (aptitude search python | grep pip) - ich wette, das gibt's als extra Paket. Was bei Python dabei ist, und was dann wiederum ein Linux Paketverwalter daraus macht - das sind zwei Paar Schuhe.


    ich musste es nachinstallieren; siehe Edit oben.

  • Um noch mal auf allgemein Multiprocessing zurück zu kommen:

    Macht es Sinn mehrere einzelne Prozesse laufen zu haben in denen sagen wir mal 2 Threads enthalten sind?

    Also jetzt nicht im Bezug auf das hier angesprochene Konstrukt sondern viel mehr auf mein Roboter-Projekt, wo nun ein Pi2 als Brain zum Einsatz kommt...

    Aktuell habe ich einzelne Scripts die untereinander über Socket kommunizieren und zuletzt hatte ich es so gemacht das ich mithilfe von psutil die Core_Affinity geändert hab, also das Haupt-Script auf zB Core#3 und das WebSocket-Script auf Core#4 verlagert habe (diese beiden Cores sind vom Kernel Isoliert). Dadurch wollte ich erreichen das auf diesen Cores wirklich nichts anderes läuft und meine Roboter-Script die volle Aufmerksamkeit/Rechenleistung genießen. Auf den ersten beiden Cores läuft dann Raspbian-Lite sowie der Webserver mit der PiCam usw.

    Das Haupt-Script verwendet bereits threading, ein mal für die Schnittstelle zum Arduino und ein mal für den SocketServer. Das WebSocket-Script verwendet tornado aber sonst kein threading. Dazu kämen jetzt aber noch mind. 2 Scripts für die Kartierung und Sprachsteuerung, wobei ersteres am meisten Rechenleistung benötigen wird.

    Nun war halt meine Überlegung Multiprocessing zu verwenden, da ich ja eigentlich sowieso einzelne Prozesse verwende weil ich mehrere Scripts hab. Nun weiß ich aber nicht ob es mehr Sinn macht auch die Threading-Sache im Haupt-Script ebenfalls mit Multiprocessing aufzuspalten bzw ob das besser wäre als bisher mit Threading? Oder sollte ich das lieber so lassen wie es ist? :huh:

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!