Hallo Alfons!
Auch dir Danke für deine Hilfe.
Alles was mit lock.acquire() und lock.release() geschieht habe ich nicht selbst implementiert.
Bin selber noch sehr unerfahren was das Programmieren umfangreicherer Anwendungen angeht. Deshalb habe ich hier einfach auf das PyQt-eigene Threading zurückgegriffen. Um die notwendigen Messwerte zwischen Einlese-Thread und Visualisierungs-Thread auszutauschen, benutze ich diesen PyQt-Signal-Slot-Mechanismus, da dieser "threadsicher" ist.
Ich habe sicherheitshalber nochmal gerade die zwei Profiles miteinander verglichen:
Im Laptop-Profil: weder lock noch release zu finden
Im Raspi-Profil: beide, sowohl lock als auch release zu finden. release braucht für 4765 calls nur 0.01 Sekunden. lock braucht für 9527 calls 31.46 Sekunden...
Um deine Frage mit der Schleife zu beantworten: Für die zyklische Abfrage nutze ich keine "while True:"-Schleife per se, aber sowas in der Art: Dazu verwende ich einen Timer (auch von Qt), welcher alle 100ms erneut die Funktion "read_values" aufruft. In dieser "read_values" scheint ja auch das Problem zu liegen, in den Bildern habe ich diese Funktion mit dem Cursor ausgewählt, sodass man die Zeit sieht (vgl. 12.080s vs. 39.739s)
Aber genug davon, hier der Thread:
Code: Alles auswählen
class ArduinoThread(QtCore.QThread):
def __init__(self):
super(ArduinoThread, self).__init__()
# Hier wird der Timer mit der 100ms-Refresh-Rate erstellt
self.updatetimer = QtCore.QTimer()
self.updatetimer.setInterval(100)
self.updatetimer.setSingleShot(False)
self.updatetimer.start()
def run(self):
port_name = 'COM3'
baud_rate = '9600'
self.fuellhoehe = 0
self.BA = 0
self.energie_l = 0
self.energie_s = 0
self.energie_h = 0
self.counter_input_flush = 0
# Verbindung zum Arduino herstellen
try:
self.arduino = serial.Serial(port=port_name,baudrate=baud_rate,timeout = None)
time.sleep(1)
self.arduino.flushInput()
self.updatetimer.timeout.connect(self.read_values)
except SerialException:
print "Arduino-Port konnte nicht geoeffnet werden."
# Hier die Funktion für das zyklische Einlesen der Messwerte, welche anscheinend die Probleme verursacht
def read_values(self):
try:
value_string = self.arduino.readline()
except SerialException:
print ("Verbindung verloren")
value_string = 'ERROR'
print ("Eingelesener String:")
print value_string
#Hier wird der empfangene String einfach ein bisschen weiter verarbeitet um die eigentlichen Werte zu extrahieren
value_dictionary = dict(i.split(':') for i in value_string.split(';'))
value_dictionary['energie_h']=value_dictionary['energie_h'].rstrip('\r\n')
self.fuellhoehe = float(value_dictionary['fuellhoehe'])
self.BA = int(value_dictionary['BA'])
self.energie_l = float(value_dictionary['energie_l'])
self.energie_s = float(value_dictionary['energie_s'])
self.energie_h = float(value_dictionary['energie_h'])
# Hier wird mit dem Signal "update_arduino" die Aktualisierung der GUI im anderen Thread ausgelöst. Dazu werden die neuen Messwerte übergeben
self.emit(QtCore.SIGNAL("update_arduino"),self.fuellhoehe,self.BA,self.energie_l,self.energie_s,self.energie_h)
#Damit man sie auch in der Console sieht
print self.fuellhoehe
print self.BA
print self.energie_l
print self.energie_s
print self.energie_h
# alle 100 Messwerte wird der InputBuffer der seriellen Schnittstelle geleert um einen "offset" zu vermeiden. Evtl. Überflüssig.
self.counter_input_flush += 1
if self.counter_input_flush == 100:
self.arduino.flushInput()
time.sleep(0.02)
self.counter_input_flush = 0
print "*******************FLUSH***********************"
# Eine Sendefunktion, wenn über die GUI die Betriebsart (deshalb "BA") des Arduinos geändert werden soll
def send_BA(self,BA):
self.arduino.writelines(BA)
PS: Leider habe ich den Raspberry Pi zurzeit nicht hier. Gerne versuche ich aber nochmal ein Mini-Programm zu schreiben, in welchem das Problem weiterhin auftritt, wozu aber nicht die ganze zusätzlichen Module und Hardware (PySerial, Stylesheet von der Anwendung, Arduino, etc...) gebraucht werden, sodass ihr das bei Interesse auch testen könnt.
PPS: Ich muss glaube ich nochmal ein bisschen selber an der ganzen Geschichte rumdoktoren. Ein paar Stellschrauben dir mir Einfallen:
1) Multithreading sein lassen und mit einem Thread probieren
2) Das emittieren des Signals "update_arduino" auskommentieren. Zwar aktualisiert dann nicht die GUI, aber ich kann ja in der Console sehen, ob die Messwerte "aktuell" sind
3) Hat zwar nix mit Threading zu tun, aber: evtl anderes Modul zum lesen der seriellen Schnittstelle (PyTTY statt PySerial)
4) Hat zwar nix mit Threading zu tun, aber: evtl gucken ob es nicht schlauer wäre nicht mit "readline" sondern "read" zu arbeiten, da das Zeilenende von Linux und Windows glaube ich unterschiedlich ist (also Carriage return + Linefeed statt nur Linefeed. Müsste ich aber nachschauen)
Ich glaube auch, dass das erstellte Profile mit cProfile nur so mittelgut für Multithread-Anwendungen geeignet ist. Vielleicht muss ich da auch nochmal gucken, ob es nicht eine bessere Lösung gibt.
Gerade habe ich auch diesen Beitrag hier gefunden:
https://groups.google.com/forum/#!msg/p ... jLLbnZpA4J
"avoid threading whenever possible" hieß es dort.
Vielleicht sollte ich diesen Ratschlag wirklich beherzigen. Stellschrauben hier wären z.B. nicht so oft Messwerte einlesen (wenn dies der Engpass ist) oder nicht so oft die GUI zu aktualisieren (wenn dies denn der Engpass ist).
Hoffe auf weitere Inputs! Euch einen schönen Samstag!