1
0
mirror of https://github.com/naruxde/revpipycontrol.git synced 2025-11-08 15:43:52 +01:00

"Monitorfunktion" als Debug zum Hautpfenster hinzugef?gt

Disconnect-Men?eintrag eingebaut (Funktion hinzugef?gt)
Funktionen f?r "nur lesen" und "nur schreiben" eingebaut
Werte schreiben per MultiCall
UI-Texte angepasst
DocStrings und CodeStyle
This commit is contained in:
2017-06-28 11:55:30 +02:00
parent e9279e4a53
commit 673c338c6b
3 changed files with 179 additions and 76 deletions

View File

@@ -10,7 +10,7 @@
import pickle
import tkinter
from threading import Lock
from xmlrpc.client import ServerProxy
from xmlrpc.client import ServerProxy, MultiCall
class RevPiCheckClient(tkinter.Frame):
@@ -18,17 +18,20 @@ class RevPiCheckClient(tkinter.Frame):
def __init__(self, master, xmlcli, xmlmode=0):
"""Instantiiert MyApp-Klasse."""
super().__init__(master)
self.pack(fill="both", expand=True)
# XML-Daten abrufen
self.xmlmode = xmlmode
self.cli = xmlcli
# FIXME: Fehlerabfang
self.cli.psstart()
self.lst_devices = self.cli.ps_devices()
self.dict_inps = pickle.loads(self.cli.ps_inps().data)
self.dict_outs = pickle.loads(self.cli.ps_outs().data)
self.lk = Lock()
self.lst_wins = []
self.autorw = tkinter.BooleanVar()
self.dowrite = tkinter.BooleanVar()
@@ -37,10 +40,25 @@ class RevPiCheckClient(tkinter.Frame):
self._createwidgets()
# Aktuelle Werte einlesen
self.readvalues()
self.refreshvalues()
def onfrmconf(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
def __chval(self, device, io):
if self.dowrite.get():
with self.lk:
self.cli.ps_setvalue(device, io[0], io[5].get())
# Alles neu einlesen wenn nicht AutoRW aktiv ist
if not self.autorw.get():
self.refreshvalues()
def __hidewin(self, win, event=None):
win.withdraw()
def __showwin(self, win):
if win.winfo_viewable():
win.withdraw()
else:
win.deiconify()
def _createiogroup(self, device, frame, iotype):
"""Erstellt IO-Gruppen."""
@@ -55,7 +73,7 @@ class RevPiCheckClient(tkinter.Frame):
canvas.create_window((4, 4), window=s_frame, anchor="nw")
s_frame.bind(
"<Configure>", lambda event, canvas=canvas: self.onfrmconf(canvas)
"<Configure>", lambda event, canvas=canvas: self._onfrmconf(canvas)
)
rowcount = 0
@@ -76,7 +94,7 @@ class RevPiCheckClient(tkinter.Frame):
var = tkinter.BooleanVar()
check = tkinter.Checkbutton(s_frame)
check["command"] = \
lambda device=device, io=io: self.chval(device, io)
lambda device=device, io=io: self.__chval(device, io)
check["state"] = "disabled" if iotype == "inp" else "normal"
check["text"] = ""
check["variable"] = var
@@ -86,7 +104,7 @@ class RevPiCheckClient(tkinter.Frame):
# FIXME: Mehrere Bytes möglich
txt = tkinter.Spinbox(s_frame, to=255 * io[1])
txt["command"] = \
lambda device=device, io=io: self.chval(device, io)
lambda device=device, io=io: self.__chval(device, io)
txt["state"] = "disabled" if iotype == "inp" else "normal"
txt["width"] = 4
txt["textvariable"] = var
@@ -97,19 +115,8 @@ class RevPiCheckClient(tkinter.Frame):
rowcount += 1
def __hidewin(self, win, event=None):
win.withdraw()
def __showwin(self, win):
if win.winfo_viewable():
win.withdraw()
else:
win.deiconify()
def _createwidgets(self):
"""Erstellt den Fensterinhalt."""
# Hauptfenster
self.master.wm_title("RevPi Onlineview")
devgrp = tkinter.LabelFrame(self)
devgrp["text"] = "Devices of RevPi"
@@ -123,6 +130,7 @@ class RevPiCheckClient(tkinter.Frame):
lambda win=win: self.__hidewin(win)
)
win.withdraw()
self.lst_wins.append(win)
# Devicegruppe erstellen
group = tkinter.LabelFrame(win)
@@ -145,44 +153,56 @@ class RevPiCheckClient(tkinter.Frame):
cntgrp["text"] = "Control"
cntgrp.pack(fill="y", side="right")
self.btn_refresh = tkinter.Button(cntgrp)
self.btn_refresh["text"] = "Alle IOs lesen"
self.btn_refresh["command"] = self.refreshvalues
self.btn_refresh.pack(fill="x")
self.btn_read = tkinter.Button(cntgrp)
self.btn_read["text"] = "LESEN"
self.btn_read["text"] = "Inputs einlesen"
self.btn_read["command"] = self.readvalues
self.btn_read.pack(fill="x")
self.btn_write = tkinter.Button(cntgrp)
self.btn_write["text"] = "Outputs schreiben"
self.btn_write["command"] = self.writevalues
self.btn_write.pack(fill="x")
check = tkinter.Checkbutton(cntgrp)
check["command"] = self.toggleauto
check["text"] = "autorefresh processimage"
check["text"] = "Autorefresh values"
check["variable"] = self.autorw
check.pack(anchor="w")
check = tkinter.Checkbutton(cntgrp)
check["state"] = "disabled" if self.xmlmode < 3 else "normal"
check["text"] = "write values to processimage"
check["text"] = "Write values to RevPi"
check["variable"] = self.dowrite
check.pack(anchor="w")
def chval(self, device, io):
if self.dowrite.get():
with self.lk:
self.cli.ps_setvalue(device, io[0], io[5].get())
def _onfrmconf(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
# Alles neu einlesen wenn nicht AutoRW aktiv ist
if not self.autorw.get():
self.readvalues()
def _readvalues(self):
def _workvalues(self, io_dicts=None, writeout=False):
"""Alle Werte der Inputs und Outputs abrufen."""
# Abfragelisten vorbereiten
if io_dicts is None:
io_dicts = [self.dict_inps, self.dict_outs]
# Werte abrufen
with self.lk:
ba_values = bytearray(self.cli.ps_values().data)
# Multicall zum Schreiben vorbereiten
if writeout:
xmlmc = MultiCall(self.cli)
for dev in self.lst_devices:
# io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
# IO Typ verarbeiten
for iotype in [self.dict_inps, self.dict_outs]:
for iotype in io_dicts:
# ios verarbeiten
for io in iotype[dev[0]]:
@@ -192,22 +212,50 @@ class RevPiCheckClient(tkinter.Frame):
)
if io[4] >= 0:
# Bit-IO
io[5].set(bool(int_byte & 1 << io[4]))
new_val = bool(int_byte & 1 << io[4])
if writeout and new_val != io[5].get():
xmlmc.ps_setvalue(dev[0], io[0], io[5].get())
else:
io[5].set(new_val)
else:
# Byte-IO
if writeout and int_byte != io[5].get():
xmlmc.ps_setvalue(dev[0], io[0], io[5].get())
else:
io[5].set(int_byte)
# Werte per Multicall schreiben
if writeout:
with self.lk:
xmlmc()
if self.autorw.get():
self.master.after(200, self._readvalues)
self.master.after(200, self._workvalues)
def hideallwindows(self):
for win in self.lst_wins:
win.withdraw()
def readvalues(self):
if not self.autorw.get():
self._readvalues()
self._workvalues([self.dict_inps])
def refreshvalues(self):
if not self.autorw.get():
self._workvalues()
def toggleauto(self):
self.btn_read["state"] = "disabled" if self.autorw.get() else "normal"
stateval = "disabled" if self.autorw.get() else "normal"
self.btn_refresh["state"] = stateval
self.btn_read["state"] = stateval
self.btn_write["state"] = stateval
if self.autorw.get():
self._readvalues()
self._workvalues()
def writevalues(self):
if not self.autorw.get():
self._workvalues([self.dict_outs], True)
# Testdrive

View File

@@ -32,6 +32,7 @@ savefile = os.path.join(homedir, ".revpipyplc", "programpath.dat")
class RevPiProgram(tkinter.Frame):
def __init__(self, master, xmlcli, xmlmode, revpi):
u"""Init RevPiProgram-Class."""
if xmlmode < 2:
return None
@@ -223,7 +224,7 @@ class RevPiProgram(tkinter.Frame):
return {}
def _savedefaults(self):
"""Schreibt fuer den Pi die letzen Pfade."""
u"""Schreibt fuer den Pi die letzen Pfade."""
try:
makedirs(os.path.dirname(savefile), exist_ok=True)
dict_all = self._loaddefault(full=True)
@@ -236,7 +237,7 @@ class RevPiProgram(tkinter.Frame):
return True
def create_filelist(self, rootdir):
"""Erstellt eine Dateiliste von einem Verzeichnis.
u"""Erstellt eine Dateiliste von einem Verzeichnis.
@param rootdir: Verzeichnis fuer das eine Liste erstellt werden soll
@returns: Dateiliste"""
filelist = []
@@ -249,7 +250,7 @@ class RevPiProgram(tkinter.Frame):
"""Gibt das rootdir von einem entpackten Verzeichnis zurueck.
Dabei wird geprueft, ob es sich um einen einzelnen Ordner handelt
und ob es eine piCtory Konfiguraiton im rootdir gibt.
und ob es eine piCtory Konfiguration im rootdir gibt.
@param rootdir: Verzeichnis fuer Pruefung
@returns: Abgeaendertes rootdir
@@ -270,6 +271,7 @@ class RevPiProgram(tkinter.Frame):
return (rootdir, None)
def getpictoryrsc(self):
u"""Läd die piCtory Konfiguration herunter."""
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
@@ -300,6 +302,7 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def getprocimg(self):
u"""Läd das aktuelle Prozessabbild herunter."""
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
@@ -330,6 +333,7 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def setpictoryrsc(self, filename=None):
u"""Überträgt die angegebene piCtory-Konfiguration."""
if filename is None:
fh = tkfd.askopenfile(
mode="rb", parent=self.master,
@@ -381,6 +385,7 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def picontrolreset(self):
u"""Fürt ein Reset der piBridge durch."""
ask = tkmsg.askyesno(
parent=self.master, title="Frage...",
message="Soll piControlReset wirklich durchgeführt werden? \n"
@@ -401,6 +406,7 @@ class RevPiProgram(tkinter.Frame):
)
def plcdownload(self):
u"""Läd das aktuelle Projekt herunter."""
tdown = self.lst_typedown.index(self.var_typedown.get())
fh = None
dirselect = ""
@@ -491,6 +497,7 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def plcupload(self):
u"""Lädt das angegebene Projekt auf den RevPi."""
tup = self.lst_typeup.index(self.var_typeup.get())
dirselect = ""
dirtmp = None

View File

@@ -41,6 +41,8 @@ def addroot(filename):
class RevPiPyControl(tkinter.Frame):
def __init__(self, master=None):
u"""Init RevPiPyControl-Class.
@param master: tkinter master"""
super().__init__(master)
self.pack(fill="both", expand=True)
@@ -48,8 +50,12 @@ class RevPiPyControl(tkinter.Frame):
self.dict_conn = revpiplclist.get_connections()
self.errcount = 0
self.revpiname = None
self.xmlfuncs = []
self.xmlmode = 0
# Debugger vorbereiten
self.debugframe = None
# Globale Fenster
self.tkcheckclient = None
self.tklogs = None
@@ -63,14 +69,30 @@ class RevPiPyControl(tkinter.Frame):
self.tmr_plcrunning()
def _btnstate(self):
u"""Setzt den state der Buttons."""
stateval = "disabled" if self.cli is None else "normal"
self.btn_plclogs["state"] = stateval
self.btn_plcstart["state"] = stateval
self.btn_plcstop["state"] = stateval
self.btn_plcrestart["state"] = stateval
self.btn_debug["state"] = stateval
def _closeall(self):
u"""Schließt alle Fenster."""
if self.tkcheckclient is not None:
self.tkcheckclient.destroy()
if self.tklogs is not None:
self.tklogs.master.destroy()
if self.tkoptions is not None:
self.tkoptions.destroy()
if self.tkprogram is not None:
self.tkprogram.destroy()
if self.debugframe is not None:
self.debugframe.destroy()
self.debugframe = None
def _createwidgets(self):
"""Erstellt den Fensterinhalt."""
u"""Erstellt den Fensterinhalt."""
# Hauptfenster
self.master.wm_title("RevPi Python PLC Loader")
self.master.wm_iconphoto(
@@ -122,26 +144,39 @@ class RevPiPyControl(tkinter.Frame):
self.txt_status["textvariable"] = self.var_status
self.txt_status.pack(fill="x")
def _fillmbar(self):
# PLC Menü
self.mplc = tkinter.Menu(self.mbar, tearoff=False)
self.mplc.add_command(label="PLC log...", command=self.plclogs)
self.mplc.add_command(label="PLC monitor...", command=self.plcmonitor)
self.mplc.add_command(label="PLC options...", command=self.plcoptions)
self.mplc.add_command(label="PLC program...", command=self.plcprogram)
self.mbar.add_cascade(label="PLC", menu=self.mplc, state="disabled")
# Connection Menü
self.mconn = tkinter.Menu(self.mbar, tearoff=False)
self.mbar.add_cascade(label="Connect", menu=self.mconn)
self.btn_debug = tkinter.Button(self)
self.btn_debug["text"] = "PLC Debugmodus"
self.btn_debug["command"] = self.plcdebug
self.btn_debug.pack(fill="x")
def _fillconnbar(self):
u"""Generiert Menüeinträge für Verbindungen."""
self.mconn.delete(0, "end")
for con in sorted(self.dict_conn.keys(), key=lambda x: x.lower()):
self.mconn.add_command(
label=con, command=partial(self._opt_conn, con)
)
def _fillmbar(self):
u"""Generiert Menüeinträge."""
# PLC Menü
self.mplc = tkinter.Menu(self.mbar, tearoff=False)
self.mplc.add_command(
label="PLC log...", command=self.plclogs)
self.mplc.add_command(
label="PLC options...", command=self.plcoptions)
self.mplc.add_command(
label="PLC program...", command=self.plcprogram)
self.mplc.add_separator()
self.mplc.add_command(
label="Disconnect", command=self.serverdisconnect)
self.mbar.add_cascade(label="PLC", menu=self.mplc, state="disabled")
# Connection Menü
self.mconn = tkinter.Menu(self.mbar, tearoff=False)
self.mbar.add_cascade(label="Connect", menu=self.mconn)
def _opt_conn(self, text):
socket.setdefaulttimeout(2)
sp = ServerProxy(
@@ -151,6 +186,7 @@ class RevPiPyControl(tkinter.Frame):
)
# Server prüfen
try:
self.xmlfuncs = sp.system.listMethods()
self.xmlmode = sp.xmlmodus()
except:
self.servererror()
@@ -168,17 +204,30 @@ class RevPiPyControl(tkinter.Frame):
))
self.mbar.entryconfig("PLC", state="normal")
def _closeall(self):
if self.tkcheckclient is not None:
self.tkcheckclient.destroy()
if self.tklogs is not None:
self.tklogs.master.destroy()
if self.tkoptions is not None:
self.tkoptions.destroy()
if self.tkprogram is not None:
self.tkprogram.destroy()
def plcdebug(self):
u"""Baut den Debugframe und packt ihn."""
self.btn_debug["state"] = "disabled"
# Debugfenster laden
if self.debugframe is None:
self.debugframe = revpicheckclient.RevPiCheckClient(
self, self.cli, self.xmlmode
)
# Show/Hide wechseln
if self.debugframe.winfo_viewable():
self.debugframe.hideallwindows()
self.debugframe.autorw.set(False)
self.debugframe.toggleauto()
self.debugframe.dowrite.set(False)
self.debugframe.pack_forget()
else:
self.debugframe.pack(fill="y")
self.btn_debug["state"] = "normal"
def plclist(self):
u"""Öffnet das Fenster für die Verbindungen."""
win = tkinter.Toplevel(self)
revpiplclist.RevPiPlcList(win)
win.focus_set()
@@ -188,24 +237,15 @@ class RevPiPyControl(tkinter.Frame):
self._fillconnbar()
def plclogs(self):
u"""Öffnet das Fenster für Logdateien."""
if self.tklogs is None or len(self.tklogs.children) == 0:
win = tkinter.Toplevel(self)
self.tklogs = revpilogfile.RevPiLogfile(win, self.cli)
else:
self.tklogs.focus_set()
def plcmonitor(self):
"""Startet das Monitorfenster."""
if self.tkcheckclient is None or len(self.tkcheckclient.children) == 0:
win = tkinter.Toplevel(self)
self.tkcheckclient = revpicheckclient.RevPiCheckClient(
win, self.cli, self.xmlmode
)
else:
self.tkcheckclient.focus_set()
def plcoptions(self):
"""Startet das Optionsfenster."""
u"""Startet das Optionsfenster."""
if self.xmlmode < 2:
tkmsg.showwarning(
parent=self.master, title="Warnung",
@@ -222,6 +262,7 @@ class RevPiPyControl(tkinter.Frame):
self.xmlmode = self.tkoptions.xmlmode
def plcprogram(self):
u"""Startet das Programmfenster."""
if self.xmlmode < 2:
tkmsg.showwarning(
parent=self.master, title="Warnung",
@@ -237,23 +278,30 @@ class RevPiPyControl(tkinter.Frame):
self.wait_window(win)
def plcstart(self):
u"""Startet das PLC Programm."""
self.cli.plcstart()
def plcstop(self):
u"""Beendet das PLC Programm."""
self.cli.plcstop()
def plcrestart(self):
u"""Startet das PLC Programm neu."""
self.cli.plcstop()
self.cli.plcstart()
def servererror(self):
"""Setzt alles auf NULL."""
def serverdisconnect(self):
u"""Trennt eine bestehende Verbindung."""
socket.setdefaulttimeout(2)
self.cli = None
self._btnstate()
self.mbar.entryconfig("PLC", state="disabled")
self.var_conn.set("")
self._closeall()
def servererror(self):
u"""Setzt alles zurück für neue Verbindungen."""
self.serverdisconnect()
tkmsg.showerror("Fehler", "Server ist nicht erreichbar!")
def tmr_plcrunning(self):