1
0
mirror of https://github.com/naruxde/revpipycontrol.git synced 2025-12-29 11:23:13 +01:00

Sicherheitswarnung vor dem Output schreiben integriert

Hauptfenster alles aufr?umen vor destroy()
Wertepr?fung bei RevPiCheckClient int() Werten
Bearbeitungsfunktion bei RevPiCheckClient eingebaut
Hilfemen? mit Webbrowser aufruf eingebaut
This commit is contained in:
2017-06-30 13:50:39 +02:00
parent a60431e456
commit 1fec478e3f
14 changed files with 718 additions and 265 deletions

View File

@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: RevPiPyControl 0.4.0\n"
"POT-Creation-Date: 2017-06-29 20:10+0200\n"
"PO-Revision-Date: 2017-06-29 20:10+0200\n"
"POT-Creation-Date: 2017-06-30 13:50+0200\n"
"PO-Revision-Date: 2017-06-30 13:50+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: de\n"
@@ -15,34 +15,72 @@ msgstr ""
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-SearchPath-0: .\n"
#: revpicheckclient.py:143
#: revpicheckclient.py:103 revpicheckclient.py:395 revpioption.py:288
#: revpiplclist.py:198 revpiprogram.py:306 revpiprogram.py:337
#: revpiprogram.py:400 revpiprogram.py:433 revpiprogram.py:515
#: revpiprogram.py:587 revpiprogram.py:618 revpiprogram.py:631
#: revpiprogram.py:686 revpiprogram.py:706 revpiprogram.py:714
#: revpipycontrol.py:342
msgid "Error"
msgstr "Fehler"
#: revpicheckclient.py:104
msgid ""
"Given value for Output '{}' is not valid! \n"
"Reset to '{}'"
msgstr ""
"Angegebener Wert bei Output '{}' ist nicht gültig! \n"
"Setze auf '{}' zurück"
#: revpicheckclient.py:202
msgid "Devices of RevPi"
msgstr "Devices vom RevPi"
#: revpicheckclient.py:175
#: revpicheckclient.py:234
msgid "Control"
msgstr "Kontrolle"
#: revpicheckclient.py:179
#: revpicheckclient.py:238
msgid "Read all IOs"
msgstr "IOs aktualisieren"
#: revpicheckclient.py:184
#: revpicheckclient.py:243
msgid "Read just Inputs"
msgstr "Inputs lesen"
#: revpicheckclient.py:189
#: revpicheckclient.py:248
msgid "Write Outputs"
msgstr "Outputs schreiben"
#: revpicheckclient.py:195
#: revpicheckclient.py:254
msgid "Autorefresh values"
msgstr "Aktualisiere automatisch"
#: revpicheckclient.py:201
#: revpicheckclient.py:262
msgid "Write values to RevPi"
msgstr "Schreibe Werte auf RevPi"
#: revpicheckclient.py:276 revpiprogram.py:407 revpipycontrol.py:228
#: revpipycontrol.py:267 revpipycontrol.py:285 revpipycontrol.py:303
msgid "Warning"
msgstr "Warnung"
#: revpicheckclient.py:277
msgid ""
"You want to set outputs on the RevPi! Note that these are set "
"IMMEDIATELY!!! \n"
"If another control program is running on the RevPi, it could interfere and "
"reset the outputs."
msgstr ""
"Sie wollen Outputs auf dem RevPi setzen! Beachten Sie, dass diese SOFORT "
"gesetzt werden!!! \n"
"Wenn auf dem RevPi ein anderes Programm zur Steuerung läuft, könnte dies "
"gestört werden und die Ausgänge wieder zurücksetzen."
#: revpicheckclient.py:392
msgid "Error set value of device '{}' Output '{}': {} \n"
msgstr "Fehler beim Setzen auf Device '{}' bei Output '{}': {}\n"
#: revpilogfile.py:37
msgid "RevPi Python PLC Logs"
msgstr "RevPi Python PLC Logdaten"
@@ -184,14 +222,6 @@ msgstr "Information"
msgid "Settings saved"
msgstr "Einstellungen gespeichert"
#: revpioption.py:288 revpiplclist.py:198 revpiprogram.py:306
#: revpiprogram.py:337 revpiprogram.py:400 revpiprogram.py:433
#: revpiprogram.py:515 revpiprogram.py:587 revpiprogram.py:618
#: revpiprogram.py:631 revpiprogram.py:686 revpiprogram.py:706
#: revpiprogram.py:714 revpipycontrol.py:325
msgid "Error"
msgstr "Fehler"
#: revpioption.py:289
msgid "The settings could not be saved. This can happen if values are wrong!"
msgstr ""
@@ -345,7 +375,7 @@ msgstr "piControlReset ausführen"
msgid "execute"
msgstr "ausführen"
#: revpiprogram.py:216 revpipycontrol.py:95
#: revpiprogram.py:216 revpipycontrol.py:107
msgid "Exit"
msgstr "Beenden"
@@ -412,11 +442,6 @@ msgstr ""
"Die piCtory Konfiguration konnte auf dem Revolution Pi nicht geschrieben "
"werden."
#: revpiprogram.py:407 revpipycontrol.py:211 revpipycontrol.py:250
#: revpipycontrol.py:268 revpipycontrol.py:286
msgid "Warning"
msgstr "Warnung"
#: revpiprogram.py:408
msgid ""
"The piCtroy configuration has been saved successfully. \n"
@@ -486,67 +511,67 @@ msgstr "Der Revoluton Pi konnte Teile der Übertragung nicht verarbeiten."
msgid "Errors occurred during transmission"
msgstr "Bei der Übertragung traten Fehler auf"
#: revpipycontrol.py:93
#: revpipycontrol.py:105
msgid "Connections..."
msgstr "Verbindungen..."
#: revpipycontrol.py:96
#: revpipycontrol.py:108
msgid "Main"
msgstr "Datei"
#: revpipycontrol.py:104
#: revpipycontrol.py:116
msgid "Visit website..."
msgstr "Webseite besuchen..."
#: revpipycontrol.py:106
#: revpipycontrol.py:118
msgid "Info..."
msgstr "Info..."
#: revpipycontrol.py:108
#: revpipycontrol.py:119
msgid "Help"
msgstr "Hilfe"
#: revpipycontrol.py:116
#: revpipycontrol.py:127
msgid "PLC start"
msgstr "PLC Start"
#: revpipycontrol.py:121
#: revpipycontrol.py:132
msgid "PLC stop"
msgstr "PLC Stopp"
#: revpipycontrol.py:126
#: revpipycontrol.py:137
msgid "PLC restart"
msgstr "PLC Neustart"
#: revpipycontrol.py:131
#: revpipycontrol.py:142
msgid "PLC logs"
msgstr "PLC Logs"
#: revpipycontrol.py:142
#: revpipycontrol.py:153
msgid "PLC watch mode"
msgstr "PLC watch Modus"
#: revpipycontrol.py:159
#: revpipycontrol.py:170
msgid "PLC log..."
msgstr "PLC Log..."
#: revpipycontrol.py:161
#: revpipycontrol.py:172
msgid "PLC options..."
msgstr "PLC Optionen..."
#: revpipycontrol.py:163
#: revpipycontrol.py:174
msgid "PLC program..."
msgstr "PLC Programm..."
#: revpipycontrol.py:167
#: revpipycontrol.py:178
msgid "Disconnect"
msgstr "Trennen"
#: revpipycontrol.py:172
#: revpipycontrol.py:183
msgid "Connect"
msgstr "Verbinden"
#: revpipycontrol.py:212
#: revpipycontrol.py:229
msgid ""
"The watch mode ist not supported in version {} of RevPiPyLoad on your RevPi! "
"You need at least version 0.4.0."
@@ -554,7 +579,7 @@ msgstr ""
"Der Watch-Modus wird von der RevPiPyLoad Version {} auf dem RevPi nicht "
"unterstützt! Sie benötigen mindestens Version 0.4.0."
#: revpipycontrol.py:251
#: revpipycontrol.py:268
msgid ""
"This version of Logviewer ist not supported in version {} of RevPiPyLoad on "
"your RevPi! You need at least version 0.4.1."
@@ -562,7 +587,7 @@ msgstr ""
"Diese Version vom Logbetrachter wird in der RevPiPyLoad Version {} auf Ihrem "
"RevPi nicht unterstützt! Sie benötigen mindestens Version 0.4.1."
#: revpipycontrol.py:269 revpipycontrol.py:287
#: revpipycontrol.py:286 revpipycontrol.py:304
msgid ""
"XML-RPC access mode in the RevPiPyLoad configuration is to small to access "
"this dialog"
@@ -570,6 +595,10 @@ msgstr ""
"Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch genug eingestellt, um "
"diesen Dialog zu verwenden!"
#: revpipycontrol.py:326
#: revpipycontrol.py:343
msgid "Can not reach server!"
msgstr "Server ist nicht erreichbar!"
#~ msgid "Given value for Output '{}' is not valid! Reset to '{}'"
#~ msgstr ""
#~ "Angegebener Wert bei Output '{}' ist nicht gültig! Setze auf '{}' zurück"

View File

@@ -9,8 +9,9 @@
import pickle
import tkinter
from threading import Lock
import tkinter.messagebox as tkmsg
from mytools import gettrans
from threading import Lock
from xmlrpc.client import ServerProxy, MultiCall
# Übersetzung laden
@@ -27,12 +28,15 @@ class RevPiCheckClient(tkinter.Frame):
self.xmlmode = xmlmode
self.cli = xmlcli
self.cli.psstart()
self.lst_devices = self.cli.ps_devices()
self.dict_devices = {v[0]: v[1] for v in 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.dict_wins = {}
self.__checkwrite = True
self.__lockedvar = None
self.__oldvalue = None
self.autorw = tkinter.BooleanVar()
self.dowrite = tkinter.BooleanVar()
@@ -43,27 +47,76 @@ class RevPiCheckClient(tkinter.Frame):
# Aktuelle Werte einlesen
self.refreshvalues()
def __chval(self, device, io):
if self.dowrite.get():
def __chval(self, device, io, event=None):
u"""Schreibt neuen Output Wert auf den RevPi."""
if self.dowrite.get() and self._warnwrite():
with self.lk:
self.cli.ps_setvalue(device, io[0], io[5].get())
self.validatereturn(
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()
self.__lockedvar = None
def __hidewin(self, win, event=None):
u"""Verbergt übergebenes Fenster.
@param win: Fenster zum verbergen
@param event: Tkinter Event"""
win.withdraw()
def __saveoldvalue(self, event, tkvar):
u"""Speichert bei Keypress aktuellen Wert für wiederherstellung."""
if self.__lockedvar is None:
self.__lockedvar = tkvar
try:
self.__oldvalue = tkvar.get()
except Exception:
pass
def __showwin(self, win):
u"""Zeigt oder verbergt übergebenes Fenster.
@param win: Fenster zum anzeigen/verbergen"""
if win.winfo_viewable():
win.withdraw()
else:
win.deiconify()
def _createiogroup(self, device, frame, iotype):
"""Erstellt IO-Gruppen."""
def __spinboxkey(self, device, io, event=None):
u"""Prüft die Eingabe auf plausibilität.
@param event: tkinter Event
@param io: IO Liste mit tkinter Variable"""
# io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
try:
newvalue = io[5].get()
# Wertebereich prüfen
if newvalue < 0 or newvalue > 255 * io[1]:
raise ValueError("too big")
self.__chval(device, io)
except Exception:
io[5].set(self.__oldvalue)
tkmsg.showerror(
_("Error"),
_("Given value for Output '{}' is not valid! \nReset to ""'{}'"
"").format(self.dict_devices[device], self.__oldvalue),
parent=self.dict_wins[device]
)
# Focus zurücksetzen
event.widget.focus_set()
def _createiogroup(self, device, frame, iotype):
u"""Erstellt IO-Gruppen.
@param device: Deviceposition
@param frame: tkinter Frame
@iotype: 'inp' oder 'out' als str()
"""
# IO-Typen festlegen
if iotype == "inp":
lst_io = self.dict_inps[device]
@@ -120,10 +173,16 @@ class RevPiCheckClient(tkinter.Frame):
check.grid(column=1, row=rowcount)
else:
var = tkinter.IntVar()
# FIXME: Mehrere Bytes möglich
txt = tkinter.Spinbox(s_frame, to=255 * io[1])
txt.bind(
"<Key>",
lambda event, tkvar=var: self.__saveoldvalue(event, tkvar)
)
txt.bind(
"<FocusOut>",
lambda event, device=device, io=io:
self.__spinboxkey(device, io, event)
)
txt["command"] = \
lambda device=device, io=io: self.__chval(device, io)
txt["state"] = "disabled" if iotype == "inp" else "normal"
@@ -131,7 +190,7 @@ class RevPiCheckClient(tkinter.Frame):
txt["textvariable"] = var
txt.grid(column=1, row=rowcount)
# Steuerelementvariable in IO übernehmen
# Steuerelementvariable in IO übernehmen (mutabel)
io.append(var)
rowcount += 1
@@ -143,31 +202,31 @@ class RevPiCheckClient(tkinter.Frame):
devgrp["text"] = _("Devices of RevPi")
devgrp.pack(fill="y", side="left")
for dev in self.lst_devices:
for dev in self.dict_devices:
win = tkinter.Toplevel(self)
win.wm_title(dev[1])
win.wm_title(self.dict_devices[dev])
win.protocol(
"WM_DELETE_WINDOW",
lambda win=win: self.__hidewin(win)
)
win.resizable(False, True)
win.withdraw()
self.lst_wins.append(win)
self.dict_wins[dev] = win
# Devicegruppe erstellen
group = tkinter.LabelFrame(win)
group["text"] = dev[1]
group["text"] = self.dict_devices[dev]
group.pack(side="left", fill="both", expand=True)
for iotype in ["inp", "out"]:
frame = tkinter.Frame(group)
frame.pack(side="left", fill="both", expand=True)
self._createiogroup(dev[0], frame, iotype)
self._createiogroup(dev, frame, iotype)
# Button erstellen
btn = tkinter.Button(devgrp)
btn["command"] = lambda win=win: self.__showwin(win)
btn["text"] = dev[1]
btn["text"] = self.dict_devices[dev]
btn.pack(fill="x", padx=10, pady=5)
# Steuerungsfunktionen
@@ -196,17 +255,38 @@ class RevPiCheckClient(tkinter.Frame):
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 RevPi")
check["variable"] = self.dowrite
check.pack(anchor="w")
self.chk_dowrite = tkinter.Checkbutton(cntgrp)
self.chk_dowrite["command"] = self.togglewrite
self.chk_dowrite["state"] = "normal" if self.xmlmode >= 3 \
and self.autorw.get() else "disabled"
self.chk_dowrite["text"] = _("Write values to RevPi")
self.chk_dowrite["variable"] = self.dowrite
self.chk_dowrite.pack(anchor="w")
def _onfrmconf(self, canvas):
u"""Erstellt Fenster in einem Canvas.
@param canvas: Canvas in dem Objekte erstellt werden sollen"""
canvas.configure(scrollregion=canvas.bbox("all"))
def _warnwrite(self):
u"""Warnung für Benutzer über Schreibfunktion einmal fragen.
@returns: True, wenn Warnung einmal mit OK bestätigt wurde"""
if self.__checkwrite:
self.__checkwrite = not tkmsg.askokcancel(
_("Warning"),
_("You want to set outputs on the RevPi! Note that these are "
"set IMMEDIATELY!!! \nIf another control program is "
"running on the RevPi, it could interfere and reset the "
"outputs."),
icon=tkmsg.WARNING,
parent=self.master
)
return not self.__checkwrite
def _workvalues(self, io_dicts=None, writeout=False):
"""Alle Werte der Inputs und Outputs abrufen."""
u"""Alle Werte der Inputs und Outputs abrufen.
@param io_dicts: Arbeit nur für dieses Dict()
@param writeout: Änderungen auf RevPi schreiben"""
# Abfragelisten vorbereiten
if io_dicts is None:
@@ -220,13 +300,17 @@ class RevPiCheckClient(tkinter.Frame):
if writeout:
xmlmc = MultiCall(self.cli)
for dev in self.lst_devices:
for dev in self.dict_devices:
# io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
# IO Typ verarbeiten
for iotype in io_dicts:
# ios verarbeiten
for io in iotype[dev[0]]:
for io in iotype[dev]:
# Gesperrte Variable überspringen
if io[5] == self.__lockedvar:
continue
# Bytes umwandeln
int_byte = int.from_bytes(
@@ -236,55 +320,81 @@ class RevPiCheckClient(tkinter.Frame):
# Bit-IO
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())
xmlmc.ps_setvalue(dev, 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())
xmlmc.ps_setvalue(dev, io[0], io[5].get())
else:
io[5].set(int_byte)
# Werte per Multicall schreiben
if writeout:
with self.lk:
xmlmc()
self.validatereturn(xmlmc())
if self.autorw.get():
self.master.after(200, self._workvalues)
def hideallwindows(self):
for win in self.lst_wins:
win.withdraw()
u"""Versteckt alle Fenster."""
for win in self.dict_wins:
self.dict_wins[win].withdraw()
def readvalues(self):
u"""Ruft nur Input Werte von RevPi ab und aktualisiert Fenster."""
if not self.autorw.get():
self._workvalues([self.dict_inps])
def refreshvalues(self):
u"""Ruft alle IO Werte von RevPi ab und aktualisiert Fenster."""
if not self.autorw.get():
self._workvalues()
def toggleauto(self):
u"""Schaltet zwischen Autorefresh um und aktualisiert Widgets."""
stateval = "disabled" if self.autorw.get() else "normal"
self.btn_refresh["state"] = stateval
self.btn_read["state"] = stateval
self.btn_write["state"] = stateval
self.chk_dowrite["state"] = "normal" if self.xmlmode >= 3 \
and self.autorw.get() else "disabled"
if self.autorw.get():
self._workvalues()
else:
self.dowrite.set(False)
def togglewrite(self):
u"""Schaltet zwischen DoWrite um und aktiviert Schreibfunktion."""
if self._warnwrite():
self.refreshvalues()
else:
self.dowrite.set(False)
def validatereturn(self, returnlist):
u"""Überprüft die Rückgaben der setvalue Funktion.
@param returnlist: list() der xml Rückgabe"""
if type(returnlist[0]) != list:
returnlist = [returnlist]
str_errmsg = ""
for lst_result in returnlist:
# [device, io, status, msg]
if not lst_result[2]:
# Fehlermeldungen erstellen
devicename = self.dict_devices[lst_result[0]]
str_errmsg += _(
"Error set value of device '{}' Output '{}': {} \n"
).format(devicename, lst_result[1], lst_result[3])
if str_errmsg != "":
tkmsg.showerror(_("Error"), str_errmsg)
def writevalues(self):
if not self.autorw.get():
u"""Schreibt geänderte Outputs auf den RevPi."""
if self._warnwrite() and not self.autorw.get():
self._workvalues([self.dict_outs], True)
# Testdrive
if __name__ == "__main__":
cli = ServerProxy("http://192.168.50.35:55123")
cli.psstart()
tk = tkinter.Tk()
RevPiCheckClient(tk, cli, 3)
tk.mainloop()

View File

@@ -646,7 +646,7 @@ class RevPiProgram(tkinter.Frame):
if fname == rscfile:
continue
# TODO: Fehlerabfang bei Dateilesen
# FIXME: Fehlerabfang bei Dateilesen
with open(fname, "rb") as fh:
# Dateinamen ermitteln
@@ -719,9 +719,3 @@ class RevPiProgram(tkinter.Frame):
# Temp-Dir aufräumen
if dirtmp is not None:
rmtree(dirtmp)
if __name__ == "__main__":
root = tkinter.Tk()
myapp = RevPiProgram(root, None, "test")
root.mainloop()

View File

@@ -1,13 +1,14 @@
#!/usr/bin/python3
#
# RevPiPyControl
# Version: 0.4.1
# Version: see global var pycontrolverion
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
import revpicheckclient
import revpiinfo
import revpilogfile
import revpioption
import revpiplclist
@@ -15,6 +16,7 @@ import revpiprogram
import socket
import tkinter
import tkinter.messagebox as tkmsg
import webbrowser
from functools import partial
from mytools import addroot, gettrans
from xmlrpc.client import ServerProxy
@@ -22,6 +24,8 @@ from xmlrpc.client import ServerProxy
# Übersetzung laden
_ = gettrans()
pycontrolversion = "0.4.1"
class RevPiPyControl(tkinter.Frame):
@@ -29,6 +33,7 @@ class RevPiPyControl(tkinter.Frame):
u"""Init RevPiPyControl-Class.
@param master: tkinter master"""
super().__init__(master)
self.master.protocol("WM_DELETE_WINDOW", self._closeapp)
self.pack(fill="both", expand=True)
self.cli = None
@@ -74,8 +79,15 @@ class RevPiPyControl(tkinter.Frame):
self.tkprogram.destroy()
if self.debugframe is not None:
self.debugframe.destroy()
self.cli.psstop()
self.debugframe = None
def _closeapp(self, event=None):
u"""Räumt auf und beendet Programm.
@param event: tkinter Event"""
self._closeall()
self.master.destroy()
def _createwidgets(self):
u"""Erstellt den Fensterinhalt."""
# Hauptfenster
@@ -104,7 +116,6 @@ class RevPiPyControl(tkinter.Frame):
label=_("Visit website..."), command=self.visitwebsite)
menu1.add_separator()
menu1.add_command(label=_("Info..."), command=self.infowindow)
# TODO: Menü einbauen
self.mbar.add_cascade(label=_("Help"), menu=menu1)
self.var_conn = tkinter.StringVar(self)
@@ -199,8 +210,14 @@ class RevPiPyControl(tkinter.Frame):
self.mbar.entryconfig("PLC", state="normal")
def infowindow(self):
# TODO: Infofenster aufrufen
pass
u"""Öffnet das Fenster für die Verbindungen."""
win = tkinter.Toplevel(self)
revpiinfo.RevPiInfo(win)
win.focus_set()
win.grab_set()
self.wait_window(win)
self.dict_conn = revpiplclist.get_connections()
self._fillconnbar()
def plcdebug(self):
u"""Baut den Debugframe und packt ihn."""
@@ -362,8 +379,9 @@ class RevPiPyControl(tkinter.Frame):
self.master.after(1000, self.tmr_plcrunning)
def visitwebsite(self):
# TODO: Webseite besuchen
pass
u"""Öffnet auf dem System einen Webbrowser zur Projektseite."""
webbrowser.open("https://revpimodio.org")
if __name__ == "__main__":
root = tkinter.Tk()