From fb3de16ef76ad19e886c86d1ead2db53b020b2fa Mon Sep 17 00:00:00 2001 From: NaruX Date: Thu, 9 Mar 2017 20:26:58 +0100 Subject: [PATCH] Programm up-download eingebaut --- revpipycontrol.e4p | 6 +- revpipycontrol/revpioption.py | 15 +- revpipycontrol/revpiplclist.py | 14 +- revpipycontrol/revpiprogram.py | 345 +++++++++++++++++++++++++++++++ revpipycontrol/revpipycontrol.py | 13 +- 5 files changed, 376 insertions(+), 17 deletions(-) create mode 100644 revpipycontrol/revpiprogram.py diff --git a/revpipycontrol.e4p b/revpipycontrol.e4p index 7e7e314..59d6c76 100644 --- a/revpipycontrol.e4p +++ b/revpipycontrol.e4p @@ -1,7 +1,7 @@ - + en_US @@ -21,12 +21,14 @@ revpipycontrol/revpiplclist.py revpipycontrol/revpilogfile.py revpipycontrol/revpioption.py + revpipycontrol/revpiprogram.py + revpipycontrol/revpipycontrol.py Mercurial @@ -194,7 +196,7 @@ MaxLineLength - 80 + 79 NoFixCodes diff --git a/revpipycontrol/revpioption.py b/revpipycontrol/revpioption.py index ff06213..a175d55 100644 --- a/revpipycontrol/revpioption.py +++ b/revpipycontrol/revpioption.py @@ -13,7 +13,7 @@ class RevPiOption(tkinter.Frame): def __init__(self, master, xmlcli): super().__init__(master) - self.pack() + self.pack(expand=True, fill="both") self.xmlcli = xmlcli @@ -68,9 +68,12 @@ class RevPiOption(tkinter.Frame): lbl = tkinter.Label(prog) lbl["text"] = "Python PLC Programname" lbl.grid(**cpadw) - txt_startpy = tkinter.Entry(prog) - txt_startpy["textvariable"] = self.var_startpy - txt_startpy.grid(**cpadwe) +# txt_startpy = tkinter.Entry(prog) +# txt_startpy["textvariable"] = self.var_startpy +# txt_startpy.grid(**cpadwe) + opt_startpy = tkinter.OptionMenu( + prog, self.var_startpy, *self.xmlcli.get_filelist()) + opt_startpy.grid(**cpadwe) ckb_slave = tkinter.Checkbutton(prog, justify="left") ckb_slave["text"] = "RevPi als PLC-Slave verwenden" ckb_slave["state"] = "disabled" @@ -140,8 +143,8 @@ class RevPiOption(tkinter.Frame): ask = tkmsg.askyesnocancel( "Frage", "Die Einstellungen werden jetzt auf den Revolution Pi " - "gespeichert.\nSollen die neuen Einstellungen sofort in Kraft " - "treten? Dies bedeutet einen Neustart des Dienstes und des ggf. " + "gespeichert. \n\nSollen die neuen Einstellungen sofort in Kraft " + "treten? \nDies bedeutet einen Neustart des Dienstes und des ggf. " "laufenden PLC-Programms!", parent=self.master ) if ask is not None: diff --git a/revpipycontrol/revpiplclist.py b/revpipycontrol/revpiplclist.py index fbab4ec..383bd6c 100644 --- a/revpipycontrol/revpiplclist.py +++ b/revpipycontrol/revpiplclist.py @@ -20,6 +20,7 @@ else: homedir = environ["APPDATA"] savefile = os.path.join(homedir, ".revpipyplc", "connections.dat") + def get_connections(): if os.path.exists(savefile): fh = open(savefile, "rb") @@ -65,7 +66,7 @@ class RevPiPlcList(tkinter.Frame): self.txt_name = tkinter.Entry(self, textvariable=self.var_name) self.txt_name.bind("", self.evt_keypress) self.txt_name.grid( - column=3, row=0, columnspan=3, sticky="n", padx=5, pady=5) + column=3, row=0, columnspan=3, sticky="n", padx=5, pady=5) tkinter.Label(self, text="IP-Adresse").grid( column=2, row=1, sticky="wn", padx=5, pady=5 @@ -87,10 +88,12 @@ class RevPiPlcList(tkinter.Frame): self, text="Neu", command=self.evt_btnnew) self.btn_new.grid(column=2, row=3, sticky="s") self.btn_add = tkinter.Button( - self, text="Übernehmen", command=self.evt_btnadd, state="disabled") + self, text="Übernehmen", command=self.evt_btnadd, + state="disabled") self.btn_add.grid(column=3, row=3, sticky="s") self.btn_remove = tkinter.Button( - self, text="Entfernen", command=self.evt_btnremove, state="disabled") + self, text="Entfernen", command=self.evt_btnremove, + state="disabled") self.btn_remove.grid(column=4, row=3, sticky="s") # Fensterbuttons @@ -119,7 +122,7 @@ class RevPiPlcList(tkinter.Frame): def build_listconn(self): self.list_conn.delete(0, "end") lst_conns = sorted(self._connections.keys(), key=lambda x: x.lower()) - self.list_conn.insert("end",*lst_conns) + self.list_conn.insert("end", *lst_conns) def evt_btnadd(self): # TODO: Daten prüfen @@ -197,7 +200,8 @@ class RevPiPlcList(tkinter.Frame): self.btn_remove["state"] = "disabled" def evt_keypress(self, evt=None): - okvalue = "normal" if (self.var_address.get() != "" + okvalue = "normal" if ( + self.var_address.get() != "" and self.var_name.get() != "" and self.var_port.get() != "" ) else "disabled" diff --git a/revpipycontrol/revpiprogram.py b/revpipycontrol/revpiprogram.py new file mode 100644 index 0000000..40b4bb5 --- /dev/null +++ b/revpipycontrol/revpiprogram.py @@ -0,0 +1,345 @@ +# +# RevPiPyControl +# +# Webpage: https://revpimodio.org/revpipyplc/ +# (c) Sven Sager, License: LGPLv3 +# +# -*- coding: utf-8 -*- +import tarfile +import tkinter +import tkinter.filedialog as tkfd +import tkinter.messagebox as tkmsg +from os import makedirs +from tempfile import mktemp +from xmlrpc.client import Binary + + +class RevPiProgram(tkinter.Frame): + + def __init__(self, master, xmlcli, revpi): + super().__init__(master) +# master.protocol("WM_DELETE_WINDOW", self._checkclose) + self.pack(expand=True, fill="both") + + self.uploaded = False + self.revpi = revpi + self.xmlcli = xmlcli + + # Fenster bauen + self._createwidgets() + + self._evt_optdown() + self._evt_optup() + +# def _checkclose(self): +# if self.uploaded: +# tkmsg.showinfo("Ein PLC Programm wurde hochgeladen. " +# "Bitte die PLC options prüfen ob dort das neue Programm" +# "eingestellt werden muss.") +# self.master.destroy() + + def _createwidgets(self): + self.master.wm_title("RevPi Python PLC Programm") + self.master.wm_resizable(width=False, height=False) + + self.rowconfigure(0, weight=1) + self.columnconfigure(0, weight=1) + + cpad = {"padx": 4, "pady": 2} + cpade = {"padx": 4, "pady": 2, "sticky": "e"} + cpadw = {"padx": 4, "pady": 2, "sticky": "w"} + cpadwe = {"padx": 4, "pady": 2, "sticky": "we"} + + # Gruppe Programm + prog = tkinter.LabelFrame(self) + prog.columnconfigure(0, weight=1) + prog["text"] = "PLC Pythonprogramm" + prog.grid(columnspan=2, pady=2, sticky="we") + + # Variablen vorbereiten + self.var_picdown = tkinter.BooleanVar(prog) + self.var_picup = tkinter.BooleanVar(prog) + self.var_cleanup = tkinter.BooleanVar(prog) + self.var_typedown = tkinter.StringVar(prog) + self.var_typeup = tkinter.StringVar(prog) + + self.lst_typedown = ["Dateien", "Zip Archiv", "TGZ Archiv"] + self.lst_typeup = ["Datei", "Ordner", "Zip Archiv", "TGZ Archiv"] + self.var_typedown.set(self.lst_typedown[0]) + self.var_typeup.set(self.lst_typeup[0]) + + r = 0 + lbl = tkinter.Label(prog) + lbl["text"] = "PLC Programm herunterladen als:" + lbl.grid(column=0, row=r, **cpadw) + opt = tkinter.OptionMenu( + prog, self.var_typedown, *self.lst_typedown, + command=self._evt_optdown) + opt["width"] = 10 + opt.grid(column=1, row=r, **cpad) + + r = 1 + self.ckb_picdown = tkinter.Checkbutton(prog) + self.ckb_picdown["text"] = "inkl. piCtory Konfiguration" + self.ckb_picdown["variable"] = self.var_picdown + self.ckb_picdown.grid(column=0, row=r, **cpadw) + btn = tkinter.Button(prog) + btn["command"] = self.plcdownload + btn["text"] = "Download" + btn.grid(column=1, row=r, **cpad) + + r = 2 + lbl = tkinter.Label(prog) + lbl["text"] = "PLC Programm hochladen als:" + lbl.grid(column=0, row=r, **cpadw) + opt = tkinter.OptionMenu( + prog, self.var_typeup, *self.lst_typeup, + command=self._evt_optup) + opt["width"] = 10 + opt.grid(column=1, row=r, **cpad) + + r = 3 + self.ckb_picup = tkinter.Checkbutton(prog) + self.ckb_picup["text"] = "enthält piCtory Konfiguration" + self.ckb_picup["variable"] = self.var_picup + self.ckb_picup.grid(column=0, row=r, **cpadw) + btn = tkinter.Button(prog) + btn["command"] = self.plcupload + btn["text"] = "Upload" + btn.grid(column=1, row=r, **cpad) + + r = 4 + ckb = tkinter.Checkbutton(prog) + ckb["text"] = "vorher alles im Uploadverzeichnis löschen" + ckb["variable"] = self.var_cleanup + ckb.grid(column=0, row=r, columnspan=2, **cpadw) + + # Gruppe piCtory + picto = tkinter.LabelFrame(self) + picto.columnconfigure(0, weight=1) + picto["text"] = "piCtory Konfiguration" + picto.grid(columnspan=2, pady=2, sticky="we") + + lbl = tkinter.Label(picto) + lbl["text"] = "piCtory Konfiguration herunterladen" + lbl.grid(column=0, row=0, **cpadw) + btn = tkinter.Button(picto) + btn["command"] = self.getpictoryrsc + btn["text"] = "Download" + btn.grid(column=1, row=0, **cpad) + lbl = tkinter.Label(picto) + lbl["text"] = "piCtory Konfiguration hochladen" + lbl.grid(column=0, row=1, **cpadw) + btn = tkinter.Button(picto) + btn["command"] = self.setpictoryrsc + btn["text"] = "Upload" + btn.grid(column=1, row=1, **cpad) + + # Gruppe ProcImg + proc = tkinter.LabelFrame(self) + proc.columnconfigure(0, weight=1) + proc["text"] = "piControl0 Prozessabbild" + proc.grid(columnspan=2, pady=2, sticky="we") + lbl = tkinter.Label(proc) + lbl["text"] = "Prozessabbild herunterladen" + lbl.grid(column=0, row=0, **cpadw) + btn = tkinter.Button(proc) + btn["command"] = self.getprocimg + btn["text"] = "Download" + btn.grid(column=1, row=0, **cpad) + + # Beendenbutton + btn = tkinter.Button(self) + btn["command"] = self.master.destroy + btn["text"] = "Beenden" + btn.grid() + + def _evt_optdown(self, text=""): + if self.lst_typedown.index(self.var_typedown.get()) == 0: + self.var_picdown.set(False) + self.ckb_picdown["state"] = "disable" + else: + self.ckb_picdown["state"] = "normal" + + def _evt_optup(self, text=""): + if self.lst_typeup.index(self.var_typeup.get()) == 0: + self.var_picup.set(False) + self.ckb_picup["state"] = "disable" + else: + self.ckb_picup["state"] = "normal" + + def getpictoryrsc(self): + fh = tkfd.asksaveasfile( + mode="wb", parent=self.master, + confirmoverwrite=True, + title="Speichern als...", + initialfile=self.revpi + ".rsc", + filetypes=(("piCtory Config", "*.rsc"), ("All Files", "*.*")) + ) + if fh is not None: + try: + fh.write(self.xmlcli.get_pictoryrsc().data) + except: + tkmsg.showerror( + parent=self.master, title="Fehler", + message="Datei konnte nicht geladen und gespeichert " + "werden!" + ) + else: + tkmsg.showinfo( + parent=self.master, title="Erfolgreich", + message="Datei erfolgreich vom Revolution Pi geladen " + "und gespeichert.", + ) + finally: + fh.close() + + def getprocimg(self): + fh = tkfd.asksaveasfile( + mode="wb", parent=self.master, + confirmoverwrite=True, + title="Speichern als...", + initialfile=self.revpi + ".img", + filetypes=(("Imagefiles", "*.img"), ("All Files", "*.*")) + ) + if fh is not None: + try: + fh.write(self.xmlcli.get_procimg().data) + except: + tkmsg.showerror( + parent=self.master, title="Fehler", + message="Datei konnte nicht geladen und gespeichert" + "werden!" + ) + else: + tkmsg.showinfo( + parent=self.master, title="Erfolgreich", + message="Datei erfolgreich vom Revolution Pi geladen " + "und gespeichert.", + ) + finally: + fh.close() + + def setpictoryrsc(self): + print("setpictoryrsc") + fh = tkfd.askopenfile( + mode="rb", parent=self.master, + title="piCtory Datei öffnen...", + initialfile=self.revpi + ".rsc", + filetypes=(("piCtory Config", "*.rsc"), ("All Files", "*.*")) + ) + if fh is not None: + # TODO: Test, ob es wirklich piCtory ist + + ask = tkmsg.askyesno( + parent=self.master, title="Frage", + message="Soll nach dem Hochladen ein reset am piControl " + "Treiber durchgeführt werden?", + ) + + ec = self.xmlcli.set_pictoryrsc(Binary(fh.read()), ask) + if ec == 0: + if ask: + tkmsg.showinfo( + parent=self.master, title="Erfolgreich", + message="Die Übertragung der piCtory Konfiguration " + "und der Reset von piControl wurden erfolgreich " + "ausgeführt") + else: + tkmsg.showinfo( + parent=self.master, title="Erfolgreich", + message="Die Übertragung der piCtory Konfiguration " + "wurde erfolgreich ausgeführt") + elif ec < 0: + tkmsg.showerror( + parent=self.master, title="Fehler", + message="Die piCtory Konfiguration konnte auf dem " + "Revolution Pi nicht geschrieben werden.") + elif ec > 0: + tkmsg.showwarning( + parent=self.master, title="Warnung", + message="Die piCtroy Konfiguration wurde erfolgreich " + "gespeichert. \nBeim piControl Reset trat allerdings ein " + "Fehler auf!") + + fh.close() + + def plcdownload(self): + tdown = self.lst_typedown.index(self.var_typedown.get()) + fh = None + dir = "" + + if tdown == 0: + # Ordner + dir = tkfd.askdirectory( + parent=self.master, title="Verzeichnis zum Ablegen", + mustexist=False, initialdir=self.revpi) + + if type(dir) == str and dir != "": + fh = open(mktemp(), "wb") + + elif tdown == 1: + # Zip + fh = tkfd.asksaveasfile( + mode="wb", parent=self.master, + confirmoverwrite=True, + title="Speichern als...", + initialfile=self.revpi + ".zip", + filetypes=(("Zip Archiv", "*.zip"), ("All Files", "*.*")) + ) + + elif tdown == 2: + # TarGz + fh = tkfd.asksaveasfile( + mode="wb", parent=self.master, + confirmoverwrite=True, + title="Speichern als...", + initialfile=self.revpi + ".tar.gz", + filetypes=(("Tar Archiv", "*.tar.gz"), ("All Files", "*.*")) + ) + + if fh is not None: + if tdown == 1: + plcfile = self.xmlcli.plcdownload("zip") + else: + plcfile = self.xmlcli.plcdownload("tar") + + try: + fh.write(plcfile.data) + # Optional entpacken + if tdown == 0: + fh.close() + makedirs(dir, exist_ok=True) + fh_pack = tarfile.open(fh.name) + fh_pack.extractall(dir) + fh_pack.close() + + except: + raise + tkmsg.showerror( + parent=self.master, title="Fehler", + message="Datei konnte nicht geladen und gespeichert " + "werden!" + ) + else: + tkmsg.showinfo( + parent=self.master, title="Erfolgreich", + message="Datei erfolgreich vom Revolution Pi geladen " + "und gespeichert.", + ) + finally: + fh.close() + + print("plcdownload", tdown) + + def plcupload(self): + tup = self.lst_typeup.index(self.var_typeup.get()) + + print("plcupload", tup) + self.uploaded = True + + +if __name__ == "__main__": + root = tkinter.Tk() + myapp = RevPiProgram(root, None, "test") + root.mainloop() diff --git a/revpipycontrol/revpipycontrol.py b/revpipycontrol/revpipycontrol.py index f34c6b2..b85e241 100644 --- a/revpipycontrol/revpipycontrol.py +++ b/revpipycontrol/revpipycontrol.py @@ -11,6 +11,7 @@ import revpicheckclient import revpilogfile import revpioption import revpiplclist +import revpiprogram import socket import tkinter import tkinter.messagebox as tkmsg @@ -28,6 +29,7 @@ class RevPiPyControl(tkinter.Frame): self.cli = None self.dict_conn = revpiplclist.get_connections() + self.revpiname = None # Fenster aufbauen self._createwidgets() @@ -108,10 +110,11 @@ class RevPiPyControl(tkinter.Frame): def _fillconnbar(self): 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)) + self.mconn.add_command( + label=con, command=partial(self._opt_conn, con) + ) def _opt_conn(self, text): - print(text) sp = ServerProxy( "http://{}:{}".format( self.dict_conn[text][0], int(self.dict_conn[text][1]) @@ -124,6 +127,7 @@ class RevPiPyControl(tkinter.Frame): self.servererror() else: self.cli = sp + self.revpiname = text self.var_conn.set("{} - {}:{}".format( text, self.dict_conn[text][0], int(self.dict_conn[text][1]) )) @@ -149,14 +153,15 @@ class RevPiPyControl(tkinter.Frame): pass def plcoptions(self): - # TODO: Optionsfenster win = tkinter.Toplevel(self) revpioption.RevPiOption(win, self.cli) self.wait_window(win) def plcprogram(self): # TODO: Programfenster - pass + win = tkinter.Toplevel(self) + revpiprogram.RevPiProgram(win, self.cli, self.revpiname) + self.wait_window(win) def plcstart(self): self.cli.plcstart()