Threading

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
smdarund
User
Beiträge: 1
Registriert: Freitag 5. Februar 2021, 15:14

Hallo zusammen,

ich bin gerade dabei ein Programm zu schreiben, welches einen Frequenzumrichter ansteuert. Nun suche den ganzen Tag schon einen Weg zwei for-Schleifen zu parallelisieren. Also das sie gleichzeitig starten. Bei meinen Recherchen bin ich über Threading und Multiprocessing gestolpert. Ich habe mich jetzt fürs Threading entschieden, aber ich bekomme es, wie gesagt, nicht hin.

Das Problem ist, dass mein Programm die zwei for-Schleifen nicht parallel ausführt. Unter "#PARALLELIZATION OF 'STEPWISE INCREASING VOLTAGE OUTPUT' AND '#MEASUREMENT VOLTAGE & CURRENT'" findet ihr mein Versuch die for-Schleife "#STEPWISE INCREASING VOLTAGE OUTPUT" und "#MEASUREMENT VOLTAGE & CURRENT" zu threaden.

Das ist mein erster in Python bzw. generell geschriebener Code. Also seht es mir nach, falls es Laienhaft geworden ist :D Ich bedanke mich schon mal für eure Hilfe und Tipps! :)

Code: Alles auswählen

import matplotlib.pyplot as plt
import serial
import time
import pandas as pd
import threading

#DEFINITION:
a = 2000      #Spannungssteigerung in V pro Sekunde                                                                   
wertetabelle = []

#CONNECTION UND BASIC SETTINGS
ser=serial.Serial(port="COM9", baudrate=9600, bytesize=8, stopbits=1)        #Port öffnen
ser.write(b"*OUTP OFF\r")                                                    #Ausgang aus
ser.write(b"*SYST:REM\r")                                                    #Remotesteuerung ein
ser.write(b"*OUTP SER\r")                                                    #Bereich seriell
ser.write(b"*FREQ 50.0\r")                                                   #Frequenz auf x stellen
ser.write(b"*VOLT 0.6\r")                                                   #Spannung auf den niedrigsten einzustellenden Wert gestellt
ser.write(b"*OUTP ON\r")  
print("basic settings done\n\r")                                             #Ausgang ein 
time.sleep(5)                                                               #Einschwingvorgang

#STEPWISE INCREASING VOLTAGE OUTPUT
def Spannungssteigerung():    
  for j in range(30):
    time.sleep(1)
    x=(round(float((a+a*j)/127),1))
    print(x)
    output="*VOLT %s\r" %(x)
    ser.write(output.encode())
    return x
    return output
    if x > 250:
        ser.write(b"*OUTP OFF\r") 
        break

#MEASUREMENT VOLTAGE & CURRENT      
def Messwerte():  
   for i in range(300):                                                                                
      time.sleep(0.1)
      ser.write(b"*READ?\r\n")
      messwerte = ser.read(46)
      Spannung = float(messwerte[0:6])
      Strom = float(messwerte[8:14])
      Spannung = Spannung*127/1000       
      wertetabelle.append([Spannung, Strom])
      return i
      return messwerte
      return Spannung
      return Strom
        
wertetabelle1= pd.DataFrame(wertetabelle, columns = ["Spannung [kV]", "Strom[A]"])
  
#PARALLELIZATION OF 'STEPWISE INCREASING VOLTAGE OUTPUT' AND '#MEASUREMENT VOLTAGE & CURRENT'
if __name__ == '__main__':     
    x1 = threading.Thread(target=Spannungssteigerung)
    x1.start()
    x2 = threading.Thread(target=Messwerte)
    x2.start()
    x1.join()
    x2.join()

#CLOSE CONNECTION AND OUTPUT
ser.write(b"*OUTP OFF\r")   #close output                                              
ser.close()     #close port
             
#EVALUATION OF MEASUREDATA                                                
wertetabelle1.to_csv(r'D:\Durchschlagsversuch\Spannungs-undStromverlauf.csv') 
df = pd.read_csv('D:\Durchschlagsversuch\Spannungs-undStromverlauf.csv')
y1=df["Spannung [kV]"].values
y2=df["Strom[A]"].values
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title("Spannungs- und Stromverlauf")
ax.set_xlabel("Zeit [cs]")
ax.set_ylabel("Spannung [kV]",color="blue")
ax.plot(y1,color="blue")
ax2=ax.twinx()
ax2.plot(y2, color = "red")
ax2.set_ylabel("Strom [A]",color="red")
ax.plot(y1)
fig.savefig("D:\Durchschlagsversuch\Spannungs-und Stromverlauf_.png", format='png', dpi=600)
print("ENDE")
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@smdarund: Das willst Du ganz sicher so nicht das zwei Funktionen parallel und unkoordiniert auf die serielle Schnittstelle zugreifen. Vor Threads die inheränt kompliziert sind — Nebenläufigkeit halt, und all die Probleme die man sich damit einhandelt — sollten auch erst einmal Grundlagen wie Funktionen sitzen. *Ein* ``return`` in einer solchen Schleife würde schon keinen Sinn machen, weil man sich dann die Schleife auch gleich sparen kann wenn die grundsätzlich nur *einmal* ausgeführt wird. Aber mehr als ein ``return`` direkt nacheinander beziehungsweise *irgendwelcher* Code nach einem ``return`` ist sinnlos weil der *nie* ausgeführt wird.

Im Zusammenhang mit Threads macht dann auch überhaupt kein ``return`` (mit einem Rückgabewert) Sinn, weil da ja nichts ist was diesen Rückgabewert entgegen nehmen würde.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen, und auch nicht nummeriert werden.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht. UND SCHREI DOCH BITTE IN DEN KOMMENTAREN NICHT SO. Wem der Kommentar zu leise ist, der kann im Editor ja die Lautstärke höher einstellen. 😉

Ich zweifele auch den Sinn an das Nebenläufig zu machen. Es sieht doch so aus als würdest Du von dreissig Einstellungen jeweils 10 Messwerte auslesen wollen. Dafür braucht man nicht nur keine Nebenläufigkeit, das ist sogar unsicher/ungenau das nebenläufig zu machen, denn so ein `sleep()` ist alles andere als genau. Das ist eine Mindestzeit die da geschlagen wird.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist schon eine ziemliche Katastrophe, mit mehreren returns, kaum Funktionen, Verletzung der PEP8 Namenskonventionen, Code mal innerhalb und mal ausserhalb des __main__-Blocks. Da ist viel das sich einfach nur mit Sorgfalt verbessern laesst, ohne das wir bei inhaltlichem sind. Und weil du den Code hier ja auch praesentierst (und ggf. irgendwann mal einem Betreuer) ist das auch nicht nur dein Privatverguegen. Du willst damit ja deinen erfolgreichen Versuch kommunizieren.

Dann sind da noch ein paar Klassiker der Zeitbasierten Programmierung bei: sleep(1) von dem dann angenommen wird, dass es auch wirklich 1 Sekunde waere. Das gleiche mit .1. Sind sie nicht. Zum einen garantiert dir das OS nur, dass es mindestens .1 oder 1 Sekunden sind. Gerne auch mehr. Und dann hast du ja auch noch allen moeglichen anderen Code, der darum herum laeuft. Wenn du dich mit der Stoppuhr an eine Rennbahn stellst, und die fuer 30 Sekunden laufen laesst, um dann 400m zu rennen, dann hast du ja auch keine Rundenzeit von 30 Sekunden, oder? Sondern 30 Sekunden + was auch immer deine eigentliche Rundenzeit war.

Zu deinem eigentlichen Problem: ich sehe ueberhaupt nicht, wozu hier irgendetwas parallelisiert gemacht werden muss. Du hast ein Verhaeltnis von 1 : 10 zwischen Spannungskontrolle und Messwerterhebung. Das kanst du also auch problemlos ineinander verschraenken.

Code: Alles auswählen

import time


LENGTH = 10.0
POWER_FACTOR = 1
MEASURMENT_FACTOR = 10


def set_power(s):
    print("setting power", s)


def measure(at):
    print("measure", at)


class IntervalCheck:

    def __init__(self, start, interval):
        self._start = start
        self._interval = interval
        self._last_step = None

    def __call__(self, now):
        delta = now - self._start
        steps = int(delta / self._interval)
        res = self._last_step is None or steps > self._last_step
        if res:
            self._last_step = steps
        return res


def main():
    start = time.monotonic()

    should_measure = IntervalCheck(start, .1)
    should_control_power = IntervalCheck(start, 1.0)

    while True:
        now = time.monotonic()
        if start + LENGTH < now:
            break
        if should_measure(now):
            measure(now)
        if should_control_power(now):
            set_power(now)

        time.sleep(.01)


if __name__ == '__main__':
    main()
Antworten