Laufende Funktion einer Klasse per socket ändern

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
Seetsay
User
Beiträge: 4
Registriert: Montag 25. August 2014, 16:06

Nabend.

Ich bin recht neu in Python und versuche gerade eine Klasse mit unterschiedlichen Funktionen laufen zu lassen. Das ganze steuert dann einen Arduino über eine pyfirmata.
Das klappt auch ganz gut. Hier das der ausgedünnte Code der Klasse:

Code: Alles auswählen

class myArduino():
  def __init__(self):
    print "Start"
    # weitere Einstellungen
  def fade(self):
    print "Fade"

  def flash(self,speed=0.1):
    print "Flash"

  def wait(self,delay=0.1):
    time.sleep(delay)
  
  def run(self, task):
    while True:
      self.running = getattr(self, task)()
Jetzt versuche ich auf einen socket lauschen zu lassen um dann per Klasse.run('name') die Funktion in der Klasse aufzurufen und laufen zu lassen.
Das klappt; leider aber nur genau ein mal.

Code: Alles auswählen

a = myArduino()
MAX_LENGTH = 4096
def handle(clientsocket):
  while 1:
    buf = clientsocket.recv(MAX_LENGTH)
    if buf == '': return #client terminated connection
    a.run(buf)
    print buf
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
PORT = 10000
HOST = '0.0.0.0'
serversocket.bind((HOST, PORT))
serversocket.listen(10)

def readSocket():
  while 1:
    #accept connections from outside
    (clientsocket, address) = serversocket.accept()
    ct = threading.Thread(target=handle, args=(clientsocket,))
    ct.run()
s = threading.Thread(target=readSocket, args=())
s.start()

Es liegt ja wohl am Threading. Da hab ich einen Knick in der Logik.
Was muss ich wo wie ändern, damit ich die Klasse myArduino genau einmal laufen habe und trotzdem die ganze Zeit auf den socket lausche?
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Seetsay: Wann wird Deine run-Methode wieder verlassen?

Und dann noch das übliche: Du hast Dir ein Streaming-Protokoll ausgesucht, das heißt, Du kannst nur einen Strom an Bytes lesen. Um also Befehle zu empfangen, mußt Du sie in ein Protokoll einbetten, das den Anfang und das Ende eines Befehls festlegt.
Seetsay
User
Beiträge: 4
Registriert: Montag 25. August 2014, 16:06

Sirius3 hat geschrieben:@Seetsay: Wann wird Deine run-Methode wieder verlassen?
Ich plante eigentlich, dass run() immer läuft und die gewünschte Funktion aufruft. Ein externer Aufruf von run() sollte also die vorherige Runde beenden und eine neue Funktion einschalten.
Denkfehler meinerseits?
Sirius3 hat geschrieben:Und dann noch das übliche: Du hast Dir ein Streaming-Protokoll ausgesucht, das heißt, Du kannst nur einen Strom an Bytes lesen. Um also Befehle zu empfangen, mußt Du sie in ein Protokoll einbetten, das den Anfang und das Ende eines Befehls festlegt.
Das verstehe ich nicht. Wenn ich nämlich einfach mal nicht run() aufrufe, sondern einfach den gesendeten Datenstrom ausgebe, dann bekomme ich den Befehl wie gewünscht als String.

Code: Alles auswählen

import socket
import sys
HOST = '127.0.0.1'
PORT = 10000
s = socket.socket()
s.connect((HOST, PORT))

while 1:
    msg = raw_input("Command To Send: ")
    if msg == "close":
       s.close()
       sys.exit(0)
    s.send(msg)
Ich kann also von diesem Script die Befehle eingeben und mit "Enter" losschicken, diese kommen dann sauber am Server an.
Gibt es da Unterschiede in Betriebssystemen? Ich nutze Linux, Debian um genau zu sein.
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

Seetsay hat geschrieben:Ich kann also von diesem Script die Befehle eingeben und mit "Enter" losschicken, diese kommen dann sauber am Server an.
@Seetsay: Wenn etwas zufällig funktionert, dann heißt das noch lange nicht, daß alles fehlerfrei ist, und unter jeder Situation das tut, was man meint, daß es tut.
BlackJack

@Seetsay: Schau noch mal in der `threading`-Dokumentation mit welcher Methode man Threads startet.
Seetsay
User
Beiträge: 4
Registriert: Montag 25. August 2014, 16:06

Sirius3 hat geschrieben:Wenn etwas zufällig funktionert, dann heißt das noch lange nicht, daß alles fehlerfrei ist, und unter jeder Situation das tut, was man meint, daß es tut.
Das hilft mir leider nicht wirklich weiter.
Was hast du für Ideen oder Vorschläge wie ich zukünftig evtl. auftretende Probleme im Voraus ausschließen kann?
BlackJack hat geschrieben:@Seetsay: Schau noch mal in der `threading`-Dokumentation mit welcher Methode man Threads startet.
In dem Zusammenhang bin ich auf Events gestoßen. Ich kann also nun während eines laufenden Tasks die gewünschte Funktion umschalten. Das Problem wäre also gelöst.
BlackJack

@Seetsay: Du musst den Code der den Datenstrom liest, so schreiben das im Ernstfall auch bei jedem `recv()` ein einzelnes Byte von einer Nachricht gelesen werden kann und anders herum auch mehr als eine Nachricht auf einmal gelesen werden kann. Nur dann ist der Code wirklich fehlerfrei. Dazu musst Du die Nachrichten irgendwie voneinander unterscheiden können. Die üblichen Wege: Jede Nachricht ist gleich gross, oder es gibt eine Bytefolge die garantiert nur am Ende vorkommen kann und niemals innerhalb einer Nachricht, oder am Anfang wird übermittelt wie viele Bytes die folgende Nachricht gross ist. Wahlweise mit einer festen Byteanzahl für die Grösseninformation oder auch dort wieder mit einem Byte oder einer Bytefolge die das Ende der Zahl markiert.

Das umzusetzen ist alles ein wenig fummelig und eine nette Programmierübung aber eigentlich möchte man das alles nicht selber machen und irgend etwas verwenden was auf einer höheren Ebene als direkt auf den Sockets ansetzt, also schon ein Protokoll implementiert.
Seetsay
User
Beiträge: 4
Registriert: Montag 25. August 2014, 16:06

Könnte ich dafür Json etc. nehmen?
{'cmd':'doThat', 'length':6}
So könnte ich erstmal valides Json habe, dann noch mal ob length die Länge des Befehls wäre? Außerdem wäre es lesbar und könnte auch von anderen Programmen einfach angesprochen werden
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du könntest aber auch einfach *sofort* eine Lib nehmen, die auf JSON basiert, Dir aber bereits Zugriffe für entfernte Dienste zur Verfügung stellt, wie etwa "JSON RPC". Dafür gibt es verschiedene Libs in Python; da musst Du mal gucken, welche die beste Wahl ist.

(Ansonsten baust Du das ganze letztlich in - vermutlich "schlechter" - nach ;-) )

Wie BlackJack schon sagte, kann das natürlich eine nette Fingerübung sein; aber Dir kommt es ja auf etwas anderes an. Insofern nutze *fertige* Sachen für Nebenschauplätze Deines Programms :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Seetsay: Ich weiss jetzt nicht so ganz was das bringen soll. Wozu JSON an dieser Stelle? Um das JSON als Nachricht zu verschicken? Dann muss man doch aber wissen wie lang das JSON ist, *vorher*. Oder man parst das JSON beim Empfangen und weiss dadurch dann wann *ein* JSON-Objekt komplett geparst wurde. Dann ist aber die Längeninformation *in* dem JSON wurst, weil man die Nachricht erst dekodieren muss bevor man dann darin nachschauen kann wie lang sie ist, was keinen Sinn macht.
Antworten