Einfache GUI für Python

Rund um die Software von Revolution Pi
matt.s
Posts: 71
Joined: 06 Sep 2017, 11:46

Einfache GUI für Python

Post by matt.s »

Hallo,

ich möchte ein kleines Projekt entwickeln, welches eine Analogspannung von einem AIO-Modul einlesen soll, diese Spannung mit mehreren Parametern verrechnet und das Resultat wieder als Analogspannung ausgibt. Die Parameter sollen dabei zur Laufzeit des Programms über eine GUI veränderbar sein. Idealerweise sollte die GUI im Browser laufen, so dass man keinen Monitor am RevPi selbst braucht. Zur Not könnte man sich auch eine Lösung mit realVNC vorstellen, das Projekt ist nur ein In-House Testsystem und kein Verkaufsprodukt. Mir ist bewusst, dass ich das mit PROCON-WEB machen könnte, aber ich würde für so ein einfaches Projekt gerne auf freie Software setzen.

Meine erste Idee wäre, die Verbindung zum AIO-Modul über das RevPiModIO2-Paket zu machen und die Berechnung in in Python. Wie könnte man in diesem Fall möglichst einfach eine GUI erzeugen und mit dem Skript verknüpfen? Hat hier schon einmal jemand mit Pyjamas gearbeitet?

NODE-RED scheint mir dafür generell auch geeignet zu sein, aber ich habe noch nichts von dem allgemeinen RevPi node gesehen der hier erwähnt wird. Das Projekt von weberg scheint nur DIO zu unterstützen, kein AIO. Gibt es zu dem Thema noch etwas, das ich übersehen habe?

Fällt jemand noch eine andere Methode ein über die man schnell zum Ziel kommt?

Vielen Dank schon einmal für euren Input!
Schönen Gruß,
matt.s
matt.s
Posts: 71
Joined: 06 Sep 2017, 11:46

Re: Einfache GUI für Python

Post by matt.s »

PS: Der neu berechnete Analogwert sollte möglichst nur mit der Verzögerung der PiBridge-Zykluszeit ausgegeben werden. Hätte die Verwendung von node-red vs python vs C++ einen merklichen Einfluss auf die Verzögerung des Ausgangswertes bei Änderung des Analogeingangwertes oder dominiert sowieso die PiBridge-Zykluszeit? Das System verwendet neben dem AIO nur ein DIO-Modul, sind Zykluszeiten von 5 ms damit am Core 3 machbar?

Mein AIO-Modul ist leider noch unterwegs, sonst würde ich es einfach selbst ausprobieren :)
Schönen Gruß,
matt.s
User avatar
volker
Posts: 1046
Joined: 09 Nov 2016, 15:41

Re: Einfache GUI für Python

Post by volker »

Node-Red ist doch eine super Idee! Du brauchst keinen speziellen Node für den Zugriff auf das PA. Da Du ja in Python erst mal alles berechnest, kannst Du dann mit kleiner Zykluszeit die Ergebnisse wieder ins PA schreiben (nutze einfach ein virtuelles Modul für die Ergebnisse). Die Ergebnisse greifst Du dann mit Node-Red angemessen langsam zyklisch ab (Für eine Webserverbasierte Lösung reicht alle 300 ms locker, denn schneller wird Dein Browser wahrscheinlich gar nicht reagieren). Dafür nimmst DU den command node und führst einfach piTest damit aus, um die Ergebnisse aus dem PA zu lesen.

Deine 5 ms für eine GUI sind absolut sinnlos, denn Du kannst mit Denem Auge nur ca. 20 ms auflösen und lesen kannst Du keine Werte, die schneller als mit 200 ms kommen. Die Server-Browser Strecke macht bei diesen Geschwindigkeiten auch eher Probleme.

zur PiBridge: Ein DIO (ohne Counter oder Encoder) plus ein AIO werden auf ca. 8 bis 10 ms Zykluszeit kommen. Mit Node-Red wirst Du aber bei diesen Geschwindigkeiten sicher nicht glücklich, denn Node-Red kommt mit einem ziemlich langsamen Interpreter daher. Aber wie gesagt, für eine GUI sind alle Zeiten unter 100 ms eh wenig sinnvoll.
matt.s
Posts: 71
Joined: 06 Sep 2017, 11:46

Re: Einfache GUI für Python

Post by matt.s »

Hallo Volker,

danke für dein Feedback, das klingt nach einem vielversprechenden Ansatz. Die GUI muss natürlich nicht mit 5 ms aktualisiert werden, die wird nur benutzt um die Parameter einzustellen.

Python macht eine Rechnung wie z.B. Aout = a + b * Ain. Was mit einem möglichst kurzen Delay laufen soll ist diese Rechnung, d.h. Aout reagiert möglichst schnell auf Änderungen von Ain. Das Ändern der Parameter a und b über die GUI darf ruhig ein paar 100 ms brauchen.

Ich werde mir mal die virtuellen Devices anschauen und wie ich diese nutzen kann, um die Parameter zwischen Pythonskript und NODE-RED auszutauschen.
Schönen Gruß,
matt.s
User avatar
volker
Posts: 1046
Joined: 09 Nov 2016, 15:41

Re: Einfache GUI für Python

Post by volker »

Mit dem virtuellen Device bekommst Du zum Beispiel 32 Byte Inputs und 32 Byte Outputs im Prozessabbild reserviert. Alternativ kannst Du 16 Words Input und/oder 16 Words Output wählen. Entsprechend werden 2 x 32 oder 2 x 16 Prozessvariablen erzeugt. Deine Software kann diese Prozessvariablen (oder die Bytes im PA mit entsprechendem Offset) nun frei wie Variablen verwenden.
Wenn Du sehr schnell auf analoge Inputs reagieren willst, dann empfehle ich Dir, direkt mit piControl zu kommunizieren und die Werte z.B. so abzugreifen (Ain1 ist der Offset für den 16 Bit Wert des Analogeingangs, Aout1 entsprechend der Output 1 des AOI Moduls und Vin1 der Offset vom ersten Input Word eines virtuellen Moduls - ich habe hier die Zykluszeit auf >10 ms eingestellt, weil bei einem AIO-Modul das ganze nicht viel schneller gehen wird).

Code: Select all

import time
import struct

f=open("/dev/piControl0","wb+",0)
Ain1=11
Aout1=31
Vin1=100
Vin2=Vin1+2

while True:

    f.seek(Ain1)
    AnaInp1=f.read(2)
    AIValue = AnaInp1[0] + 256 * AnaInp1[1]  # oder mit struct auch:    AIValue = struct.unpack('h',AnaInp1)
    
    f.seek(Vin1)
    VirtInp1=f.read(2)
    a = VirtInp1[0] + 256 * VirtInp1[1]    # oder mit struct auch:    a = struct.unpack('h',VirtInp1)
    
    f.seek(Vin2)
    VirtInp2=f.read(2)
    b = VirtInp2[0] + 256 * VirtInp2[1]    # oder mit struct auch:    b = struct.unpack('h',VirtInp2)
    
    AOValue = a + b * AIValue	# oder mit struct auch: AOValue = a[0] + b[0] * AIValue[0]
    f.seek(Aout1)
    AnaOut1 =  struct.pack('h', AOValue )
    f.write(AnaOut1)

    time.sleep(0.01)
Unser RevPi Motto: Don't just claim it - make it!
User avatar
RevPiModIO
KUNBUS
Posts: 335
Joined: 20 Jan 2017, 08:44
Contact:

Re: Einfache GUI für Python

Post by RevPiModIO »

Hi Matt!

Mit RevPiModIO2 könnte man das evtl. so machen - In zwei Teilen mit virtuellem Device:
Screenshot_20180726_154335.png
Screenshot_20180726_154335.png (91.45 KiB) Viewed 17327 times
Steuerungsprogramm auf dem Revolution Pi für RevPiPyLoad:

Code: Select all

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# (c) Sven Sager, License: GPLv3
#
u"""Kleines Programm für Berechnung von AIO Werten.

In piCtory ist der erste Eingang des AIO mit dem Namen InputValue_1 bezeichnet
und auf 0 - 10 V konfiguriert. Der erste Ausgang auf dem AIO hat den Namen
OutputValue_1 und ist ebenfalls auf 0 - 10 V konfiguriert.

Der Berechnungswert wird über ein virtuelles Device in die Steuerung übertragen
und gibt ein "value_ok" Bit zurück. True, wenn der Wert für den Ausgang OK ist,
False, wenn er zu groß oder klein ist.

"""
import revpimodio2

# RevPiModIO intsantiieren
rpi = revpimodio2.RevPiModIO(autorefresh=True)
rpi.handlesignalend()

# Vier Eingangsbytes vom virtuellen Device zusammenfassen
# f = float 4 Bytes (Input_1 - Input_4)
rpi.io.Input_1.replace_io("rechnung", "f")

# Ein Ausgangsbyte in Bits aufbrechen
rpi.io.Output_1.replace_io("value_ok", "?", bit=0)


def zyklus(ct):
    u"""Zyklusfunktion für Berechnungen."""

    # Aktuellen Wert von AIO holen
    int_input = rpi.io.InputValue_1.value

    # Floatwert vom virtuellen Device holen
    flt_rechner = rpi.io.rechnung.value

    # Berechnen
    int_calc = round(int_input * flt_rechner)

    # Neuen Wert prüfen
    if 0 <= int_calc <= 10000:
        # Gültigkeit des Werts übernehmen
        rpi.io.value_ok.value = True

        # Neuen Wert in AIO schreiben
        rpi.io.OutputValue_1.value = int_calc

    else:
        rpi.io.value_ok.value = False


print("gehe in loop")
rpi.cycleloop(zyklus)
print("programm ende")
Dazu ggf. ein kleines Tkinter-Programm, welches über RevPiPyLoad und dem darin steckenden Prozessabbildserver mit dem virtuellen Device kommuniziert.

Code: Select all

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# (c) Sven Sager, License: GPLv3
#
u"""Kleines Netzwerkprogramm für Analogrechner.

Der Berechnungswert wird über ein virtuelles Device in die Steuerung übertragen
und wertet "value_ok" Bit aus. True, wenn der Wert für den Ausgang OK ist,
False, wenn er zu groß oder klein ist.

"""
import revpimodio2
import tkinter
import tkinter.messagebox as tkmsg


class Analogrechner(tkinter.Frame):
    
    u"""Baut kleines Fenster für Wertübertragung"""
    
    def __init__(self, master=None):
        u"""Init Analogrechner-Class."""
        super().__init__(master)
        self.pack()

        # RevPiNetIODriver für virtuelles Device 64 erzeugen
        self.rpi = revpimodio2.RevPiNetIODriver(
            "revpi344.local", 64, autorefresh=True
        )

        # IOs wie in Steuerungsprogramm für virtuelles Device registrieren
        self.rpi.io.Input_1.replace_io("rechnung", "f")
        self.rpi.io.Output_1.replace_io("value_ok", "?", bit=0)
        
        # Fenster bauen
        self._createwidgets()
        
        # Events registrieren
        self.rpi.io.value_ok.reg_event(self.check_ok)
        
        # Eventmanagement starten
        self.rpi.mainloop(blocking=False)

    def _createwidgets(self):
        u"""Fenster aufbauen."""

        # Tkinter Variablen
        self.var_value = tkinter.DoubleVar(self)
        self.var_value.set(self.rpi.io.rechnung.value)
        
        # Steuerelemente
        self.lbl_status = tkinter.Label(self)
        self.lbl_status["text"] = "test"
        self.lbl_status.pack()
        
        txt = tkinter.Entry(self)
        txt["textvariable"] = self.var_value
        txt.pack()
        
        btn = tkinter.Button(self)
        btn["command"] = self.btn_click
        btn["text"] = "Absenden"
        btn.pack()
        
        self.check_ok()

    def btn_click(self):
        try:
            var = float(self.var_value.get())
            print(var)
        except:
            tkmsg.showerror("Error...", "Need a <class 'float'> value")
        else:
            self.rpi.io.rechnung.value = var
        

    def check_ok(self, name=None, value=None):
        if self.rpi.io.value_ok.value:
            self.lbl_status["text"] = "OK"
        else:
            self.lbl_status["text"] = "WRONG"


if __name__ == "__main__":
    # TK-Inter Fenster erstellen
    root = tkinter.Tk()
    myapp = Analogrechner(root)
    root.mainloop()
    myapp.rpi.disconnect()

WICHTIG: Der RevPi muss mit mit RevPiPyLoad ausgestattet sein: https://revpimodio.org/revpipyplc/revpipyload/
UND konfiguriertem PLCSLAVE

Gruß, Sven
python3-RevPiModIO - https://revpimodio.org/ || Der RevPi ist das Beste, was passieren konnte!
matt.s
Posts: 71
Joined: 06 Sep 2017, 11:46

Re: Einfache GUI für Python

Post by matt.s »

Vielen Dank für das Feedback! Werde nächste Woche mal beide Lösungsansätze testen. Vielleicht kann ich ja sogar messen wie groß die Verzögerung bis zur Ausgabe der berechneten Ausgangsspannung am AIO-Modul ist, berichte dann.
Schönen Gruß,
matt.s
User avatar
krambambuli
Posts: 70
Joined: 18 Jun 2018, 09:56

Re: Einfache GUI für Python

Post by krambambuli »

Ich arbeite gerade an einer ähnlichen Sache. Ich habe ein Python Programm mit GUI entwickelt (kivy für die Oberfläche). Es werden analoge Werte gemessen und angezeigt. Gleichzeitig kann der Benutzer Elektrozylinder mittels DO starten ( diese stoppen sobald gewünschte Entfernung erreicht wurde). Ich habe das ganze in einem einfachen Pythonprogramm mittels RevPiModIO2 gelöst. Beispiel:

Code: Select all

        
        	# start drive        
                if distance < float(value):
                    rpi.io.PWM_1.value = int(speed)
                    rpi.io.O_2.value = 0
                    rpi.io.O_3.value = 1

                while distance < float(value):
                    distance = float(rpi.io.Counter_1.value) / 288
		
		# stop drive after reaching distance
                rpi.io.PWM_1.value = 0
                rpi.io.O_2.value = 0
                rpi.io.O_3.value = 0
Die Begriffe virtuelle devices und RevPiPyLoad sind mir noch neu und ich habe ich bisher nichts damit gemacht. Meine Frage daher: Was hat es hier für einen Vorteil Lösungen mit virtual devices oder RevPiLoad zu zu nutzen? Könnte ich damit deutlich bessere Zykluszeiten erreichen? Bei mir ist die Reaktionsgeschwindigkeit bisher okay (nicht super aber auch nicht ungenügend).

Viele Grüße!
User avatar
volker
Posts: 1046
Joined: 09 Nov 2016, 15:41

Re: Einfache GUI für Python

Post by volker »

RevPiPyLoad ist Bestandteil von RevPiModIO, bitte daher in dem zugehörigen Forum (Maker's corner) dort die Frage stellen.
"virtuelle Module" sind in PiCtory wie "reale Module" anzuordnende Module, die aber physikalisch nicht im modularen Aufbau zu finden sind, weil es sich um reine Softwarekonstrukte handelt. In PiCtory werden für solche Module einfach N Bytes im Prozessabbild reserviert, denen man auch Namen geben kann (symbolischer Zugriff über piControl). Alle Programme können dann auf diese Bytes zugreifen, als wären es physikalische IOs. Im Prinzip legst Du also damit eine Art Variablenliste im PA an, die von Programmen genutzt werden kann, die ihre Daten mit dem PA austauschen (z.B. logi.RTS, Procon Web, Node-Red, eigene Pythonprogramme, usw.). So etwas brauchst DU zum Beispiel, wenn Dein Steuerungsprogramm IOs verrechnet und daraus Werte generiert, die Procon Web anzeigen soll. Diese Werte schreibt dann das Steuerungsprogramm ins PA und Procon liest sie aus dem PA.
Manche virtuellen Module kommen auch gleich mit einem Backend-Agenten daher, der die Variablen im PA verwendet (z.B. virtuelles Modul RevPi7, Modbus Master oder Modbus Slave), um sie z.B. über Ethernet oder Rs485 mit anderen Geräten auszutauschen. Du kannst Dir auch eigene virtuelle Geräte definieren, indem Du eine RAP-Datei schreibst und mit einem Bild des Moduls PiCtory für die Device Liste zur Verfügung stellst. Dabei kannst Du alle einfachen Datentypen für die Prozess-Variablen definieren, die in Structured Text definiert sind (außer Strukturen und andere komplexe Formate). Es gibt ein Tutorial dazu, in dem als Beispiel das virtuelle Modul "Wochenzeitschaltuhr" erklärt wird.
Unser RevPi Motto: Don't just claim it - make it!
User avatar
RevPiModIO
KUNBUS
Posts: 335
Joined: 20 Jan 2017, 08:44
Contact:

Re: Einfache GUI für Python

Post by RevPiModIO »

Kurz zu : krambambuli

Die Zykluszeit in RevPiModIO2 ist im default 50 Millisekunden. Du kannst sie direkt nach Instantiierung deines rpi mit rpi.cycletime = 20 mal auf 20 Millisekunden setzen - oder einen anderen Wert. Achte beim Starten auf ggf. Warnungen, die ausgegeben werden, wenn die Laufzeit deines Programms nicht in die Zykluszeit passt.

RevPiPyLoad ist ein Dienst auf dem Revolution Pi, der dein Pythonprogramm, welches du ja schon geschrieben hast, automatisch startet und überwacht. Es bietet dir auch Möglichkeiten über das Netzwerk auf das Prozessabbild zuzugreifen, zu debuggen und dein Programm über ein grafisches Tool "RevPiPyControl" auf den Pi zu kopieren: https://revpimodio.org/revpipyplc/

Virtuelle Devices verwende ich immer, wenn ich z.B. mit einer Visualisierung arbeiten will. Das Steuerungsprogramm auf dem Revolution Pi steuert natürlich meine komplette Anlage und interagiert mit den Daten des virtuellen Devices. Meine Visualisierung wird mit RevPiNetIODriver über das Netzwerk (und RevPiPyLoad) mit dem virtuellen Device verbunden und schreibt in die "Eingänge", die das Steuerungsprogramm auf dem RevPi dann ja auswertet. Warum nicht direkt den Ausgang über das Netzwerk/Visualisierung ansprechen? Wenn z.B. ein Button für den Handbetrieb den Motor an schaltet, die Verbindung abreißt, kann der Motor nicht mehr ausgeschaltet werden :O. Mal abgesehen davon, dass man seine ganze Logik aus dem Hauptprogramm um den Motor zu aktivieren (und alle benötigten Bedingungen) auch in der Visualisierung implementiert werden müsste - macht keinen Sinn.
RevPiPyLoad und RevPiNetIO überwachen die Verbindung durchgehend und können alle Eingänge eines virtuellen Devices auf NULL setzen und die Steuerung auf dem RevPi kann dann SOFORT reagieren und den Handbetrieb deaktivieren. Hier an unserem Revolutionsumbau: https://revpimodio.org/revolutionsumbau ... e-panelpc/

Gruß, Sven
python3-RevPiModIO - https://revpimodio.org/ || Der RevPi ist das Beste, was passieren konnte!
Post Reply