Ablaufproblem pexpect

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
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo,

ich möchte mich kurz vorstellen, mein Name ist Alex, ich programmiere seit ca. 15 Jahren, damals habe ich in der Schule mit Pascal angefangen,
dann ging es in der schulischen Ausbildung mit ASM und C/C++ weiter.
Dann im Beruf mit VBA, VB und VB.NET. Privat habe ich mal min Objective-C reingeschnuppert.

Jetzt als Admin bediene ich mich der Sprache, mit der sich mein Problem am besten lösen lässt. Da nehme ich mal PHP oder Perl, oder auch mal VB.NET oder ich arbeite komplett im Shell-Skript.

Bei einen Problem auf einen Linux-Rechner, bin ich auf Python gestoßen und finde die Sprache sehr praktisch, natürlich muss man sich erst einmal daran gewöhnen an die Einrückungen in den Schleifen etc.pp.

Mein Problem ist relativ schnell umschrieben, ich lese in einer Endlosschleife Ausgaben eines Programms aus das Messfühler abfragt. Das mache ich mit pexpect sehr genial, würde es pexpect nicht geben, müsste man es erfinden.

Code: Alles auswählen

if __name__ == "__main__":

    child_mess = pexpect.spawn ('get_mess.bin -C s1')

    while True:
        try:
            child_mess.expect('measurement okay')
            process_sensor(child_mess.before)
        except KeyboardInterrupt:
            child_mess.close(force=True)
            break

Das funktioniert sehr gut, ich starte den Prozess, wenn er den Text "measurement okay" entdeckt, übergibt er die vorherige Ausgabe an die Funktion process_mess()

Das wird sie bereinigt und die relevanten Messdaten gefiltert.

Diese Prüfe ich dann in einer letzten Funktion auf den richtigen Sensor.

Code: Alles auswählen

def process_sensor(sensor_text):
	str1 = remove_tags(sensor_text)
	str2 = str1.replace('sensor1: ','')
	transcribed = str2
	if transcribed.finde("123"):
		os.system("get_mess.bin -T s1")
Soweit alles toll, ich prüfe nun ob es "123" ist, ist es das soll ich neue speziellere Messung passieren.
Das geht natürlich nicht da das Programm noch von dem ersten Prozess child_mess belegt ist und ich kann auch keinen neuen Prozessstarten, weil der Sensor noch
vom ersten Programm belegt ist.

Ich muss also den ersten Prozess child_mess beenden, dann kann ich meine speziellere Messung starten, wenn diese dann fertig ist fehlt mir der erste Prozess.
Klar ich habe ihn beendet:

Code: Alles auswählen

def process_sensor(sensor_text):
	str1 = remove_tags(sensor_text)
	str2 = str1.replace('sensor1: ','')
	transcribed = str2
	if transcribed.finde("123"):
		 child_mess.close(force=true)
                 os.system("get_mess.bin -T s1")
        else:
                 #mache irgendwas da braucht man die Messung nicht
Wenn er dann wieder in die Endlosschleife in Main zurückkehrt, ist der Prozess nicht mehr da :-(

Wie kann ich das Programm anlegen, so dass er wenn "123" kommt den ersten Prozess beendet, die spezielle Messung vornimmt und dann den Prozess wieder startet.
Wenn andere Werte außer "123" kommen, dann soll er normal weiter machen?

Hoffe ihr habt einen Denkanstoß für mich :-)

Danke für Eure Hilfe.

Gruß Alex
xeike
User
Beiträge: 83
Registriert: Donnerstag 28. Februar 2013, 09:58

facebraker hat geschrieben:Wie kann ich das Programm anlegen, so dass er wenn "123" kommt den ersten Prozess beendet, die spezielle Messung vornimmt und dann den Prozess wieder startet.
Wenn andere Werte außer "123" kommen, dann soll er normal weiter machen?
Hilft eine Variable?

Code: Alles auswählen


    muss_neu_starten = True
    while....
        # ....
        if muss_neu_starten:
            child_mess = pexpect.spawn ('get_mess.bin -C s1')
        # ...
        muss_neu_starten = process_sensor(...)

Sonst verstehe ich das Problem noch nicht.

Xe
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hallo, facebraker. Willkommen im Forum. :)

Eine Rückfrage zu deiner Problembeschreibung: Woher bezieht das externe Programm denn eigentlich die Informationen, welche Messungen es durchführen soll? Kriegt es die aus einer Datei oder sonstiger von dir beeinflussbarer Quelle? Oder ist das ein fest im externen Programm implementierter Ablauf? Falls letzteres zutrifft: Hast du die Möglichkeit, diesen Ablauf z.B. durch Übergabe spezieller Kommandozeilen-Argumente dahingehend zu steuern, dass er ab der Unterbrechung mit den noch ausstehenden Messungen weitermachen kann?

Falls all das nicht geht, bleibt dir ja nur noch die Möglichkeit, den Prozess des Mess-Programms zu pausieren und nach der erfolgten anderen Messung weiter fortzuführen. Inwiefern dies technisch möglich ist, lässt sich aber nach dem bisherigen Informationsstand schlecht einschätzen. Grundsätzlich ist es zwar möglich, ein Programm anzuhalten (weiß ich zumindest von Linux-ähnlichen Systemen), aber es kann ja trotzdem passieren, dass das aufgeweckte Programm nicht mitspielt, da zwischendurch eine andere Messung durchgeführt wurde. Das müsstest du ggf einfach mal ausprobieren, ob es da negative Auswirkungen gibt - von deiner Beschreibung her tendiere ich stark zu der Annahme, dass das aufgeweckte Programm ziemlich "verwirrt" sein wird, wenn der Sensor sich plötzlich aufgrund der anderen Messung in einem ganz anderen Zustand befindet.
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo Xe, Hallo snafu

denke für die schnelle Antwort, das Problem ist, dass der Prozess (child_mess) vor der Endlosschleife gestartet wurde (IMHO)

Code: Alles auswählen

if __name__ == "__main__":

    child_mess = pexpect.spawn ('get_mess.bin -C s1')

    while True:
        try:
            child_mess.expect('measurement okay')
            process_sensor(child_mess.before)
        except KeyboardInterrupt:
            child_mess.close(force=True)
            break
Selbst wenn ich nach process_sensor den Prozess neu starte (child_mess = pexpect.spawn ('get_mess.bin -C s1'))
fliegt er mir um die Ohren:

Code: Alles auswählen

if __name__ == "__main__":

    child_mess = pexpect.spawn ('get_mess.bin -C s1')

    while True:
        try:
            child_mess.expect('measurement okay')
            process_sensor(child_mess.before)
            child_mess = pexpect.spawn ('get_mess.bin -C s1')
        except KeyboardInterrupt:
            child_mess.close(force=True)
            break
Deshlab bin ich ein bisschen ratlos und dazu noch ein Python-Neuling ;-)

@Snafu:

Ich kann es nur mit Übergabeparamentern steuern:

Code: Alles auswählen

'get_mess.bin -C s1'
oder

Code: Alles auswählen

get_mess.bin -T s1
Pausieren ist schlecht, nur die erste Messung stoppen :-( und dann wieder starten.
Die Messung selber stört das nicht, ich muss es nur in den Programmablauf abbilden können.

Wenn "123" dann stoppe den Prozess und starte ihn nach der 2. Messung wieder, ansonsten frage den ersten Prozess ganz normal weiter ab.


Der Kernpunkt, wie kann ich die Enlosschleife anlegen so dass ich nach belieben den Prozess wieder starten kann?

Danke

Gruß Alex
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Achso, es geht dir einfach nur um die Logik? Dann sollte dies wunschgemäß funktionieren:

Code: Alles auswählen

while True:
    try:
        child_mess = pexpect.spawn('get_mess.bin -C s1')
        child_mess.expect('measurement okay')
        output = child_mess.before
        child_mess.close(force=True)
        process_sensor(output)
    except KeyboardInterrupt:
        child_mess.close(force=True)
        break
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo snafu,

dann müsste ich deinen Vorschlag noch mit einer Überprüfung ergänzen, nur wenn der Prozess gestoppt wurde,
so wie es Xe vorgeschlagen hat, werde ich nachher gleich probieren.

Vielen Dank!!!

Gruß Alex
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo, ich habe noch einmal die Verbesserungen eingebaut und habe den selben Fehler, aber diesmal mit
dem genauen Fehler.

Ich habe es so eingebaut, to_start ist erst einmal True und wird beim ersten mal gestartet, ich setze dann to_start auf False
sonst wird es immer wieder in der Schleife gestartet :-/
Das PRINT ist zur kontrolle ob er den Zweig ausführt.

Dann lasse ich es durchlaufen und hoffe dass es nach dem Durchlauf wieder gestartet wird, geht aber nicht.

Code:

Code: Alles auswählen

if __name__ == "__main__":

    #child_mess = pexpect.spawn ('get_mess.bin -C s1')

    to_start=True
    while True:
        try:
            if to_start:
                   child_mess = pexpect.spawn ('get_mess.bin -C s1')
                   print "wird gestartet"
                   to_start=Flase
            child_mess.expect('measurement okay')
            process_sensor(child_mess.before)
        except KeyboardInterrupt:
            child_mess.close(force=True)
            break
Hier der Code, der process_sensor - Funktion

Code: Alles auswählen

def process_sensor(sensor_text):
   str1 = remove_tags(sensor_text)
   str2 = str1.replace('sensor1: ','')
   transcribed = str2
   if transcribed.finde("123"):
      child.close(force=True)
      os.system("get_mess.bin -T s1")
      to_start=True
Soweit funktioniert es auch, der erste Prozess wird beendet und ich kann die 2. Messung durchführen und bekomme auch Ergebnisse.
Wenn ich dann aber wieder in der Main-Funktion auf den child-Prozess zugreifen will, ist er nicht mehr da.
Auch das oben genannte Print wird gestartet wird nicht ausgeführt?????

Ich glaube ich habe mich in der Syntax verzettelt

Die Fehlermeldung ist:
Traceback (most recent call last):
File "./get_sensor_data.py", line 93, in <module>
child.expect('measurement okay')
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 1316, in expect
return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 1330, in expect_list
return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize)
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 1383, in expect_loop
c = self.read_nonblocking (self.maxread, timeout)
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 797, in read_nonblocking
raise ValueError ('I/O operation on closed file in read_nonblocking().')
ValueError: I/O operation on closed file in read_nonblocking().
Als ob er den Start nicht ausführt, aber warum??? Ein Frage für die Python-Spezies


Danke


Gruß Alex

PS: SNAFU wie bekommst du den Python-Code hier hin, sind das spezielle Tags?
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo,

ich habe jetzt versucht den Fehler weiter einzugrenzen, in dem ich Debug-Ausgaben starte:

Code: Alles auswählen

if __name__ == "__main__":

    #child_mess = pexpect.spawn ('get_mess.bin -C s1')

    to_start=True
    while True:
        try:
            print "in der While schleife nach try:" + str(to_start)
            if to_start:
                   child_mess = pexpect.spawn ('get_mess.bin -C s1')
                   print "wird gestartet"
                   to_start=Flase
            child_mess.expect('measurement okay')
            process_sensor(child_mess.before)
        except KeyboardInterrupt:
            child_mess.close(force=True)
            break
und:

Code: Alles auswählen

def process_sensor(sensor_text):
   str1 = remove_tags(sensor_text)
   str2 = str1.replace('sensor1: ','')
   transcribed = str2
   if transcribed.finde("123"):
      child.close(force=True)
      os.system("get_mess.bin -T s1")
      to_start=True
      print "in der Funktion nach 2. Messung:" + str(to_start)

Die Fehlermeldung sieht wie folgt aus:
in der Funktion nach 2. Messung:True
in der While schleife nach try:False
Traceback (most recent call last):
File "./get_sensor_data.py", line 95, in <module>
child.expect('measurement okay')
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 1316, in expect
return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 1330, in expect_list
return self.expect_loop(searcher_re(pattern_list), timeout, searchwindowsize)
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 1383, in expect_loop
c = self.read_nonblocking (self.maxread, timeout)
File "/usr/lib/python2.7/dist-packages/pexpect.py", line 797, in read_nonblocking
raise ValueError ('I/O operation on closed file in read_nonblocking().')
ValueError: I/O operation on closed file in read_nonblocking().
Sieht für mich so aus, in der Funktion nach der 2. Messung ist es korrekt True
Springt er in die While-Schleife zurück wird aus dem richtigen True ein False und somit wird die
If-Schleife mit dem Start nicht abgearbeitet und es fliegt mir um die Ohren, aber warum???

Gruß Alex
BlackJack

@facebraker: Das scheint nicht der Qelltext zu sein, den Du tatsächlich ausführst, denn die Funktion, die den Messwert verarbeitet stoppt ein `child` während das auf Modulebene offenbar `child_mess` heisst. Zeichenketten haben auch keine `finde()`-Methode, das kann also auch nicht richtig sein.

Du solltest als erstes mal den Quelltext in ``if __name__ == '__main__':``-Block in einer Funktion verschwinden lassen und die aus diesem ``if`` heraus aufrufen. Dann kommt man nicht in Versuchung auf „globale” Werte zuzugreifen.

Das `to_start` in der `process_sensor()`-Funktion ist ein anderes als das `to_start` auf Modulebene. Das auf Modulebene kannst Du aus einer Funktion heraus nicht verändern ohne ``global`` zu verwenden, und *das* sollte man *nicht* machen. Eine saubere Lösung wäre es der `process_sensor()`-Funktion das `child_mess` als Argument zu geben und als Rückgabewert zum Beispiel einen Wahrheitswert der sagt ob der externe Prozess abgebrochen wurde und deshalb neu gestartet werden muss oder nicht. Noch besser wäre es das Lesen des ersten Sensors in einer Klasse zu kapseln die sich neben dem Prozess noch merkt ob er mit beendet wurde und neu gestartet werden muss. Ungetestet:

Code: Alles auswählen

from contextlib import closing
import pexpect


class SensorReader(object):
    def __init__(self, command_line, expected):
        self.command_line = command_line
        self.expected = expected
        self.reading_process = None

    def __iter__(self):
        return self

    def next(self):
        if self.reading_process is None:
            self.reading_process = pexpect.spawn(self.command_line)
        self.reading_process.expect(self.expected)
        return self.reading_process.before

    def close(self, force=True):
        self.reading_process.close(force)
        self.reading_process = None


def process_sensor(reader, value):
    value = remove_tags(value)[len('sensor1: '):]
    if '123' in value:
        reader.close()
        # 
        # Hier die Messungen mit anderen Argumenten durchführen.
        # 


def main():
    try:
        with closing(
            SensorReader('get_mess.bin -C s1', 'measurement okay')
        ) as reader:
            for value in reader:
                process_sensor(reader, value)
    except KeyboardInterrupt:
        pass


if __name__ == '__main__':
    main()
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo BlackJack,

Danke für die Antwort.
Ich muss mich 2 mal entschuldigen, klar child und child_mess sind das gleiche, ich bin jetzt bloß zuhause und habe alten und neuen Code zusammen kopiert ... Hab Remote nur immer geändert und die Fehlermeldungen produziert und hier am Rechner gepostet ;-)

Dann muss ich mich entschuldigen, hab mich ein bisschen verhaspelt, beim vereinfachen des Problems, es greift nicht das selbe Programm zu sondern verschiedene Programme auf einen Sensor, wie im ersten Post erklärt.

Wenn der Sensor vom ersten Programm blockiert wird, kann ich nicht mit dem 2. messen (spezielle Messung) also muss ich das erste Programm stoppen dann kann ich mit den 2. Programm messen und kriege jetzt das erste Programm nicht wieder zum laufen.

Sorry für die Verwirrung. :oops:

Deine Lösung überschreitet gerade meinen Python-Horizont, klar weiß ich was OOP ist aber im Moment kenn ich die Python Syntax nicht. :K

Könntest du mir es kurz erklären, grob.

Danke schön!

Gruß Alex
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Hallo, klar

die Variablen-Gültigkeit da hätte man gleich drauf kommen können :twisted:

Klar ich habe mir 2 verschiedene Variablen ausgeben lassen, darum wurde to_start plötzlich von True auf False gesetzt :!:

Ich werde es morgen gleich probieren, ich lasse process_sensor einfach True oder False zurückgeben lassen,oder?

Danke für den Tipp.

Gruß Alex
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@facebraker: statt os.system besser subprocess benutzen und auch pexpect.spawn nimmt die Argumente als Liste entgegen:

Code: Alles auswählen

child_mess = pexpect.spawn ('get_mess.bin', ['-C', 's1'])
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Guten Morgen,

das stimmt Sirius3, das os.system ist mir auch ein Dorn im Auge.
Aber ich wollte erst einmal das Problem mit der zweiten Messung und der Rückkehr in die Endlosschleife mit der ersten Messung lösen.
Wenn das sauber klappt benutze ich dann auch pexpect und mache den Code hübsch :oops:

Wie gesagt, ich bin Neuling in Python und taste mich langsam heran, also learning by doing ...

Danke für den Tipp auch mit der Argumentenliste.

Gruß Alex
facebraker
User
Beiträge: 25
Registriert: Montag 22. April 2013, 13:17

Guten Morgen,

es funktioniert :lol:
Ich habe die Lösung von Xeike umgesetzt, ich habe ein Variable die ich immer prüfe ob der Prozess neugestartet werden soll (wenn er beendet wurde).
Die Funktion process_sensor() gibt True oder False zurück, wenn der Wert "123" ist, dann True und der Prozess wird dann neu gestartet.

Die Lösung von BlackJack ist sehr elegant, aber ich bin absoluter Beginner in Python und muss mich erst in OOP in Python einarbeiten, aber in Zukunft wird
kein Weg daran vorbei kommen ... aber wenn ich es jetzt nutzen würde, wäre es Copy & Paste ohne Sinn und Verstand.

Vielen Dank für Eure Hilfe.

Gruß Alex
Antworten