Meine For Schleife in While Schleife umwandeln

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
pythonstudyy
User
Beiträge: 4
Registriert: Samstag 28. August 2021, 09:34

Hallo Allerseits

Nach sehr langem ausprobieren habe ich mich entschieden, mich nun in diesem Forum anzumelden und Hilfe anzufordern, wenn möglich.

Ich der Schule müssen wir ein Python Software schreiben, welches Werte von einem externen Programm (OPC UA) einliest und diese in eine Boris.txt Datei ausgibt.
Ich habe die Aufgabe erfolgreich mit einer For- Schleife abgeschlossen. Nun wollte ich das gleiche mit einer While- Schleife ausführen. Ich habe es jedoch trotz allen Vorlagen und Erklärungen im WWW nicht hinbekommen. Langsam habe ich eine Blockade und deswegen wollte ich hier Fragen, ob jemand von euch Experten mir helfen könnte.

Die For Schleife sieht wie folgt aus:

Code: Alles auswählen

        werte = open("data/BORIS.txt", "w+")
        import numpy as np
        time = 0.000
        ergebniss = []

        for i in range(1000):
            werte.write("{:.3f}".format(time)), werte.write("\t")
            werte.write("{:.3f}".format(Signal1[i])), werte.write("\t")
            werte.write("{:.3f}".format(Signal2[i])), werte.write("\t")
            werte.write("{:.3f}".format(Signal3[i])), werte.write("\t")
            werte.write("{:.3f}".format(Signal4[i])), werte.write("\t")
            werte.write("{:.3f}".format(Signal5[i])), werte.write("\r")
            time = time + 0.005
            ergebniss.append(time)
Die Werte werden in insgesamt sechs Spalten aufgelistet und es Zählt von 0 bis 1000. Auch müssen alle ausgelesenen Werte mit drei Kommastellen ausgegeben werden. Deswegen habe ich die Formatierung drinnen. Vielleicht kann mir jemand auch sagen wie das auch anders funktioniert, da ich es mir dem Round nicht hinbekommen habe, dass mir alle Werte mir drei nachkommastellen ausgegeben werden. Die Tabulatoren zwischen den Spalten und der <CR> am Schluss ist zwingend von der Aufgabenstellung.

Ich Bedanke mir Herzlich für hilfreiche Antworten.

LG Pythonstudyy
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@pythonstudyy,

da du immer 6 Werte in eine Zeile mit Tabulator ("\t") getrennt schreiben möchtest, solltest du statt einer normalen Textdatei eine csv-Datei verwenden. Auch wenn das für "Komma getrennte Werte" steht kann man die Werte auch einfach mit Tabulatoren trennen.

Numpy verwendest du in diesem Code nicht daher kannst du die Zeile zum Import von numpy auch weg lassen. (Importe sollten auch immer in den ersten Zeilen über dem Code stehen, damit man sie leichter erkennt)

Statt die 6 Signalwerte mit 6 Variablen durchzunummerieren solltest du sie in einer Liste anlegen.
In der Art:

Code: Alles auswählen

signals = [0.537, 0.427, 0.572, 0.29, 0.87, 0.478]
Du zeigst aber nicht den Code mit dem die Signale gelesen werden, daher kann ich dir da auch keine weitere Hilfe geben.

Aktuell würdest du 1000 mal die gleichen Signalwerte in die Datei schreiben. Da kann also etwas nicht stimmen....

Ein Beispiel wie man eine Zeile von 6 Zufallswerten in eine Zeile einer csv-Datei speichert:

Code: Alles auswählen

import csv
import random

signals = [round(random.random(), 3) for _ in range(6)]

with open("test.csv", "w", encoding="utf-8") as csv_file:
    writer = csv.writer(csv_file, delimiter="\t")
    writer.writerow(signals)
In der Datei würde die erste Zeile dann zum Beispiel so aussehen:

Code: Alles auswählen

0.499	0.987	0.117	0.798	0.541	0.673
Wie man das genau auf deinen Fall anwenden kann, hängt aber auch davon ab wie die Werte erzeugt bzw. gelesen werden.
Wenn du damit nicht weiter kommst müsstest du den Teil auch mal zeigen.

Übrigens, du kannst das zur Übung auch als while-Schleife machen, das bietet sich in diesem Fall aber nicht an. Es wäre etwas ungewöhnlich aber machbar.
pythonstudyy
User
Beiträge: 4
Registriert: Samstag 28. August 2021, 09:34

@rogerb

Danke erstmals für die sehr schnelle und ausführliche Antwort.
Das mit der csv. Datei macht sinn, jedoch müssen wir vom Auftrag aus eine txt. Datei erzeugen.

Ich habe hier nochmals den gesamten Code. Die Werte kommen von einem Client welche die Daten in ein Logger Schreibt und sobald ich den Logger aktiviere, wird von 0 - 100%. geladen und diese aufgezeichneten Werte muss ich eben in einer Boris.txt Datei ausgeben. Wenn Sie mir sagen, dass eine While Schleife weniger Sinn macht, dann kann ich das auch auslassen. Ich habe lange probiert aber auch keine wirklich Sinnvolle Lösung gefunden. Nun wäre noch Interessant zu wissen, ob ich die Round Function auch in meiner for Schleife für die Text Datei anwenden kann, dass mir die Zahlen in der Textdatei IMMER auf 3 Nachkommastellen angezeigt werden.

Code: Alles auswählen

import sys
sys.path.insert(0, "..")
from opcua import Client
import matplotlib.pyplot as plt

if __name__ == "__main__":

    client = Client("opc.tcp://192.168.43.151:4840")

    try:
        client.connect()
        root = client.get_root_node()
        OpcUA_U_in1 = root.get_child(["0:Objects",
                                      "2:DeviceSet",
                                      "4:CODESYS Control for Raspberry Pi SL",
                                      "3:Resources",
                                      "4:app",
                                      "3:Programs",
                                      "4:MAIN",
                                      "4:OpcUA_U_in1"])

        OpcUA_U_in2 = root.get_child(["0:Objects",
                                      "2:DeviceSet",
                                      "4:CODESYS Control for Raspberry Pi SL",
                                      "3:Resources",
                                      "4:app",
                                      "3:Programs",
                                      "4:MAIN",
                                      "4:OpcUA_U_in2"])

        OpcUA_U_out1 = root.get_child(["0:Objects",
                                       "2:DeviceSet",
                                       "4:CODESYS Control for Raspberry Pi SL",
                                       "3:Resources",
                                       "4:app",
                                       "3:Programs",
                                       "4:MAIN",
                                       "4:OpcUA_U_out1"])

        OpcUA_U_out2 = root.get_child(["0:Objects",
                                       "2:DeviceSet",
                                       "4:CODESYS Control for Raspberry Pi SL",
                                       "3:Resources",
                                       "4:app",
                                       "3:Programs",
                                       "4:MAIN",
                                       "4:OpcUA_U_out2"])

        OpcUA_U_out3 = root.get_child(["0:Objects",
                                       "2:DeviceSet",
                                       "4:CODESYS Control for Raspberry Pi SL",
                                       "3:Resources",
                                       "4:app",
                                       "3:Programs",
                                       "4:MAIN",
                                       "4:OpcUA_U_out3"])

        In1 = OpcUA_U_in1.get_value()
        In2 = OpcUA_U_in2.get_value()
        Out1 = OpcUA_U_out1.get_value()
        Out2 = OpcUA_U_out2.get_value()
        Out3 = OpcUA_U_out3.get_value()

        werte = open("data/BORIS.txt", "w+")
        time = 0.000
        ergebniss = []

        for i in range(1000):
            werte.write("{:.3f}".format(time)), werte.write("\t")
            werte.write("{:.3f}".format(In1[i])), werte.write("\t")
            werte.write("{:.3f}".format(In2[i])), werte.write("\t")
            werte.write("{:.3f}".format(Out1[i])), werte.write("\t")
            werte.write("{:.3f}".format(Out2[i])), werte.write("\t")
            werte.write("{:.3f}".format(Out3[i])), werte.write("\r")
            time = time + 0.005
            ergebniss.append(time)


        plt.axis([0.0, 5.0, -4.0, 8.0])
        plt.plot(ergebniss, In1, label=r'$In1$', color="green", linestyle="-", linewidth=2.5)
        plt.plot(ergebniss, In2, label=r'$In2$', color="purple", linestyle="-", linewidth=2.5)
        plt.plot(ergebniss, Out1, label=r'$Out1$', color="cyan", linestyle="-", linewidth=2.5)
        plt.plot(ergebniss, Out2, label=r'$Out2$', color="orange",linestyle="-", linewidth=2.5)
        plt.plot(ergebniss, Out3, label=r'$Out3$', color="red",linestyle="-", linewidth=2.5)
        plt.title('SPS_Signals', fontsize=26, color='blue'), plt.legend(loc='best')
        plt.grid(False), plt.savefig('data/SPS_Signal.png'), plt.show()

    finally:client.disconnect()

0.000 1.936 3.218 -1.282 5.153 0.527
0.005 2.024 3.192 -1.168 5.216 0.527
0.010 2.024 3.192 -1.168 5.216 0.527
0.015 2.065 3.213 -1.148 5.278 0.527
0.020 2.065 3.213 -1.148 5.278 0.527
0.025 2.101 3.236 -1.135 5.336 0.527
0.030 2.101 3.236 -1.135 5.336 0.527
0.035 2.202 3.201 -0.998 5.403 0.527
0.040 2.202 3.201 -0.998 5.403 0.527
0.045 2.225 3.183 -0.958 5.408 0.527
0.050 2.225 3.183 -0.958 5.408 0.527
0.055 2.350 3.186 -0.836 5.536 0.527
0.060 2.350 3.186 -0.836 5.536 0.527
0.065 2.415 3.199 -0.784 5.614 0.527
0.070 2.415 3.199 -0.784 5.614 0.527
0.075 2.480 3.192 -0.712 5.672 0.527
0.080 2.480 3.192 -0.712 5.672 0.527
0.085 2.521 3.207 -0.686 5.727 0.527
0.090 2.521 3.207 -0.686 5.727 0.527
0.095 2.601 3.206 -0.605 5.806 0.527
0.100 2.601 3.206 -0.605 5.806 0.527
0.105 2.656 3.205 -0.549 5.861 0.527
0.110 2.656 3.205 -0.549 5.861 0.527
0.115 2.699 3.229 -0.530 5.928 0.527
0.120 2.699 3.229 -0.530 5.928 0.527
0.125 2.795 3.207 -0.412 6.001 0.527
0.130 2.795 3.207 -0.412 6.001 0.527
0.135 2.842 3.207 -0.364 6.049 0.527
0.140 2.842 3.207 -0.364 6.049 0.527
0.145 2.912 3.219 -0.306 6.131 0.527
0.150 2.912 3.219 -0.306 6.131 0.527
0.155 2.990 3.202 -0.213 6.192 0.527
0.160 2.990 3.202 -0.213 6.192 0.527
0.165 3.074 3.202 -0.127 6.276 0.527
0.170 3.074 3.202 -0.127 6.276 0.527
0.175 3.104 3.186 -0.082 6.291 0.527
0.180 3.104 3.186 -0.082 6.291 0.527
0.185 3.186 3.213 -0.027 6.399 0.527
0.190 3.186 3.213 -0.027 6.399 0.527
0.195 3.229 3.196 0.033 6.425 0.527
0.200 3.229 3.196 0.033 6.425 0.527
0.205 3.101 3.211 -0.110 6.312 0.527
0.210 3.101 3.211 -0.110 6.312 0.527


So sieht die Textdatei aus und ganz Links werden dann Zeilen von 0 bis 1000 Werte bis nach unten mit eben diesen 6 Werten pro Zeile ausgegeben.

Danke für Ihre Hilfe!!!
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Ich habe noch ein paar Änderungen gemacht.
Man könnte noch einiges mehr machen, aber das ist erstmal ein erster Schritt.

Runden würde ich die Werte nicht, denn dadurch geht ja Genauigkeit verloren.
Jetzt hast du die Anforderung mit drei Nachkommastellen ja schon erfüllt.

Code: Alles auswählen

import sys

sys.path.insert(0, "..")
from opcua import Client
import matplotlib.pyplot as plt


def main():
    # Mit der with Anweisung werden connect() und disconnect() automatisch ausgeführt
    with Client("opc.tcp://192.168.43.151:4840") as client:  # hier wird automatisch client.connect() aus geführt
        root = client.get_root_node()
        opcua_u_in1 = root.get_child(
            [
                "0:Objects",
                "2:DeviceSet",
                "4:CODESYS Control for Raspberry Pi SL",
                "3:Resources",
                "4:app",
                "3:Programs",
                "4:MAIN",
                "4:OpcUA_U_in1",
            ]
        )

        opcua_u_in2 = root.get_child(
            [
                "0:Objects",
                "2:DeviceSet",
                "4:CODESYS Control for Raspberry Pi SL",
                "3:Resources",
                "4:app",
                "3:Programs",
                "4:MAIN",
                "4:OpcUA_U_in2",
            ]
        )

        opcua_u_out1 = root.get_child(
            [
                "0:Objects",
                "2:DeviceSet",
                "4:CODESYS Control for Raspberry Pi SL",
                "3:Resources",
                "4:app",
                "3:Programs",
                "4:MAIN",
                "4:OpcUA_U_out1",
            ]
        )

        opcua_u_out2 = root.get_child(
            [
                "0:Objects",
                "2:DeviceSet",
                "4:CODESYS Control for Raspberry Pi SL",
                "3:Resources",
                "4:app",
                "3:Programs",
                "4:MAIN",
                "4:OpcUA_U_out2",
            ]
        )

        opcua_u_out3 = root.get_child(
            [
                "0:Objects",
                "2:DeviceSet",
                "4:CODESYS Control for Raspberry Pi SL",
                "3:Resources",
                "4:app",
                "3:Programs",
                "4:MAIN",
                "4:OpcUA_U_out3",
            ]
        )

        in1 = opcua_u_in1.get_value()
        in2 = opcua_u_in2.get_value()
        out1 = opcua_u_out1.get_value()
        out2 = opcua_u_out2.get_value()
        out3 = opcua_u_out3.get_value()

        # hier wird automatisch client.disconnect() aus geführt

    num_values = 1000
    time_values = [i / 200 for i in range(num_values)]  # statt die Zeitwerte in der Schleife zu generieren werden sie vorher in einer Liste abgelegt
    with open("data/BORIS.txt", "w+") as text_file:  # Auch text Dateien sollte man in einem with-Ausdruck öffnen
        for i in range(num_values):
            text_file.write(f"{time_values[i]:.3f}\t")  # <---- Tabulatoren
            text_file.write(f"{in1[i]:.3f}\t")
            text_file.write(f"{in2[i]:.3f}\t")
            text_file.write(f"{out1[i]:.3f}\t")
            text_file.write(f"{out2[i]:.3f}\t")
            text_file.write(f"{out3[i]:.3f}\n")  # < Zeilenumbruch

    # hier wird die Datei am ende des with-Ausdrucks automatisch geschlossen

    plt.axis([0.0, 5.0, -4.0, 8.0])
    plt.plot(time_values, in1, label=r"$In1$", color="green", linestyle="-", linewidth=2.5)
    plt.plot(time_values, in2, label=r"$In2$", color="purple", linestyle="-", linewidth=2.5)
    plt.plot(time_values, out1, label=r"$Out1$", color="cyan", linestyle="-", linewidth=2.5)
    plt.plot(time_values, out2, label=r"$Out2$", color="orange", linestyle="-", linewidth=2.5)
    plt.plot(time_values, out3, label=r"$Out3$", color="red", linestyle="-", linewidth=2.5)
    plt.title("SPS_Signals", fontsize=26, color="blue"), plt.legend(loc="best")
    plt.grid(False), plt.savefig("data/SPS_Signal.png"), plt.show()


if __name__ == "__main__":
    # statt den gesamtent code hier zu haben wird er in eine main() Funktion ausgelagert.
    # Auch die main() Funktion könnte man noch in Teilfunktionen gliedern
    # Es gibt auch eine Menge an Wiederholungen. Die sollte man möglichst in Unterfunktionen auslagern
    main()  
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

@pythonstudyy: Du hast aber eine csv-Datei, auch wenn Du behauptest, dass das eine Text-Datei ist.
Statt 5mal den selben Code zu kopieren, benutzt man schleifen.
Der Dateimodus "w+" ist eigentlich nie sinnvoll.
sys.path sollte man nicht verändern, sondern das Programm gleich mit dem richtigen Pfad aufrufen.
Alles gehört in Funktionen.
Einlesen, Schreiben und Plotten, dafür ist eine Funktion aber zu lang.
Statt mit indes auf viele Listen zuzugreifen, benutzt man `zip`.
Ist es wirklich Absicht, dass als Zeilentrennzeichen ein einzelnes "\r” benutzt wird?
Warum erzeugst Du Tuple aus zwei ints, die Du gar nicht verwendest? Man schreibt eine Anweisung pro Zeile und trennt nicht mehrere per Komma.

Code: Alles auswählen

import csv
from itertools import count
from opcua import Client
import matplotlib.pyplot as plt


def get_opc_child(root, channel):
    return root.get_child([
        "0:Objects",
        "2:DeviceSet",
        "4:CODESYS Control for Raspberry Pi SL",
        "3:Resources",
        "4:app",
        "3:Programs",
        "4:MAIN",
        f"4:OpcUA_U_{channel}"])


def read_channels(client):
    try:
        client.connect()
        root = client.get_root_node()
        return [
            get_opc_child(root, channel).get_value()
            for channel in ["in1", "in2", "out1", "out2", "out3"]
        ]
    finally:
        client.disconnect()


def write_values(values):
    with open("data/BORIS.txt", "w", encoding="utf8") as file:
        times = (0.005 * t for t in count())
        writer = csv.writer(file, delimiter="\t", lineterminator="\r")
        for row in zip(times, *values):
            writer.writerow(map("{.3f}".format, row))


def plot_values(values):
    plt.axis([0.0, 5.0, -4.0, 8.0])
    times = [0.005 * t for t in range(len(values[0]))]
    for numbers, label, color in zip(values,
        ["In1", "In2", "Out1", "Out2", "Out3"],
        ["green", "purple", "cyan", "orange", "red"]):
        plt.plot(times, numbers, label=label, color=color, linestyle="-", linewidth=2.5)
    plt.title('SPS_Signals', fontsize=26, color='blue')
    plt.legend(loc='best')
    plt.grid(False)
    plt.savefig('data/SPS_Signal.png')
    plt.show()

def main():
    client = Client("opc.tcp://192.168.43.151:4840")
    values = read_channels(client)
    write_values(value)
    plot_values(values)

if __name__ == "__main__":
    main()
pythonstudyy
User
Beiträge: 4
Registriert: Samstag 28. August 2021, 09:34

@Sirius3

Ach ja, es ist wirklich Absicht und auch vom Auftrag gegeben, dass alle Werte in der Zeile jeweils mit einem Tabulator getrennt sind und am Schluss der jeweiligen Zeile ein einzelner "\r” benutzt wird. Zuerst habe ich es auch mit "\n” umgesetzt, aber dies wäre gemäss diesem Auftrag falsch. Der Dozent wollte sehen, dass wenn man die .txt Datei mit Notepat++ öffnet dies so umgesetzt wurde. Danke für die Frage!
Benutzeravatar
DeaD_EyE
User
Beiträge: 1242
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Die Funktion open ist ziemlich praktisch, wenn man das Konzept mit newline verstanden hat.
Es findet eine Übersetzung in beide Richtungen statt. Wenn newline auf dem System z.B. \r\n ist,
wird es im Textmodus als \n ausgeben. Schreibt man hingegen im Textmodus in die Datei ein \n, wird stattdessen \r\n geschrieben.

Unabhängig vom OS kann man newline in der open Funktion übergeben werden. Gültig sind: \n, \r\n, \r
https://docs.python.org/3/library/functions.html#open


Hier noch eine kleine Übersicht
Windows: CRLF
UNIX: LF
MAC (bis Version 9): CR
MAC OS X: LF

CR = "\r"
LF = "\n"


Wenn dann csv noch dazwischen ist und man gezwungen wird Windows zu nutzen, sollte man newline="" setzen und stattdessen dem csv.writer mitteilen, was der lineterminator ist.

Code: Alles auswählen

import csv


fd = open("test.csv", "wt", encoding="latin1", newline="")
writer = csv.writer(fd, lineterminator="\r")
writer.writerow(["a","b","c"])
fd.close()

# bytes lesen
print(open("test.csv", "rb").read())
b'a,b,c\r'
Unabhängig davon was der Lehrer sagt, möchte man eigentlich das Zeilenende haben, dass vom Betriebssystem vorgegeben wird und unter Windows ist es CRLF und nicht CR.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Keine Ahnung wie sich ein MAC (bis Version 9) bei so einer Textdatei verhält.
Anscheinend möchte der Dozent die Textdateien der Studenten auf seinem MAC (bis Version 9) öffnen können, ohne dafür ein Pythonscript zu verwenden?
Benutzeravatar
__blackjack__
User
Beiträge: 14065
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich denke eher der will das man lernt sich an eine Spezifikation zu halten und hat dafür Bedingungen gewählt die man in der Regel auf modernen Systemen nicht automatisch bekommt oder leicht zufällig richtig hin bekommt, sondern für die man auf allen Systemen tatsächlich schauen muss wie man da explizit für sorgt, dass die Spezifikation am Ende richtig umgesetzt wird.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten