diff --git a/doc/aclmanager.html b/doc/aclmanager.html new file mode 100644 index 0000000..b765e24 --- /dev/null +++ b/doc/aclmanager.html @@ -0,0 +1,256 @@ + + +aclmanager + + + +

+aclmanager

+ +

+Global Attributes

+ + +
_
+

+Classes

+ + + + + +
AclManagerHauptfenster des ACL-Managers.
+

+Functions

+ + +
None
+

+ +

AclManager

+

+Hauptfenster des ACL-Managers. +

+

+Derived from

+ttk.Frame +

+Class Attributes

+ + +
acl
acltext
root
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AclManagerInit RevPiOption-Class.
__get_acltextGetter fuer Leveltexte.
__set_acltextSetter fuer Leveltexte.
_ask_deleteLöscht ein Eintrag der Liste.
_changesdonePrüft ob sich die Einstellungen geändert haben.
_checkbackSpringt bei Backspace in vorheriges Feld.
_checkclosePrüft ob Fenster beendet werden soll.
_checkdotPrüft auf .
_clearfieldsLeert die Eingabefelder.
_createwidgetsErstellt Widgets.
_loadfieldsÜbernimmt Listeneintrag in Editfelder.
_refreshaclsLeert die ACL Liste und füllt sie neu.
_saveÜbernimt die Änderungen.
_savefieldsÜbernimmt neuen ACL Eintrag.
_status_editremoveSetzt state der Buttons.
get_aclGibt die Konfigurierten ACL zurück.
+

+Static Methods

+ + +
None
+ +

+AclManager (Constructor)

+AclManager(master, minlevel, maxlevel, acl_str="", readonly=False) +

+Init RevPiOption-Class. +

+
Returns:
+
+None +
+
+

+AclManager.__get_acltext

+__get_acltext() +

+Getter fuer Leveltexte. +

+
Returns:
+
+Leveltexte als +
+
+

+AclManager.__set_acltext

+__set_acltext(value) +

+Setter fuer Leveltexte. +

+
value
+
+Leveltexte als +
+
+

+AclManager._ask_delete

+_ask_delete() +

+Löscht ein Eintrag der Liste. +

+

+AclManager._changesdone

+_changesdone() +

+Prüft ob sich die Einstellungen geändert haben. +

+
Returns:
+
+True, wenn min. eine Einstellung geändert wurde +
+
+

+AclManager._checkback

+_checkback(event, tkvar, pretxt) +

+Springt bei Backspace in vorheriges Feld. +

+
event
+
+TK Event +
tkvar
+
+TK Variable zum prüfen +
nexttxt
+
+Vorheriges IP Feld für Fokus +
+
+

+AclManager._checkclose

+_checkclose(event=None) +

+Prüft ob Fenster beendet werden soll. +

+
event
+
+tkinter-Event +
+
+

+AclManager._checkdot

+_checkdot(event, tkvar, nexttxt) +

+Prüft auf . und geht weiter. +

+
event
+
+TK Event +
tkvar
+
+TK Variable zum prüfen +
nexttxt
+
+Nächstes IP Feld für Fokus +
+
+

+AclManager._clearfields

+_clearfields() +

+Leert die Eingabefelder. +

+

+AclManager._createwidgets

+_createwidgets() +

+Erstellt Widgets. +

+

+AclManager._loadfields

+_loadfields() +

+Übernimmt Listeneintrag in Editfelder. +

+

+AclManager._refreshacls

+_refreshacls() +

+Leert die ACL Liste und füllt sie neu. +

+

+AclManager._save

+_save() +

+Übernimt die Änderungen. +

+

+AclManager._savefields

+_savefields() +

+Übernimmt neuen ACL Eintrag. +

+

+AclManager._status_editremove

+_status_editremove(tkevt) +

+Setzt state der Buttons. +

+

+AclManager.get_acl

+get_acl() +

+Gibt die Konfigurierten ACL zurück. +

+
Returns:
+
+ACL als +
+
+
Up
+
+ \ No newline at end of file diff --git a/doc/index-revpipycontrol.shared.html b/doc/index-revpipycontrol.shared.html new file mode 100644 index 0000000..00f7dd6 --- /dev/null +++ b/doc/index-revpipycontrol.shared.html @@ -0,0 +1,20 @@ + + +revpipycontrol.shared + + + +

+revpipycontrol.shared

+ + + +

+Modules

+ + + + + +
ipaclmanagerVerwaltet IP Adressen und deren ACLs.
+ \ No newline at end of file diff --git a/doc/index.html b/doc/index.html index 621e3bb..1a15819 100644 --- a/doc/index.html +++ b/doc/index.html @@ -8,20 +8,34 @@ Table of contents +

+Packages

+ + + + + +
shared

Modules

- + + + + + + + diff --git a/doc/mytools.html b/doc/mytools.html index c8a2a96..db6bf59 100644 --- a/doc/mytools.html +++ b/doc/mytools.html @@ -6,11 +6,13 @@

mytools

- +

+Tools-Sammlung. +

Global Attributes

mytoolsaclmanager
mytoolsTools-Sammlung.
revpicheckclient
revpiinfo
revpilegacy
revpilogfile
- +
None
savefile_connections
savefile_programpath

Classes

@@ -25,7 +27,7 @@ Functions Hängt root-dir der Anwendung vor Dateinamen. gettrans - +Wertet die Sprache des OS aus und gibt Übersetzung zurück.

@@ -53,7 +55,19 @@ root dir

gettrans

gettrans(proglang=None) - +

+Wertet die Sprache des OS aus und gibt Übersetzung zurück. +

+
proglang
+
+Bestimmte Sprache laden +
+
+
Returns:
+
+gettext Übersetzung für Zuweisung an '_' +
+
Up

\ No newline at end of file diff --git a/doc/revpicheckclient.html b/doc/revpicheckclient.html index 2069514..8fd8d2e 100644 --- a/doc/revpicheckclient.html +++ b/doc/revpicheckclient.html @@ -17,7 +17,7 @@ Classes - +
RevPiCheckClientBaut Fenstererweiterung für 'watch modus'.

@@ -28,7 +28,9 @@ Functions



RevPiCheckClient

- +

+Baut Fenstererweiterung für 'watch modus'. +

Derived from

tkinter.Frame diff --git a/doc/revpiinfo.html b/doc/revpiinfo.html index ebc2484..228cfc2 100644 --- a/doc/revpiinfo.html +++ b/doc/revpiinfo.html @@ -17,7 +17,7 @@ Classes - +
RevPiInfoBaut Frame für Programminformationen.

@@ -28,7 +28,9 @@ Functions



RevPiInfo

- +

+Baut Frame für Programminformationen. +

Derived from

tkinter.Frame diff --git a/doc/revpilegacy.html b/doc/revpilegacy.html new file mode 100644 index 0000000..5cd1125 --- /dev/null +++ b/doc/revpilegacy.html @@ -0,0 +1,166 @@ + + +revpilegacy + + + +

+revpilegacy

+ +

+Global Attributes

+ + +
_
+

+Classes

+ + + + + +
RevPiOptionOptionen für RevPiPyload vor 0.6.0.
+

+Functions

+ + +
None
+

+ +

RevPiOption

+

+Optionen für RevPiPyload vor 0.6.0. +

+

+Derived from

+tkinter.Frame +

+Class Attributes

+ + +
None
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RevPiOptionInit RevPiOption-Class.
_changesdonePrüft ob sich die Einstellungen geändert haben.
_checkclosePrüft ob Fenster beendet werden soll.
_createwidgetsErstellt Widgets.
_loadappdataLäd aktuelle Einstellungen vom RevPi.
_setappdataSpeichert geänderte Einstellungen auf RevPi.
askxmlonFragt Nuter, ob wirklicht abgeschaltet werden soll.
xmlmod2_tailPasst XML-Optionszugriff an.
xmlmod_tailPasst XML-Optionszugriff an.
+

+Static Methods

+ + +
None
+ +

+RevPiOption (Constructor)

+RevPiOption(master, xmlcli) +

+Init RevPiOption-Class. +

+
Returns:
+
+None +
+
+

+RevPiOption._changesdone

+_changesdone() +

+Prüft ob sich die Einstellungen geändert haben. +

+
Returns:
+
+True, wenn min. eine Einstellung geändert wurde +
+
+

+RevPiOption._checkclose

+_checkclose(event=None) +

+Prüft ob Fenster beendet werden soll. +

+
event
+
+tkinter-Event +
+
+

+RevPiOption._createwidgets

+_createwidgets() +

+Erstellt Widgets. +

+

+RevPiOption._loadappdata

+_loadappdata(refresh=False) +

+Läd aktuelle Einstellungen vom RevPi. +

+
refresh
+
+Wenn True, werden Einstellungen heruntergeladen. +
+
+

+RevPiOption._setappdata

+_setappdata() +

+Speichert geänderte Einstellungen auf RevPi. +

+
Returns:
+
+None +
+
+

+RevPiOption.askxmlon

+askxmlon() +

+Fragt Nuter, ob wirklicht abgeschaltet werden soll. +

+

+RevPiOption.xmlmod2_tail

+xmlmod2_tail() +

+Passt XML-Optionszugriff an. +

+

+RevPiOption.xmlmod_tail

+xmlmod_tail() +

+Passt XML-Optionszugriff an. +

+
Up
+
+ \ No newline at end of file diff --git a/doc/revpilogfile.html b/doc/revpilogfile.html index 3e0242b..1bf3eac 100644 --- a/doc/revpilogfile.html +++ b/doc/revpilogfile.html @@ -17,7 +17,7 @@ Classes - +
RevPiLogfileBaut Fenster für Logfiles.

@@ -28,7 +28,9 @@ Functions



RevPiLogfile

- +

+Baut Fenster für Logfiles. +

Derived from

tkinter.Frame diff --git a/doc/revpioption.html b/doc/revpioption.html index 1c42b8d..f6bfe29 100644 --- a/doc/revpioption.html +++ b/doc/revpioption.html @@ -17,7 +17,7 @@ Classes - +
RevPiOptionZeigt Optionen von RevPiPyLoad an.

@@ -28,7 +28,9 @@ Functions



RevPiOption

- +

+Zeigt Optionen von RevPiPyLoad an. +

Derived from

tkinter.Frame @@ -55,6 +57,9 @@ Methods _checkclose Prüft ob Fenster beendet werden soll. +_checkvalues +Prüft alle Werte auf Gültigkeit. + _createwidgets Erstellt Widgets. @@ -67,11 +72,11 @@ Methods askxmlon Fragt Nuter, ob wirklicht abgeschaltet werden soll. -xmlmod2_tail -Passt XML-Optionszugriff an. +btn_slaveacl +Öffnet Fenster für ACL-Verwaltung. -xmlmod_tail -Passt XML-Optionszugriff an. +btn_xmlacl +Öffnet Fenster für ACL-Verwaltung.

@@ -112,6 +117,17 @@ Prüft ob Fenster beendet werden soll.
tkinter-Event
+ +

+RevPiOption._checkvalues

+_checkvalues() +

+Prüft alle Werte auf Gültigkeit. +

+
Returns:
+
+True, wenn alle Werte gültig sind +

RevPiOption._createwidgets

@@ -146,18 +162,18 @@ RevPiOption.askxmlon askxmlon()

Fragt Nuter, ob wirklicht abgeschaltet werden soll. -

+

-RevPiOption.xmlmod2_tail

-xmlmod2_tail() +RevPiOption.btn_slaveacl +btn_slaveacl()

-Passt XML-Optionszugriff an. -

+Öffnet Fenster für ACL-Verwaltung. +

-RevPiOption.xmlmod_tail

-xmlmod_tail() +RevPiOption.btn_xmlacl +btn_xmlacl()

-Passt XML-Optionszugriff an. +Öffnet Fenster für ACL-Verwaltung.

Up

diff --git a/doc/revpioptionlegacy.html b/doc/revpioptionlegacy.html new file mode 100644 index 0000000..732b51c --- /dev/null +++ b/doc/revpioptionlegacy.html @@ -0,0 +1,164 @@ + + +revpioptionlegacy + + + +

+revpioptionlegacy

+ +

+Global Attributes

+ + +
_
+

+Classes

+ + + + + +
RevPiOption
+

+Functions

+ + +
None
+

+ +

RevPiOption

+ +

+Derived from

+tkinter.Frame +

+Class Attributes

+ + +
None
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RevPiOptionInit RevPiOption-Class.
_changesdonePrüft ob sich die Einstellungen geändert haben.
_checkclosePrüft ob Fenster beendet werden soll.
_createwidgetsErstellt Widgets.
_loadappdataLäd aktuelle Einstellungen vom RevPi.
_setappdataSpeichert geänderte Einstellungen auf RevPi.
askxmlonFragt Nuter, ob wirklicht abgeschaltet werden soll.
xmlmod2_tailPasst XML-Optionszugriff an.
xmlmod_tailPasst XML-Optionszugriff an.
+

+Static Methods

+ + +
None
+ +

+RevPiOption (Constructor)

+RevPiOption(master, xmlcli) +

+Init RevPiOption-Class. +

+
Returns:
+
+None +
+
+

+RevPiOption._changesdone

+_changesdone() +

+Prüft ob sich die Einstellungen geändert haben. +

+
Returns:
+
+True, wenn min. eine Einstellung geändert wurde +
+
+

+RevPiOption._checkclose

+_checkclose(event=None) +

+Prüft ob Fenster beendet werden soll. +

+
event
+
+tkinter-Event +
+
+

+RevPiOption._createwidgets

+_createwidgets() +

+Erstellt Widgets. +

+

+RevPiOption._loadappdata

+_loadappdata(refresh=False) +

+Läd aktuelle Einstellungen vom RevPi. +

+
refresh
+
+Wenn True, werden Einstellungen heruntergeladen. +
+
+

+RevPiOption._setappdata

+_setappdata() +

+Speichert geänderte Einstellungen auf RevPi. +

+
Returns:
+
+None +
+
+

+RevPiOption.askxmlon

+askxmlon() +

+Fragt Nuter, ob wirklicht abgeschaltet werden soll. +

+

+RevPiOption.xmlmod2_tail

+xmlmod2_tail() +

+Passt XML-Optionszugriff an. +

+

+RevPiOption.xmlmod_tail

+xmlmod_tail() +

+Passt XML-Optionszugriff an. +

+
Up
+
+ \ No newline at end of file diff --git a/doc/revpiplclist.html b/doc/revpiplclist.html index d2f8095..1669a44 100644 --- a/doc/revpiplclist.html +++ b/doc/revpiplclist.html @@ -10,14 +10,14 @@ revpiplclist

Global Attributes

- +
_
savefile
_

Classes

- +
RevPiPlcListTK Fenster.

@@ -31,7 +31,9 @@ Functions



RevPiPlcList

- +

+TK Fenster. +

Derived from

tkinter.Frame diff --git a/doc/revpiprogram.html b/doc/revpiprogram.html index 4709afd..4a8dc38 100644 --- a/doc/revpiprogram.html +++ b/doc/revpiprogram.html @@ -10,25 +10,33 @@ revpiprogram

Global Attributes

- +
_
savefile
_

Classes

- +
RevPiProgramZeigt Programmfenster an.

Functions

- + + + + + + +
None
_loaddefaultsÜbernimmt für den Pi die letzen Pfade.
_savedefaultsSchreibt fuer den Pi die letzen Pfade.


RevPiProgram

- +

+Zeigt Programmfenster an. +

Derived from

tkinter.Frame @@ -61,12 +69,6 @@ Methods _evt_optup Passt je nach gewählter Option den Status der Widgets an. -_loaddefault -Übernimmt für den Pi die letzen Pfade. - -_savedefaults -Schreibt fuer den Pi die letzen Pfade. - check_replacedir Gibt das rootdir von einem entpackten Verzeichnis zurueck. @@ -137,34 +139,7 @@ RevPiProgram._evt_optup _evt_optup(text="")

Passt je nach gewählter Option den Status der Widgets an. -

-

-RevPiProgram._loaddefault

-_loaddefault(full=False) -

-Übernimmt für den Pi die letzen Pfade. -

-
full
-
-Einstellungen für alle Verbindungen laden -
-
-
Returns:
-
-dict() mit Einstellungen -
-
-

-RevPiProgram._savedefaults

-_savedefaults() -

-Schreibt fuer den Pi die letzen Pfade. -

-
Returns:
-
-True, bei erfolgreicher Verarbeitung -
-
+

RevPiProgram.check_replacedir

check_replacedir(rootdir) @@ -242,5 +217,44 @@ RevPiProgram.setpictoryrsc Überträgt die angegebene piCtory-Konfiguration.

Up
+

+ +

_loaddefaults

+_loaddefaults(revpiname=None) +

+Übernimmt für den Pi die letzen Pfade. +

+
revpiname
+
+Einstellungen nur für RevPi laden +
+
+
Returns:
+
+ mit Einstellungen +
+
+
Up
+

+ +

_savedefaults

+_savedefaults(revpiname, settings) +

+Schreibt fuer den Pi die letzen Pfade. +

+
revpiname
+
+Einstellungen sind für diesen RevPi +
settings
+
+ mit Einstellungen +
+
+
Returns:
+
+True, bei erfolgreicher Verarbeitung +
+
+
Up

\ No newline at end of file diff --git a/doc/revpipycontrol.html b/doc/revpipycontrol.html index f1ea29b..77aa118 100644 --- a/doc/revpipycontrol.html +++ b/doc/revpipycontrol.html @@ -17,7 +17,7 @@ Classes - +
RevPiPyControlBaut Hauptprogramm auf.

@@ -28,7 +28,9 @@ Functions



RevPiPyControl

- +

+Baut Hauptprogramm auf. +

Derived from

tkinter.Frame diff --git a/doc/revpipycontrol.shared.ipaclmanager.html b/doc/revpipycontrol.shared.ipaclmanager.html new file mode 100644 index 0000000..2c5cb69 --- /dev/null +++ b/doc/revpipycontrol.shared.ipaclmanager.html @@ -0,0 +1,239 @@ + + +revpipycontrol.shared.ipaclmanager + + + +

+revpipycontrol.shared.ipaclmanager

+

+Verwaltet IP Adressen und deren ACLs. +

+

+Global Attributes

+ + +
None
+

+Classes

+ + + + + +
IpAclManagerVerwaltung fuer IP Adressen und deren ACL Level.
+

+Functions

+ + + + + +
refullmatchre.fullmatch wegen alter python version aus wheezy nachgebaut.
+

+ +

IpAclManager

+

+Verwaltung fuer IP Adressen und deren ACL Level. +

+

+Derived from

+None +

+Class Attributes

+ + +
acl
filename
regex_acl
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IpAclManagerInit IpAclManager class.
__get_aclGetter fuer den rohen ACL-String.
__get_filenameGetter fuer Dateinamen.
__get_regex_aclGibt formatierten RegEx-String zurueck.
__iter__Gibt einzelne ACLs als aus.
__set_aclUebernimmt neue ACL-Liste fuer die Ausertung der Level.
get_acllevelPrueft IP gegen ACL List und gibt ACL-Wert aus.
loadaclLaed ACL String und gibt erfolg zurueck.
loadaclfileLaed ACL Definitionen aus Datei.
writeaclfileSchreibt ACL Definitionen in Datei.
+

+Static Methods

+ + +
None
+ +

+IpAclManager (Constructor)

+IpAclManager(minlevel, maxlevel, acl=None) +

+Init IpAclManager class. +

+
minlevel
+
+Smallest access level (min. 0) +
maxlevel
+
+Biggest access level (max. 9) +
acl
+
+ACL Liste fuer Berechtigungen als +
+
+

+IpAclManager.__get_acl

+__get_acl() +

+Getter fuer den rohen ACL-String. + return ACLs als +

+

+IpAclManager.__get_filename

+__get_filename() +

+Getter fuer Dateinamen. +

+
Returns:
+
+Filename der ACL +
+
+

+IpAclManager.__get_regex_acl

+__get_regex_acl() +

+Gibt formatierten RegEx-String zurueck. + return RegEx Code als +

+

+IpAclManager.__iter__

+__iter__() +

+Gibt einzelne ACLs als aus. +

+

+IpAclManager.__set_acl

+__set_acl(value) +

+Uebernimmt neue ACL-Liste fuer die Ausertung der Level. +

+
value
+
+Neue ACL-Liste als +
+
+

+IpAclManager.get_acllevel

+get_acllevel(ipaddress) +

+Prueft IP gegen ACL List und gibt ACL-Wert aus. +

+
ipaddress
+
+zum pruefen +
+
+
Returns:
+
+ ACL Wert oder -1 wenn nicht gefunden +
+
+

+IpAclManager.loadacl

+loadacl(str_acl) +

+Laed ACL String und gibt erfolg zurueck. +

+
str_acl
+
+ACL als +
+
+
Returns:
+
+True, wenn erfolgreich uebernommen +
+
+

+IpAclManager.loadaclfile

+loadaclfile(filename) +

+Laed ACL Definitionen aus Datei. +

+
filename
+
+Dateiname fuer Definitionen +
+
+
Returns:
+
+True, wenn Laden erfolgreich war +
+
+

+IpAclManager.writeaclfile

+writeaclfile(filename=None, aclname=None) +

+Schreibt ACL Definitionen in Datei. +

+
filename
+
+Dateiname fuer Definitionen +
+
+
Returns:
+
+True, wenn Schreiben erfolgreich war +
+
+
Up
+

+ +

refullmatch

+refullmatch(regex, string) +

+re.fullmatch wegen alter python version aus wheezy nachgebaut. +

+
regex
+
+RegEx Statement +
string
+
+Zeichenfolge gegen die getestet wird +
+
+
Returns:
+
+True, wenn komplett passt sonst False +
+
+
Up
+
+ \ No newline at end of file diff --git a/revpipycontrol.api b/revpipycontrol.api index 4c807c3..35d2dd1 100644 --- a/revpipycontrol.api +++ b/revpipycontrol.api @@ -1,5 +1,27 @@ +aclmanager.AclManager.__get_acltext?6() +aclmanager.AclManager.__set_acltext?6(value) +aclmanager.AclManager._ask_delete?5() +aclmanager.AclManager._changesdone?5() +aclmanager.AclManager._checkback?5(event, tkvar, pretxt) +aclmanager.AclManager._checkclose?5(event=None) +aclmanager.AclManager._checkdot?5(event, tkvar, nexttxt) +aclmanager.AclManager._clearfields?5() +aclmanager.AclManager._createwidgets?5() +aclmanager.AclManager._loadfields?5() +aclmanager.AclManager._refreshacls?5() +aclmanager.AclManager._save?5() +aclmanager.AclManager._savefields?5() +aclmanager.AclManager._status_editremove?5(tkevt) +aclmanager.AclManager.acl?7 +aclmanager.AclManager.acltext?7 +aclmanager.AclManager.get_acl?4() +aclmanager.AclManager.root?7 +aclmanager.AclManager?1(master, minlevel, maxlevel, acl_str="", readonly=False) +aclmanager._?8 mytools.addroot?4(filename) mytools.gettrans?4(proglang=None) +mytools.savefile_connections?7 +mytools.savefile_programpath?7 revpicheckclient.RevPiCheckClient.__chval?6(device, io, event=None) revpicheckclient.RevPiCheckClient.__hidewin?6(win, event=None) revpicheckclient.RevPiCheckClient.__saveoldvalue?6(event, tkvar) @@ -26,6 +48,16 @@ revpiinfo.RevPiInfo._createwidgets?5(extended=False) revpiinfo.RevPiInfo.visitwebsite?4(event=None) revpiinfo.RevPiInfo?1(master, xmlcli, version) revpiinfo._?8 +revpilegacy.RevPiOption._changesdone?5() +revpilegacy.RevPiOption._checkclose?5(event=None) +revpilegacy.RevPiOption._createwidgets?5() +revpilegacy.RevPiOption._loadappdata?5(refresh=False) +revpilegacy.RevPiOption._setappdata?5() +revpilegacy.RevPiOption.askxmlon?4() +revpilegacy.RevPiOption.xmlmod2_tail?4() +revpilegacy.RevPiOption.xmlmod_tail?4() +revpilegacy.RevPiOption?1(master, xmlcli) +revpilegacy._?8 revpilogfile.RevPiLogfile._checkclose?5(event=None) revpilogfile.RevPiLogfile._createwidgets?5() revpilogfile.RevPiLogfile._load_log?5(textwidget, xmlcall, startposition, full) @@ -37,12 +69,13 @@ revpilogfile.RevPiLogfile?1(master, xmlcli) revpilogfile._?8 revpioption.RevPiOption._changesdone?5() revpioption.RevPiOption._checkclose?5(event=None) +revpioption.RevPiOption._checkvalues?5() revpioption.RevPiOption._createwidgets?5() revpioption.RevPiOption._loadappdata?5(refresh=False) revpioption.RevPiOption._setappdata?5() revpioption.RevPiOption.askxmlon?4() -revpioption.RevPiOption.xmlmod2_tail?4() -revpioption.RevPiOption.xmlmod_tail?4() +revpioption.RevPiOption.btn_slaveacl?4() +revpioption.RevPiOption.btn_xmlacl?4() revpioption.RevPiOption?1(master, xmlcli) revpioption._?8 revpiplclist.RevPiPlcList._checkclose?5(event=None) @@ -60,13 +93,10 @@ revpiplclist.RevPiPlcList.root?7 revpiplclist.RevPiPlcList?1(master) revpiplclist._?8 revpiplclist.get_connections?4() -revpiplclist.savefile?7 revpiprogram.RevPiProgram._checkclose?5(event=None) revpiprogram.RevPiProgram._createwidgets?5() revpiprogram.RevPiProgram._evt_optdown?5(text="") revpiprogram.RevPiProgram._evt_optup?5(text="") -revpiprogram.RevPiProgram._loaddefault?5(full=False) -revpiprogram.RevPiProgram._savedefaults?5() revpiprogram.RevPiProgram.check_replacedir?4(rootdir) revpiprogram.RevPiProgram.create_filelist?4(rootdir) revpiprogram.RevPiProgram.getpictoryrsc?4() @@ -77,7 +107,8 @@ revpiprogram.RevPiProgram.plcupload?4() revpiprogram.RevPiProgram.setpictoryrsc?4(filename=None) revpiprogram.RevPiProgram?1(master, xmlcli, xmlmode, revpi) revpiprogram._?8 -revpiprogram.savefile?7 +revpiprogram._loaddefaults?5(revpiname=None) +revpiprogram._savedefaults?5(revpiname, settings) revpipycontrol.RevPiPyControl._btnstate?5() revpipycontrol.RevPiPyControl._closeall?5() revpipycontrol.RevPiPyControl._closeapp?5(event=None) @@ -103,3 +134,17 @@ revpipycontrol.RevPiPyControl.visitwebsite?4() revpipycontrol.RevPiPyControl?1(master=None) revpipycontrol._?8 revpipycontrol.pycontrolversion?7 +revpipycontrol.shared.ipaclmanager.IpAclManager.__get_acl?6() +revpipycontrol.shared.ipaclmanager.IpAclManager.__get_filename?6() +revpipycontrol.shared.ipaclmanager.IpAclManager.__get_regex_acl?6() +revpipycontrol.shared.ipaclmanager.IpAclManager.__iter__?6() +revpipycontrol.shared.ipaclmanager.IpAclManager.__set_acl?6(value) +revpipycontrol.shared.ipaclmanager.IpAclManager.acl?7 +revpipycontrol.shared.ipaclmanager.IpAclManager.filename?7 +revpipycontrol.shared.ipaclmanager.IpAclManager.get_acllevel?4(ipaddress) +revpipycontrol.shared.ipaclmanager.IpAclManager.loadacl?4(str_acl) +revpipycontrol.shared.ipaclmanager.IpAclManager.loadaclfile?4(filename) +revpipycontrol.shared.ipaclmanager.IpAclManager.regex_acl?7 +revpipycontrol.shared.ipaclmanager.IpAclManager.writeaclfile?4(filename=None, aclname=None) +revpipycontrol.shared.ipaclmanager.IpAclManager?1(minlevel, maxlevel, acl=None) +revpipycontrol.shared.ipaclmanager.refullmatch?4(regex, string) diff --git a/revpipycontrol.bas b/revpipycontrol.bas index 32a817a..7e6201d 100644 --- a/revpipycontrol.bas +++ b/revpipycontrol.bas @@ -1,3 +1,4 @@ +AclManager ttk.Frame RevPiCheckClient tkinter.Frame RevPiInfo tkinter.Frame RevPiLogfile tkinter.Frame diff --git a/revpipycontrol.e4p b/revpipycontrol.e4p index 56bfd30..11cb786 100644 --- a/revpipycontrol.e4p +++ b/revpipycontrol.e4p @@ -1,18 +1,18 @@ - - + + en_US 66103e2eaf8a762f14d1fd51d8b1c9dcaf35a275 Python3 Console - 0.4.2 + 0.6.0 Sven Sager akira@narux.de - + revpipycontrol/revpipycontrol.py revpipycontrol/revpicheckclient.py @@ -23,6 +23,10 @@ revpipycontrol/revpiprogram.py revpipycontrol/mytools.py revpipycontrol/revpiinfo.py + revpipycontrol/aclmanager.py + revpipycontrol/revpilegacy.py + revpipycontrol/shared/ipaclmanager.py + revpipycontrol/shared/__init__.py diff --git a/revpipycontrol/aclmanager.py b/revpipycontrol/aclmanager.py new file mode 100644 index 0000000..72e19b2 --- /dev/null +++ b/revpipycontrol/aclmanager.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +# +# RevPiPyControl +# +# Webpage: https://revpimodio.org/revpipyplc/ +# (c) Sven Sager, License: LGPLv3 +# +u"""Manager für ACL Einträge.""" +import tkinter +import tkinter.messagebox as tkmsg +from mytools import gettrans +from shared.ipaclmanager import IpAclManager +from tkinter import ttk + +# Übersetzung laden +_ = gettrans() + + +class AclManager(ttk.Frame): + + u"""Hauptfenster des ACL-Managers.""" + + def __init__(self, master, minlevel, maxlevel, acl_str="", readonly=False): + u"""Init RevPiOption-Class. + @return None""" + super().__init__(master) + self.master.bind("", self._checkclose) + self.master.protocol("WM_DELETE_WINDOW", self._checkclose) + self.pack(expand=True, fill="both") + + # Daten laden + self.__acl = IpAclManager(minlevel, maxlevel, acl_str) + self.__dict_acltext = {} + self.__oldacl = self.__acl.acl + self.__ro = "disabled" if readonly else "normal" + self.maxlevel = maxlevel + self.minlevel = minlevel + + # Fenster bauen + self._createwidgets() + + def __get_acltext(self): + """Getter fuer Leveltexte. + @return Leveltexte als """ + return self.__dict_acltext.copy() + + def __set_acltext(self, value): + """Setter fuer Leveltexte. + @param value Leveltexte als """ + if type(value) != dict: + raise ValueError("value must be ") + self.__dict_acltext = value.copy() + + # Infotexte aufbauen + self.aclinfo.destroy() + self.aclinfo = ttk.Frame(self) + for acltext in self.__dict_acltext: + lbl = ttk.Label(self.aclinfo) + lbl["text"] = _("Level") + " {}: {}".format( + acltext, self.__dict_acltext[acltext] + ) + lbl.pack(anchor="w") + + self.aclinfo.pack(anchor="w", padx=4, pady=4) + + def _changesdone(self): + u"""Prüft ob sich die Einstellungen geändert haben. + @return True, wenn min. eine Einstellung geändert wurde""" + return not self.__acl.acl == self.__oldacl + + def _checkclose(self, event=None): + u"""Prüft ob Fenster beendet werden soll. + @param event tkinter-Event""" + ask = True + if self._changesdone(): + ask = tkmsg.askyesno( + _("Question"), + _("Do you really want to quit? \nUnsaved changes will " + "be lost"), + parent=self.master, default="no" + ) + + if ask: + self.master.destroy() + + def _createwidgets(self): + u"""Erstellt Widgets.""" + self.master.wm_title(_("IP access control list")) + self.master.wm_resizable(width=False, height=False) + + cpadw = {"padx": 4, "pady": 2, "sticky": "w"} + + # Gruppe Bestehende ACL ###################################### + gb_acl = ttk.LabelFrame(self) + gb_acl["text"] = _("Existing ACLs") + gb_acl.columnconfigure(0, weight=1) + gb_acl.columnconfigure(1, weight=1) + gb_acl.pack(expand=True, fill="both", padx=4, pady=4) + + row = 0 + frame = ttk.Frame(gb_acl) + frame.columnconfigure(0, weight=1) + + scb_acl = ttk.Scrollbar(frame) + + self.trv_acl = ttk.Treeview(frame) + self.trv_acl["columns"] = ("level") + self.trv_acl["yscrollcommand"] = scb_acl.set + self.trv_acl.heading("level", text=_("Access level")) + self.trv_acl.column("level", width=100) + self.trv_acl.bind("<>", self._status_editremove) + self._refreshacls() + self.trv_acl.grid(row=0, column=0, sticky="we") + + scb_acl["command"] = self.trv_acl.yview + scb_acl.grid(row=0, column=1, sticky="ns") + + frame.grid(row=row, columnspan=2, sticky="we") + + row = 1 + + # Edit / Remove button + self.btn_edit = ttk.Button(gb_acl) + self.btn_edit["command"] = self._loadfields + self.btn_edit["text"] = _("load entry") + self.btn_edit["state"] = "disabled" + self.btn_edit.grid(row=row, column=0, pady=4) + + self.btn_remove = ttk.Button(gb_acl) + self.btn_remove["command"] = self._ask_delete + self.btn_remove["text"] = _("remove entry") + self.btn_remove["state"] = "disabled" + self.btn_remove.grid(row=row, column=1, pady=4) + + # ############################################################ + + # Gruppe Bearbeiten ########################################## + gb_edit = ttk.LabelFrame(self) + gb_edit["text"] = _("Edit acess control list") + gb_edit.pack(expand=True, fill="both", padx=4, pady=4) + + frame = ttk.Frame(gb_edit) + frame.grid() + + row = 0 + lbl = ttk.Label(frame) + lbl["text"] = _("IP address") + ": " + lbl.grid(row=row, column=0, **cpadw) + + # Variablen IP / Level + self.var_ip1 = tkinter.StringVar(frame) + self.var_ip2 = tkinter.StringVar(frame) + self.var_ip3 = tkinter.StringVar(frame) + self.var_ip4 = tkinter.StringVar(frame) + self.var_acl = tkinter.StringVar(frame, self.minlevel) + + ip_block1 = ttk.Entry(frame, width=4) + ip_block2 = ttk.Entry(frame, width=4) + ip_block3 = ttk.Entry(frame, width=4) + ip_block4 = ttk.Entry(frame, width=4) + + ip_block1.bind( + "", + lambda event, tkvar=self.var_ip1: self._checkdot( + event, tkvar, ip_block2 + ) + ) + ip_block1["state"] = self.__ro + ip_block1["textvariable"] = self.var_ip1 + ip_block1.grid(row=row, column=1) + + ip_block2.bind( + "", + lambda event, tkvar=self.var_ip2: self._checkdot( + event, tkvar, ip_block3 + ) + ) + ip_block2.bind( + "", + lambda event, tkvar=self.var_ip2: self._checkback( + event, tkvar, ip_block1 + ) + ) + ip_block2["state"] = self.__ro + ip_block2["textvariable"] = self.var_ip2 + ip_block2.grid(row=row, column=3) + + ip_block3.bind( + "", + lambda event, tkvar=self.var_ip3: self._checkdot( + event, tkvar, ip_block4 + ) + ) + ip_block3.bind( + "", + lambda event, tkvar=self.var_ip3: self._checkback( + event, tkvar, ip_block2 + ) + ) + ip_block3["state"] = self.__ro + ip_block3["textvariable"] = self.var_ip3 + ip_block3.grid(row=row, column=5) + + ip_block4.bind( + "", + lambda event, tkvar=self.var_ip4: self._checkdot( + event, tkvar, None + ) + ) + ip_block4.bind( + "", + lambda event, tkvar=self.var_ip4: self._checkback( + event, tkvar, ip_block3 + ) + ) + ip_block4["state"] = self.__ro + ip_block4["textvariable"] = self.var_ip4 + ip_block4.grid(row=row, column=7) + + # Punkte zwischen IP-Feldern + for i in range(2, 7, 2): + lbl = ttk.Label(frame, text=".") + lbl.grid(row=row, column=i) + + row = 1 + lbl = ttk.Label(frame) + lbl["text"] = _("Access level") + ": " + lbl.grid(row=row, column=0, **cpadw) + + self.sb_level = tkinter.Spinbox(frame, width=4) + self.sb_level["from_"] = self.minlevel + self.sb_level["to"] = self.maxlevel + self.sb_level["state"] = self.__ro + self.sb_level["textvariable"] = self.var_acl + self.sb_level.grid(row=row, column=1, columnspan=8, sticky="w") + + # Buttons neben IP-Eintrag + self.btn_add = ttk.Button(gb_edit) + self.btn_add["command"] = self._savefields + self.btn_add["state"] = self.__ro + self.btn_add["text"] = _("add to list") + self.btn_add.grid(column=0, row=1, sticky="e", padx=4, pady=4) + self.btn_clear = ttk.Button(gb_edit) + self.btn_clear["command"] = self._clearfields + self.btn_clear["state"] = self.__ro + self.btn_clear["text"] = _("clear") + self.btn_clear.grid(column=1, row=1, padx=4, pady=4) + + # ############################################################ + + frame = ttk.Frame(self) + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + frame.pack(expand=True, fill="both", pady=4) + + # Buttons + btn_save = ttk.Button(frame) + btn_save["command"] = self._save + btn_save["state"] = self.__ro + btn_save["text"] = _("Save") + btn_save.grid(column=0, row=0) + + btn_close = ttk.Button(frame) + btn_close["command"] = self._checkclose + btn_close["text"] = _("Close") + btn_close.grid(column=1, row=0) + + # Infotexte vorbereiten + self.aclinfo = ttk.Frame(self) + + def _ask_delete(self): + u"""Löscht ein Eintrag der Liste.""" + str_acl = self.trv_acl.focus() + if str_acl != "": + lst_ipacl = str_acl.split() + ask = tkmsg.askyesno( + _("Question"), + _("Do you really want to delete the following item? \n" + "\nIP: {} / Level: {}").format(*lst_ipacl), + parent=self.master, default="no" + ) + if ask: + new_acl = self.__acl.acl.replace( + "{},{}".format(*lst_ipacl), "" + ).replace(" ", " ") + + if self.__acl.loadacl(new_acl.strip()): + # Liste neu aufbauen + self._refreshacls() + else: + tkmsg.showerror( + _("Error"), + _("Can not delete ACL! Check format."), + parent=self.master + ) + + def _checkback(self, event, tkvar, pretxt): + u"""Springt bei Backspace in vorheriges Feld. + + @param event TK Event + @param tkvar TK Variable zum prüfen + @param nexttxt Vorheriges IP Feld für Fokus + + """ + if pretxt is not None and event.keycode == 22 and tkvar.get() == "": + pretxt.focus_set() + + def _checkdot(self, event, tkvar, nexttxt): + u"""Prüft auf . und geht weiter. + + @param event TK Event + @param tkvar TK Variable zum prüfen + @param nexttxt Nächstes IP Feld für Fokus + + """ + val = tkvar.get() + if val.find(".") >= 0: + tkvar.set(val[:-1]) + if nexttxt is not None: + nexttxt.focus_set() + + def _clearfields(self): + u"""Leert die Eingabefelder.""" + self.var_ip1.set("") + self.var_ip2.set("") + self.var_ip3.set("") + self.var_ip4.set("") + self.var_acl.set(self.minlevel) + + def _loadfields(self): + u"""Übernimmt Listeneintrag in Editfelder.""" + str_acl = self.trv_acl.focus() + if str_acl != "": + lst_ip, acl = str_acl.split() + lst_ip = lst_ip.split(".") + self.var_ip1.set(lst_ip[0]) + self.var_ip2.set(lst_ip[1]) + self.var_ip3.set(lst_ip[2]) + self.var_ip4.set(lst_ip[3]) + self.var_acl.set(acl) + + def _refreshacls(self): + u"""Leert die ACL Liste und füllt sie neu.""" + self.trv_acl.delete(*self.trv_acl.get_children()) + for tup_acl in self.__acl: + self.trv_acl.insert( + "", "end", tup_acl, text=tup_acl[0], values=tup_acl[1] + ) + + def _save(self): + u"""Übernimt die Änderungen.""" + self.__oldacl = self.__acl.acl + self._checkclose() + + def _savefields(self): + u"""Übernimmt neuen ACL Eintrag.""" + new_acl = "{}.{}.{}.{},{}".format( + self.var_ip1.get(), + self.var_ip2.get(), + self.var_ip3.get(), + self.var_ip4.get(), + self.var_acl.get() + ) + if self.__acl.loadacl((self.__acl.acl + " " + new_acl).strip()): + self._refreshacls() + else: + tkmsg.showerror( + _("Error"), + _("Can not load new ACL! Check format."), + parent=self.master + ) + + def _status_editremove(self, tkevt): + u"""Setzt state der Buttons.""" + if self.__ro == "normal": + status = "disabled" if self.trv_acl.focus() == "" else "normal" + self.btn_edit["state"] = status + self.btn_remove["state"] = status + + def get_acl(self): + u"""Gibt die Konfigurierten ACL zurück. + @return ACL als """ + return self.__oldacl + + acl = property(get_acl) + acltext = property(__get_acltext, __set_acltext) + + +if __name__ == "__main__": + root = AclManager(tkinter.Tk(), 0, 9, " 192.168.50.100,2 127.0.0.*,1") + root.acltext = {0: "Keine Rechte", 1: "Hohe Rechte"} + root.mainloop() + print(root.acl) diff --git a/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po b/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po index e17facf..5f8fdda 100644 --- a/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po +++ b/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: RevPiPyControl 0.4.0\n" -"POT-Creation-Date: 2017-07-06 14:05+0200\n" -"PO-Revision-Date: 2017-07-06 14:05+0200\n" +"POT-Creation-Date: 2018-04-07 13:40+0200\n" +"PO-Revision-Date: 2018-04-07 13:54+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -15,17 +15,101 @@ msgstr "" "X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SearchPath-0: .\n" -#: revpicheckclient.py:107 revpicheckclient.py:439 revpioption.py:309 -#: revpiplclist.py:205 revpiprogram.py:315 revpiprogram.py:346 -#: revpiprogram.py:409 revpiprogram.py:415 revpiprogram.py:421 -#: revpiprogram.py:429 revpiprogram.py:435 revpiprogram.py:468 -#: revpiprogram.py:552 revpiprogram.py:625 revpiprogram.py:656 -#: revpiprogram.py:669 revpiprogram.py:724 revpiprogram.py:744 -#: revpiprogram.py:752 revpipycontrol.py:257 revpipycontrol.py:377 +#: aclmanager.py:59 +msgid "Level" +msgstr "Level" + +#: aclmanager.py:77 aclmanager.py:277 revpilegacy.py:69 revpilegacy.py:281 +#: revpilegacy.py:328 revpioption.py:73 revpioption.py:318 revpioption.py:362 +#: revpiplclist.py:61 revpiplclist.py:197 revpiprogram.py:394 +#: revpiprogram.py:468 +msgid "Question" +msgstr "Frage" + +#: aclmanager.py:78 revpilegacy.py:70 revpioption.py:74 revpiplclist.py:62 +msgid "" +"Do you really want to quit? \n" +"Unsaved changes will be lost" +msgstr "" +"Wollen Sie wirklich beenden? \n" +"Nicht gespeicherte Änderungen gehen verloren" + +#: aclmanager.py:88 +msgid "IP access control list" +msgstr "IP Zugriffsliste" + +#: aclmanager.py:95 +msgid "Existing ACLs" +msgstr "Vorhandene ACLs" + +#: aclmanager.py:109 aclmanager.py:227 +msgid "Access level" +msgstr "Zugriffslevel" + +#: aclmanager.py:125 +msgid "load entry" +msgstr "Eintrag laden" + +#: aclmanager.py:131 +msgid "remove entry" +msgstr "Eintrag entfernen" + +#: aclmanager.py:139 +msgid "Edit acess control list" +msgstr "Liste der Zugriffslevel bearbeiten" + +#: aclmanager.py:147 revpiplclist.py:101 +msgid "IP address" +msgstr "IP Adresse" + +#: aclmanager.py:241 +msgid "add to list" +msgstr "in Liste einfügen" + +#: aclmanager.py:246 +msgid "clear" +msgstr "leeren" + +#: aclmanager.py:260 revpilegacy.py:236 revpioption.py:263 revpiplclist.py:137 +msgid "Save" +msgstr "Speichern" + +#: aclmanager.py:265 revpiinfo.py:141 revpilegacy.py:241 revpioption.py:268 +#: revpiplclist.py:141 +msgid "Close" +msgstr "Schließen" + +#: aclmanager.py:278 +msgid "" +"Do you really want to delete the following item? \n" +"\n" +"IP: {} / Level: {}" +msgstr "" +"Soll das Element wirklich gelöscht werden?\n" +"\n" +"IP: {} / Level: {}" + +#: aclmanager.py:292 aclmanager.py:368 revpicheckclient.py:110 +#: revpicheckclient.py:442 revpilegacy.py:318 revpioption.py:87 +#: revpioption.py:352 revpiplclist.py:221 revpiprogram.py:330 +#: revpiprogram.py:361 revpiprogram.py:424 revpiprogram.py:430 +#: revpiprogram.py:436 revpiprogram.py:444 revpiprogram.py:450 +#: revpiprogram.py:483 revpiprogram.py:567 revpiprogram.py:640 +#: revpiprogram.py:671 revpiprogram.py:684 revpiprogram.py:739 +#: revpiprogram.py:759 revpiprogram.py:767 revpipycontrol.py:263 +#: revpipycontrol.py:390 msgid "Error" msgstr "Fehler" -#: revpicheckclient.py:108 +#: aclmanager.py:293 +msgid "Can not delete ACL! Check format." +msgstr "Kann ACL nicht löschen! Format prüfen." + +#: aclmanager.py:369 +msgid "Can not load new ACL! Check format." +msgstr "Kann neue ACL nicht laden! Format prüfen." + +#: revpicheckclient.py:111 msgid "" "Given value for Output '{}' is not valid! \n" "Reset to '{}'" @@ -33,40 +117,40 @@ msgstr "" "Angegebener Wert für Output '{}' ist nicht gültig! \n" "Setze auf '{}' zurück" -#: revpicheckclient.py:208 +#: revpicheckclient.py:211 msgid "Devices of RevPi" msgstr "Devices vom RevPi" -#: revpicheckclient.py:240 +#: revpicheckclient.py:243 msgid "Control" msgstr "Kontrolle" -#: revpicheckclient.py:244 +#: revpicheckclient.py:247 msgid "Read all IOs" msgstr "Lese alle IOs" -#: revpicheckclient.py:249 +#: revpicheckclient.py:252 msgid "Read just Inputs" msgstr "Nur Inputs lesen" -#: revpicheckclient.py:256 +#: revpicheckclient.py:259 msgid "Write Outputs" msgstr "Outputs schreiben" -#: revpicheckclient.py:262 +#: revpicheckclient.py:265 msgid "Autorefresh values" msgstr "Aktualisiere Werte automatisch" -#: revpicheckclient.py:270 +#: revpicheckclient.py:273 msgid "Write values to RevPi" msgstr "Schreibe Werte auf RevPi" -#: revpicheckclient.py:284 revpiprogram.py:442 revpipycontrol.py:238 -#: revpipycontrol.py:294 revpipycontrol.py:312 revpipycontrol.py:338 +#: revpicheckclient.py:287 revpiprogram.py:457 revpipycontrol.py:244 +#: revpipycontrol.py:300 revpipycontrol.py:318 revpipycontrol.py:351 msgid "Warning" msgstr "Warnung" -#: revpicheckclient.py:285 +#: revpicheckclient.py:288 msgid "" "You want to set outputs on the RevPi! Note that these are set " "IMMEDIATELY!!! \n" @@ -78,31 +162,31 @@ msgstr "" "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:436 +#: revpicheckclient.py:439 msgid "Error set value of device '{}' Output '{}': {} \n" msgstr "Fehler beim Setzen der Werte auf Device '{}' bei Output '{}': {} \n" -#: revpiinfo.py:38 +#: revpiinfo.py:41 msgid "RevPi Python PLC info" msgstr "RevPi Python PLC Information" -#: revpiinfo.py:51 +#: revpiinfo.py:54 msgid "RevPi Python PLC - Control" msgstr "RevPi Python PLC - Kontrollcenter" -#: revpiinfo.py:55 +#: revpiinfo.py:58 msgid "Version: {}" msgstr "Version: {}" -#: revpiinfo.py:78 +#: revpiinfo.py:81 msgid "RevPiPyLoad version on RevPi:" msgstr "RevPiPyLoad Version auf RevPi:" -#: revpiinfo.py:83 +#: revpiinfo.py:86 msgid "not conn." msgstr "nicht verb." -#: revpiinfo.py:91 +#: revpiinfo.py:94 msgid "" "\n" "RevPiModIO, RevPiPyLoad and RevPiPyControl\n" @@ -125,104 +209,63 @@ msgstr "" "\n" "(c) Sven Sager, Lizenz: LGPLv3" -#: revpiinfo.py:138 revpioption.py:232 revpiplclist.py:134 -msgid "Close" -msgstr "Schließen" - -#: revpilogfile.py:41 -msgid "RevPi Python PLC Logs" -msgstr "RevPi Python PLC Logdaten" - -#: revpilogfile.py:54 -msgid "RevPiPyLoad - Logfile" -msgstr "RevPiPyLoad - Logdatei" - -#: revpilogfile.py:58 revpilogfile.py:73 -msgid "Clear screen" -msgstr "Leere Ansicht" - -#: revpilogfile.py:69 -msgid "Python PLC program - Logfile" -msgstr "Python PLC Programm - Logdatei" - -#: revpilogfile.py:157 -msgid "Can not access log file on the RevPi" -msgstr "Auf die Logdatei des RevPi kann nicht zugegriffen werden" - -#: revpioption.py:66 revpioption.py:272 revpioption.py:319 revpiplclist.py:63 -#: revpiplclist.py:181 revpiprogram.py:379 revpiprogram.py:453 -msgid "Question" -msgstr "Frage" - -#: revpioption.py:67 revpiplclist.py:64 -msgid "" -"Do you really want to quit? \n" -"Unsaved changes will be lost" -msgstr "" -"Wollen Sie wirklich beenden? \n" -"Nicht gespeicherte Änderungen gehen verloren" - -#: revpioption.py:77 +#: revpilegacy.py:80 revpioption.py:97 msgid "RevPi Python PLC Options" msgstr "RevPi Python PLC Einstellungen" -#: revpioption.py:87 +#: revpilegacy.py:90 revpioption.py:108 msgid "Start / Stop behavior" msgstr "Start / Stop Verhalten" -#: revpioption.py:96 +#: revpilegacy.py:99 revpioption.py:118 msgid "Start program automatically" msgstr "Starte Programm automatisch" -#: revpioption.py:102 +#: revpilegacy.py:105 revpioption.py:124 msgid "Restart program after exit" msgstr "Starte Programm nach Beenden neu" -#: revpioption.py:110 -msgid "" -"Set process image to NULL if program\n" -"terminates successfully" -msgstr "" -"Prozessabbild auf NULL setzen, wenn Programm\n" -"erfolgreich beendet wird" +#: revpilegacy.py:111 revpioption.py:140 +msgid "Set process image to NULL if program terminates..." +msgstr "Prozessabbild auf NULL setzen, wenn Programm..." -#: revpioption.py:118 -msgid "" -"Set process image to NULL if program\n" -"terminates with errors" -msgstr "" -"Prozessabbild auf NULL setzen, wenn Programm\n" -"fehlerhaft beendet wird" +#: revpilegacy.py:116 revpioption.py:145 +msgid "... successfully" +msgstr "... ohne Fehler beendet" -#: revpioption.py:125 +#: revpilegacy.py:122 revpioption.py:151 +msgid "... with errors" +msgstr "... mit Fehlern beendet" + +#: revpilegacy.py:128 revpioption.py:159 msgid "PLC program" msgstr "PLC Programm" -#: revpioption.py:136 +#: revpilegacy.py:139 revpioption.py:170 msgid "Python version" msgstr "Python Version" -#: revpioption.py:153 +#: revpilegacy.py:158 revpioption.py:189 msgid "Python PLC program name" msgstr "Python PLC Programmname" -#: revpioption.py:165 +#: revpilegacy.py:173 revpioption.py:204 msgid "Program arguments" msgstr "Programmargumente" -#: revpioption.py:174 +#: revpilegacy.py:184 revpioption.py:225 msgid "Use RevPi as PLC-Slave" msgstr "RevPi als PLC-Slave verwenden" -#: revpioption.py:181 +#: revpilegacy.py:190 msgid "XML-RPC server" msgstr "XML-RPC Server" -#: revpioption.py:193 +#: revpilegacy.py:202 revpioption.py:250 msgid "Activate XML-RPC server on RevPi" msgstr "Aktiviere XML-RPC Server auf RevPi" -#: revpioption.py:201 +#: revpilegacy.py:210 msgid "" "Allow download of piCtory configuration and\n" "PLC programm" @@ -230,7 +273,7 @@ msgstr "" "Download von piCtroy Konfiguration und\n" "PLC Programm zulassen" -#: revpioption.py:208 +#: revpilegacy.py:217 msgid "" "Allow upload of piCtory configuration and\n" "PLC programm" @@ -238,23 +281,20 @@ msgstr "" "Hochladen von piCtroy Konfiguration und\n" "PLC Programm zulassen" -#: revpioption.py:213 +#: revpilegacy.py:222 msgid "XML-RPC server port" msgstr "XML-RPC Serverport" -#: revpioption.py:227 revpiplclist.py:131 -msgid "Save" -msgstr "Speichern" - -#: revpioption.py:265 revpioption.py:301 revpiplclist.py:197 revpiprogram.py:68 +#: revpilegacy.py:274 revpilegacy.py:310 revpioption.py:300 revpioption.py:344 +#: revpiplclist.py:213 revpiprogram.py:98 msgid "Information" msgstr "Information" -#: revpioption.py:266 +#: revpilegacy.py:275 revpioption.py:301 msgid "You have not made any changes to save." msgstr "Sie haben keine Änderungen zum Speichern vorgenommen." -#: revpioption.py:273 +#: revpilegacy.py:282 msgid "" "The settings are now saved on the Revolution Pi. \n" "\n" @@ -266,17 +306,17 @@ msgstr "" "Sollen die neuen Einstellungen sofort in Kraft treten? \n" "Dies bedeutet einen Neustart des Dienstes und des laufenden PLC-Programms!" -#: revpioption.py:302 +#: revpilegacy.py:311 revpioption.py:345 msgid "Settings saved" msgstr "Einstellungen gespeichert" -#: revpioption.py:310 +#: revpilegacy.py:319 revpioption.py:353 msgid "The settings could not be saved. This can happen if values are wrong!" msgstr "" "Die Einstellungen konnten nicht gesichert werden. Dies kann passieren, wenn " "Werte falsch sind!" -#: revpioption.py:320 +#: revpilegacy.py:329 revpioption.py:363 msgid "" "Are you sure you want to deactivate the XML-RPC server? You will NOT be able " "to access the Revolution Pi with this program." @@ -284,39 +324,125 @@ msgstr "" "Soll der XML-RPC Server wirklich beendet werden? Sie können dann NICHT mehr " "mit diesem Programm auf den Revolution Pi zugreifen." -#: revpiplclist.py:74 +#: revpilogfile.py:44 +msgid "RevPi Python PLC Logs" +msgstr "RevPi Python PLC Logdaten" + +#: revpilogfile.py:57 +msgid "RevPiPyLoad - Logfile" +msgstr "RevPiPyLoad - Logdatei" + +#: revpilogfile.py:61 revpilogfile.py:77 +msgid "Clear screen" +msgstr "Leere Ansicht" + +#: revpilogfile.py:73 +msgid "Python PLC program - Logfile" +msgstr "Python PLC Programm - Logdatei" + +#: revpilogfile.py:162 +msgid "Can not access log file on the RevPi" +msgstr "Auf die Logdatei des RevPi kann nicht zugegriffen werden" + +#: revpioption.py:88 +msgid "The value of 'restart delay' ist not valid." +msgstr "Der Wert für 'Neustart Verzögerung' ist nicht gültig." + +#: revpioption.py:130 +msgid "Restart after n seconds of delay" +msgstr "Neustart nach n Sekunden Verzögerung" + +#: revpioption.py:214 +msgid "RevPiPyLoad server services" +msgstr "RevPiPyLoad Server Dienste" + +#: revpioption.py:231 revpioption.py:256 +msgid "Edit ACL" +msgstr "ACL bearbeiten" + +#: revpioption.py:236 +msgid "RevPi-Slave service is:" +msgstr "RevPi-Slave Dienst Status:" + +#: revpioption.py:242 +msgid "running" +msgstr "läuft" + +#: revpioption.py:242 +msgid "stopped" +msgstr "beendet" + +#: revpioption.py:319 +msgid "" +"The settings will be set on the Revolution Pi now. \n" +"\n" +"If you made changes on the 'PCL Program' section, your plc program will " +"restart! \n" +"ACL changes and service settings are applied immediately." +msgstr "" +"Die Einstellungen werden jetzt auf dem Revolution Pi gespeichert\n" +"\n" +"Wenn Änderungen im Block 'PLC Programm' vorgenommen wurden, wird das PLC " +"Programm jetzt neu starten!\n" +"ACL Änderungen und Diensteinstellungen werden sofort angewandt." + +#: revpioption.py:382 +msgid "read only" +msgstr "nur lesen" + +#: revpioption.py:383 +msgid "read and write" +msgstr "lesen und schreiben" + +#: revpioption.py:399 +msgid "Start/Stop PLC program and read logs" +msgstr "PLC Programm starten/stoppen und Logs lesen" + +#: revpioption.py:400 +msgid "+ read IOs in watch modus" +msgstr "+ IOs in 'Watch modus' lesen" + +#: revpioption.py:401 +msgid "+ read properties and download PLC program" +msgstr "+ Einstellungen lesen und PLC Programm herunterladen" + +#: revpioption.py:402 +msgid "+ upload PLC program" +msgstr "+ PLC Programm hochladen" + +#: revpioption.py:403 +msgid "+ set properties" +msgstr "+ Einstellungen ändern" + +#: revpiplclist.py:72 msgid "RevPi Python PLC connections" msgstr "RevPi Python PLC Verbindungen" -#: revpiplclist.py:94 +#: revpiplclist.py:92 msgid "Name" msgstr "Name" -#: revpiplclist.py:101 -msgid "IP address" -msgstr "IP Adresse" - -#: revpiplclist.py:109 +#: revpiplclist.py:110 msgid "Port" msgstr "Port" -#: revpiplclist.py:118 +#: revpiplclist.py:121 msgid "New" msgstr "Neu" -#: revpiplclist.py:121 +#: revpiplclist.py:125 msgid "Apply" msgstr "Übernehmen" -#: revpiplclist.py:125 +#: revpiplclist.py:130 msgid "Remove" msgstr "Entfernen" -#: revpiplclist.py:182 +#: revpiplclist.py:198 msgid "Do you really want to delete the selected connection '{}'?" msgstr "Wollen Sie die ausgewählte Verbindung '{}' wirklich löschen?" -#: revpiplclist.py:198 +#: revpiplclist.py:214 msgid "" "Successfully saved. \n" "Do you want to close this window?" @@ -324,11 +450,11 @@ msgstr "" "Verbindungen erfolgreich gespeichert. \n" "Möchten Sie dieses Fenster jetzt schließen?" -#: revpiplclist.py:206 +#: revpiplclist.py:222 msgid "Failed to save connections" msgstr "Verbindungen konnten nicht gespeichert werden" -#: revpiprogram.py:69 +#: revpiprogram.py:99 msgid "" "A PLC program has been uploaded. Please check the PLC options to see if the " "correct program is specified as the start program." @@ -336,135 +462,135 @@ msgstr "" "Ein PLC Programm wurde hochgeladen. Bitte prüfen Sie die PLC Optionen, ob " "dort das richtige Programm als Startprogramm angegeben ist." -#: revpiprogram.py:78 +#: revpiprogram.py:108 msgid "RevPi Python PLC program" msgstr "RevPi Python PLC Programm" -#: revpiprogram.py:92 +#: revpiprogram.py:122 msgid "PLC python program" msgstr "PLC Python Programm" -#: revpiprogram.py:102 revpiprogram.py:104 revpiprogram.py:112 -#: revpiprogram.py:116 +#: revpiprogram.py:132 revpiprogram.py:134 revpiprogram.py:142 +#: revpiprogram.py:146 msgid "Files" msgstr "Dateien" -#: revpiprogram.py:102 revpiprogram.py:104 revpiprogram.py:500 -#: revpiprogram.py:609 +#: revpiprogram.py:132 revpiprogram.py:134 revpiprogram.py:515 +#: revpiprogram.py:624 msgid "Zip archive" msgstr "Zip Archiv" -#: revpiprogram.py:102 revpiprogram.py:104 revpiprogram.py:513 -#: revpiprogram.py:639 +#: revpiprogram.py:132 revpiprogram.py:134 revpiprogram.py:528 +#: revpiprogram.py:654 msgid "TGZ archive" msgstr "TGZ Archiv" -#: revpiprogram.py:104 +#: revpiprogram.py:134 msgid "Folder" msgstr "Verzeichnis" -#: revpiprogram.py:121 +#: revpiprogram.py:151 msgid "Download PLC program as:" msgstr "PLC Programm herunterladen als:" -#: revpiprogram.py:131 +#: revpiprogram.py:162 msgid "include piCtory configuration" msgstr "inkl. piCtory Konfiguration" -#: revpiprogram.py:136 revpiprogram.py:180 revpiprogram.py:201 +#: revpiprogram.py:168 revpiprogram.py:216 revpiprogram.py:241 msgid "Download" msgstr "Herunterladen" -#: revpiprogram.py:141 +#: revpiprogram.py:173 msgid "Upload PLC program as:" msgstr "PLC Programm hochladen als:" -#: revpiprogram.py:153 +#: revpiprogram.py:187 msgid "clean upload folder before upload" msgstr "Uploadverzeichnis vor dem Upload leeren" -#: revpiprogram.py:160 +#: revpiprogram.py:194 msgid "includes piCtory configuration" msgstr "enthält piCtory Konfiguration" -#: revpiprogram.py:166 revpiprogram.py:188 +#: revpiprogram.py:201 revpiprogram.py:226 msgid "Upload" msgstr "Hochladen" -#: revpiprogram.py:172 +#: revpiprogram.py:207 msgid "piCtory configuration" msgstr "piCtory Konfiguration" -#: revpiprogram.py:176 +#: revpiprogram.py:211 msgid "Download piCtory configuration" msgstr "piCtory Konfiguration herunterladen" -#: revpiprogram.py:183 +#: revpiprogram.py:220 msgid "Upload piCtory configuration" msgstr "piCtory Konfiguration hochladen" -#: revpiprogram.py:194 +#: revpiprogram.py:232 msgid "piControl0 process image" msgstr "piControl0 Prozessabbild" -#: revpiprogram.py:197 +#: revpiprogram.py:236 msgid "Download process image dump" msgstr "Prozessabbild Dump herunterladen" -#: revpiprogram.py:207 +#: revpiprogram.py:247 msgid "Reset piControl" msgstr "piControl zurücksetzen" -#: revpiprogram.py:210 +#: revpiprogram.py:251 msgid "Execute piControlReset" msgstr "piControlReset ausführen" -#: revpiprogram.py:214 +#: revpiprogram.py:256 msgid "execute" msgstr "ausführen" -#: revpiprogram.py:220 revpipycontrol.py:109 +#: revpiprogram.py:262 revpipycontrol.py:114 msgid "Exit" msgstr "Beenden" -#: revpiprogram.py:305 revpiprogram.py:336 revpiprogram.py:496 -#: revpiprogram.py:509 +#: revpiprogram.py:320 revpiprogram.py:351 revpiprogram.py:511 +#: revpiprogram.py:524 msgid "Save as..." msgstr "Speichern unter..." -#: revpiprogram.py:308 revpiprogram.py:371 +#: revpiprogram.py:323 revpiprogram.py:386 msgid "piCtory config" msgstr "piCtory Konfiguration" -#: revpiprogram.py:308 revpiprogram.py:339 revpiprogram.py:371 -#: revpiprogram.py:500 revpiprogram.py:513 revpiprogram.py:584 -#: revpiprogram.py:609 revpiprogram.py:639 +#: revpiprogram.py:323 revpiprogram.py:354 revpiprogram.py:386 +#: revpiprogram.py:515 revpiprogram.py:528 revpiprogram.py:599 +#: revpiprogram.py:624 revpiprogram.py:654 msgid "All files" msgstr "Alle Dateien" -#: revpiprogram.py:316 revpiprogram.py:347 revpiprogram.py:553 +#: revpiprogram.py:331 revpiprogram.py:362 revpiprogram.py:568 msgid "Could not load and save file!" msgstr "Datei konnte nicht geladen und gespeichert werden!" -#: revpiprogram.py:321 revpiprogram.py:352 revpiprogram.py:390 -#: revpiprogram.py:398 revpiprogram.py:462 revpiprogram.py:558 -#: revpiprogram.py:714 +#: revpiprogram.py:336 revpiprogram.py:367 revpiprogram.py:405 +#: revpiprogram.py:413 revpiprogram.py:477 revpiprogram.py:573 +#: revpiprogram.py:729 msgid "Success" msgstr "Erfolgreich" -#: revpiprogram.py:322 revpiprogram.py:353 revpiprogram.py:559 +#: revpiprogram.py:337 revpiprogram.py:368 revpiprogram.py:574 msgid "File successfully loaded and saved." msgstr "Dateien erfolgreich übertragen und gespeichert." -#: revpiprogram.py:339 +#: revpiprogram.py:354 msgid "Imagefiles" msgstr "Image Dateien" -#: revpiprogram.py:367 +#: revpiprogram.py:382 msgid "Open piCtory file..." msgstr "piCtory Datei öffnen..." -#: revpiprogram.py:380 +#: revpiprogram.py:395 msgid "" "Should the piControl driver be reset after uploading the piCtory " "configuration?" @@ -472,7 +598,7 @@ msgstr "" "Soll nach dem Hochladen der piCtory Konfiguration ein Reset am piControl " "Treiber durchgeführt werden?" -#: revpiprogram.py:391 +#: revpiprogram.py:406 msgid "" "The transfer of the piCtory configuration and the reset of piControl have " "been successfully executed." @@ -480,20 +606,20 @@ msgstr "" "Die Übertragung der piCtory Konfiguration und der Reset von piControl wurden " "erfolgreich ausgeführt." -#: revpiprogram.py:399 +#: revpiprogram.py:414 msgid "The piCtory configuration was successfully transferred." msgstr "" "Die Übertragung der piCtory Konfiguration wurde erfolgreich ausgeführt." -#: revpiprogram.py:410 +#: revpiprogram.py:425 msgid "Can not process the transferred file." msgstr "Kann die übertragene Datei nicht verarbeiten." -#: revpiprogram.py:416 +#: revpiprogram.py:431 msgid "Can not find main elements in piCtory file." msgstr "Kann Hauptelemente in piCtory datei nicht finden." -#: revpiprogram.py:422 +#: revpiprogram.py:437 msgid "" "Contained devices could not be found on Revolution Pi. The configuration may " "be from a newer piCtory version!" @@ -501,17 +627,17 @@ msgstr "" "Enthaltene Geräte konnten auf dem Revolution Pi nicht gefunden werden. " "Möglicherweise stammt die Konfiguration von einer neueren piCtory Version!" -#: revpiprogram.py:430 +#: revpiprogram.py:445 msgid "Could not load RAP catalog on Revolution Pi." msgstr "Konnte RAP Katalog auf dem Revolution Pi nicht laden." -#: revpiprogram.py:436 +#: revpiprogram.py:451 msgid "The piCtory configuration could not be written on the Revolution Pi." msgstr "" "Die piCtory Konfiguration konnte auf dem Revolution Pi nicht geschrieben " "werden." -#: revpiprogram.py:443 +#: revpiprogram.py:458 msgid "" "The piCtroy configuration has been saved successfully. \n" "An error occurred on piControl reset!" @@ -519,7 +645,7 @@ msgstr "" "Die piCtroy Konfiguration wurde erfolgreich gespeichert. \n" "Beim piControl Reset trat allerdings ein Fehler auf!" -#: revpiprogram.py:454 +#: revpiprogram.py:469 msgid "" "Are you sure to reset piControl? \n" "The process image and the piBridge are interrupted !!!" @@ -527,139 +653,138 @@ msgstr "" "Soll piControlReset wirklich durchgeführt werden? \n" "Das Prozessabbild und die piBridge werden dann unterbrochen!!!" -#: revpiprogram.py:463 +#: revpiprogram.py:478 msgid "piControl reset executed successfully" msgstr "piControl reset wurde erfolgreich ausgeführt" -#: revpiprogram.py:469 +#: revpiprogram.py:484 msgid "piControl reset could not be executed successfully" msgstr "piControl Reset konnte nicht erfolgreich durchgeführt werden" -#: revpiprogram.py:483 +#: revpiprogram.py:498 msgid "Directory to save" msgstr "Verzeichnis zum speichern" -#: revpiprogram.py:594 +#: revpiprogram.py:609 msgid "Folder to upload" msgstr "Verzeichnis zum Hochladen" -#: revpiprogram.py:605 +#: revpiprogram.py:620 msgid "Upload Zip archive..." msgstr "Zip Archiv hochladen..." -#: revpiprogram.py:626 +#: revpiprogram.py:641 msgid "The specified file is not a ZIP archive." msgstr "Die angegebene Datei ist kein ZIP Archiv." -#: revpiprogram.py:635 +#: revpiprogram.py:650 msgid "Upload TarGz archiv..." msgstr "TarGz Archiv hochladen..." -#: revpiprogram.py:657 +#: revpiprogram.py:672 msgid "The specified file is not a TAR archive." msgstr "Die angegebene Datei ist kein TAR Archiv." -#: revpiprogram.py:670 +#: revpiprogram.py:685 msgid "There was an error deleting the files on the Revolution Pi." msgstr "" "Beim Löschen der Dateien auf dem Revolution Pi ist ein Fehler aufgetreten." -#: revpiprogram.py:715 +#: revpiprogram.py:730 msgid "The PLC program was transferred successfully." msgstr "Das PLC Programm wurde erfolgreich übertragen." -#: revpiprogram.py:725 +#: revpiprogram.py:740 msgid "There is no piCtory configuration in this archive." msgstr "Es wurde keine piCtory Konfiguration im Archiv gefunden." -#: revpiprogram.py:745 +#: revpiprogram.py:760 msgid "The Revolution Pi could not process some parts of the transmission." msgstr "Der Revoluton Pi konnte Teile der Übertragung nicht verarbeiten." -#: revpiprogram.py:753 +#: revpiprogram.py:768 msgid "Errors occurred during transmission" msgstr "Bei der Übertragung traten Fehler auf" -#: revpipycontrol.py:107 +#: revpipycontrol.py:112 msgid "Connections..." msgstr "Verbindungen..." -#: revpipycontrol.py:110 +#: revpipycontrol.py:115 msgid "Main" msgstr "Datei" -#: revpipycontrol.py:118 +#: revpipycontrol.py:123 msgid "Visit website..." msgstr "Webseite besuchen..." -#: revpipycontrol.py:120 +#: revpipycontrol.py:125 msgid "Info..." msgstr "Info..." -#: revpipycontrol.py:121 +#: revpipycontrol.py:126 msgid "Help" msgstr "Hilfe" -#: revpipycontrol.py:129 +#: revpipycontrol.py:134 msgid "PLC start" msgstr "PLC Start" -#: revpipycontrol.py:134 +#: revpipycontrol.py:139 msgid "PLC stop" msgstr "PLC Stopp" -#: revpipycontrol.py:139 +#: revpipycontrol.py:144 msgid "PLC restart" msgstr "PLC Neustart" -#: revpipycontrol.py:144 +#: revpipycontrol.py:149 msgid "PLC logs" msgstr "PLC Logs" -#: revpipycontrol.py:155 +#: revpipycontrol.py:160 msgid "PLC watch mode" msgstr "PLC watch Modus" -#: revpipycontrol.py:172 +#: revpipycontrol.py:177 msgid "PLC log..." msgstr "PLC Log..." -#: revpipycontrol.py:174 +#: revpipycontrol.py:179 msgid "PLC options..." msgstr "PLC Optionen..." -#: revpipycontrol.py:176 +#: revpipycontrol.py:181 msgid "PLC program..." msgstr "PLC Programm..." -#: revpipycontrol.py:180 +#: revpipycontrol.py:185 msgid "Disconnect" msgstr "Trennen" -#: revpipycontrol.py:185 +#: revpipycontrol.py:190 msgid "Connect" msgstr "Verbinden" -#: revpipycontrol.py:239 +#: revpipycontrol.py:245 msgid "" "The watch mode ist not supported in version {} of RevPiPyLoad on your RevPi! " -"You need at least version 0.4.0. Or the python3-revpimodio module is not " -"installton your RevPi at least version 0.15.0." +"You need at least version 0.5.3! Maybe the python3-revpimodio2 module is not " +"installed on your RevPi at least version 2.0.0." msgstr "" -"Der Watch-Modus wird von der RevPiPyLoad Version {} auf dem RevPi nicht " -"unterstützt! Sie benötigen mindestens Version 0.4.0. Oder das python3-" -"revpimodio Modul ist auf dem RevPi nicht in min. Version 0.15.0 installiert." +"Der 'Watch Mode' ist nicht unterstützt in RevPiPyLoad Version {}! Es muss " +"mindestens Version 0.5.3 installiert sein! Stellen Sie sicher, dass auch " +"python3-revpimodio2 auf dem RevPi installiert ist." -#: revpipycontrol.py:258 +#: revpipycontrol.py:264 msgid "" "Can not load piCtory configuration. \n" -"Have you created a hardware configuration? Please check this in piCtory!" +"Did you create a hardware configuration? Please check this in piCtory!" msgstr "" -"Kann piCtory Konfiguration nicht laden. \n" -"Haben Sie eine Hardwarekonfiguration angelegt? Bitte prüfen Sie dies in " -"piCtory!" +"Kann piCtory Konfiguration nicht laden\n" +"Wurde eine Hardwarekonfiguration erstellt? Bitte in piCtory prüfen!" -#: revpipycontrol.py:295 +#: revpipycontrol.py:301 msgid "" "This version of Logviewer ist not supported in version {} of RevPiPyLoad on " "your RevPi! You need at least version 0.4.1." @@ -667,7 +792,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:313 revpipycontrol.py:339 +#: revpipycontrol.py:319 revpipycontrol.py:352 msgid "" "XML-RPC access mode in the RevPiPyLoad configuration is too small to access " "this dialog!" @@ -675,10 +800,42 @@ msgstr "" "Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch genug eingestellt, um " "diesen Dialog zu verwenden!" -#: revpipycontrol.py:378 +#: revpipycontrol.py:391 msgid "Can not reach server!" msgstr "Server ist nicht erreichbar!" +#~ msgid "" +#~ "Set process image to NULL if program\n" +#~ "terminates successfully" +#~ msgstr "" +#~ "Prozessabbild auf NULL setzen, wenn Programm\n" +#~ "erfolgreich beendet wird" + +#~ msgid "" +#~ "Set process image to NULL if program\n" +#~ "terminates with errors" +#~ msgstr "" +#~ "Prozessabbild auf NULL setzen, wenn Programm\n" +#~ "fehlerhaft beendet wird" + +#~ msgid "" +#~ "The watch mode ist not supported in version {} of RevPiPyLoad on your " +#~ "RevPi! You need at least version 0.4.0. Or the python3-revpimodio module " +#~ "is not installton your RevPi at least version 0.15.0." +#~ msgstr "" +#~ "Der Watch-Modus wird von der RevPiPyLoad Version {} auf dem RevPi nicht " +#~ "unterstützt! Sie benötigen mindestens Version 0.4.0. Oder das python3-" +#~ "revpimodio Modul ist auf dem RevPi nicht in min. Version 0.15.0 " +#~ "installiert." + +#~ msgid "" +#~ "Can not load piCtory configuration. \n" +#~ "Have you created a hardware configuration? Please check this in piCtory!" +#~ msgstr "" +#~ "Kann piCtory Konfiguration nicht laden. \n" +#~ "Haben Sie eine Hardwarekonfiguration angelegt? Bitte prüfen Sie dies in " +#~ "piCtory!" + #~ msgid "" #~ "The watch mode ist not supported in version {} of RevPiPyLoad on your " #~ "RevPi! You need at least version 0.4.0." diff --git a/revpipycontrol/mytools.py b/revpipycontrol/mytools.py index b9fd30d..8cd4ab9 100644 --- a/revpipycontrol/mytools.py +++ b/revpipycontrol/mytools.py @@ -1,15 +1,28 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +"""Tools-Sammlung.""" import gettext import locale import sys +from os import environ from os.path import dirname from os.path import join as pathjoin +from sys import platform + +# Systemwerte und SaveFiles +if platform == "linux": + homedir = environ["HOME"] +else: + homedir = environ["APPDATA"] +savefile_connections = pathjoin( + homedir, ".revpipyplc", "connections.dat") +savefile_programpath = pathjoin( + homedir, ".revpipyplc", "programpath.dat") def addroot(filename): @@ -29,7 +42,12 @@ def addroot(filename): def gettrans(proglang=None): + u"""Wertet die Sprache des OS aus und gibt Übersetzung zurück. + @param proglang Bestimmte Sprache laden + @return gettext Übersetzung für Zuweisung an '_' + + """ # Sprache auswählen if proglang is None: # Autodetect Language or switch to static diff --git a/revpipycontrol/revpicheckclient.py b/revpipycontrol/revpicheckclient.py index c619e8e..f75b103 100644 --- a/revpipycontrol/revpicheckclient.py +++ b/revpipycontrol/revpicheckclient.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # @@ -6,7 +7,7 @@ # # Thranks to: http://stackoverflow.com/questions/3085696/adding-a- # scrollbar-to-a-group-of-widgets-in-tkinter - +u"""Fenstererweiterung für den 'watch modus'.""" import pickle import tkinter import tkinter.messagebox as tkmsg @@ -20,6 +21,8 @@ _ = gettrans() class RevPiCheckClient(tkinter.Frame): + u"""Baut Fenstererweiterung für 'watch modus'.""" + def __init__(self, master, xmlcli, xmlmode=0): """Instantiiert MyApp-Klasse.""" super().__init__(master) @@ -202,7 +205,7 @@ class RevPiCheckClient(tkinter.Frame): def _createwidgets(self): """Erstellt den Fensterinhalt.""" - cFxPxy53 = {"fill": "x", "padx": 5, "pady": 3} + cfxpxy53 = {"fill": "x", "padx": 5, "pady": 3} devgrp = tkinter.LabelFrame(self) devgrp["text"] = _("Devices of RevPi") @@ -233,7 +236,7 @@ class RevPiCheckClient(tkinter.Frame): btn = tkinter.Button(devgrp) btn["command"] = lambda win=win: self.__showwin(win) btn["text"] = "{} | {}".format(dev, self.dict_devices[dev]) - btn.pack(**cFxPxy53) + btn.pack(**cfxpxy53) # Steuerungsfunktionen cntgrp = tkinter.LabelFrame(self) @@ -243,19 +246,19 @@ class RevPiCheckClient(tkinter.Frame): self.btn_refresh = tkinter.Button(cntgrp) self.btn_refresh["text"] = _("Read all IOs") self.btn_refresh["command"] = self.refreshvalues - self.btn_refresh.pack(**cFxPxy53) + self.btn_refresh.pack(**cfxpxy53) self.btn_read = tkinter.Button(cntgrp) self.btn_read["text"] = _("Read just Inputs") self.btn_read["command"] = self.readvalues - self.btn_read.pack(**cFxPxy53) + self.btn_read.pack(**cfxpxy53) self.btn_write = tkinter.Button(cntgrp) self.btn_write["state"] = "normal" if self.xmlmode >= 3 \ else "disabled" self.btn_write["text"] = _("Write Outputs") self.btn_write["command"] = self.writevalues - self.btn_write.pack(**cFxPxy53) + self.btn_write.pack(**cfxpxy53) self.chk_auto = tkinter.Checkbutton(cntgrp) self.chk_auto["command"] = self.toggleauto diff --git a/revpipycontrol/revpiinfo.py b/revpipycontrol/revpiinfo.py index 775ba9b..58393e5 100644 --- a/revpipycontrol/revpiinfo.py +++ b/revpipycontrol/revpiinfo.py @@ -1,10 +1,11 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +u"""Programminformationen anzeigen.""" import tkinter import tkinter.font as tkf import webbrowser @@ -16,6 +17,8 @@ _ = gettrans() class RevPiInfo(tkinter.Frame): + u"""Baut Frame für Programminformationen.""" + def __init__(self, master, xmlcli, version): u"""Init RevPiLogfile-Class.""" self.master = master diff --git a/revpipycontrol/revpilegacy.py b/revpipycontrol/revpilegacy.py new file mode 100644 index 0000000..6c97f09 --- /dev/null +++ b/revpipycontrol/revpilegacy.py @@ -0,0 +1,358 @@ +# -*- coding: utf-8 -*- +# +# RevPiPyControl +# +# Webpage: https://revpimodio.org/revpipyplc/ +# (c) Sven Sager, License: LGPLv3 +# +u"""Alte Klassen laden hier, bevor sie entsorgt werden.""" +import tkinter +import tkinter.messagebox as tkmsg +from mytools import gettrans + +# Übersetzung laden +_ = gettrans() + + +class RevPiOption(tkinter.Frame): + + u"""Optionen für RevPiPyload vor 0.6.0.""" + + def __init__(self, master, xmlcli): + u"""Init RevPiOption-Class. + @return None""" + try: + self.dc = xmlcli.get_config() + except: + self.dc = None + return None + + super().__init__(master) + self.master.bind("", self._checkclose) + self.master.protocol("WM_DELETE_WINDOW", self._checkclose) + self.pack(expand=True, fill="both") + + self.xmlcli = xmlcli + self.mrk_var_xmlmod2 = False + self.mrk_var_xmlmod3 = False + self.mrk_xmlmodask = False + self.dorestart = False + + # Fenster bauen + self._createwidgets() + self._loadappdata() + + def _changesdone(self): + u"""Prüft ob sich die Einstellungen geändert haben. + @return True, wenn min. eine Einstellung geändert wurde""" + return ( + self.var_start.get() != self.dc.get("autostart", "1") + or self.var_reload.get() != self.dc.get("autoreload", "1") + or self.var_zexit.get() != self.dc.get("zeroonexit", "0") + or self.var_zerr.get() != self.dc.get("zeroonerror", "0") + or self.var_startpy.get() != self.dc.get("plcprogram", "none.py") + or self.var_startargs.get() != self.dc.get("plcarguments", "") + or self.var_pythonver.get() != self.dc.get("pythonversion", "3") + or self.var_slave.get() != self.dc.get("plcslave", "0") + or self.var_xmlon.get() != (self.dc.get("xmlrpc", 0) >= 1) + or self.var_xmlmod2.get() != (self.dc.get("xmlrpc", 0) >= 2) + or self.var_xmlmod3.get() != (self.dc.get("xmlrpc", 0) >= 3) + # or self.var_xmlport.get() != self.dc.get("xmlrpcport", "55123") + ) + + def _checkclose(self, event=None): + u"""Prüft ob Fenster beendet werden soll. + @param event tkinter-Event""" + ask = True + if self._changesdone(): + ask = tkmsg.askyesno( + _("Question"), + _("Do you really want to quit? \nUnsaved changes will " + "be lost"), + parent=self.master, default="no" + ) + + if ask: + self.master.destroy() + + def _createwidgets(self): + u"""Erstellt Widgets.""" + self.master.wm_title(_("RevPi Python PLC Options")) + self.master.wm_resizable(width=False, height=False) + + xmlstate = "normal" if self.dc["xmlrpc"] >= 3 else "disabled" + + cpadw = {"padx": 4, "pady": 2, "sticky": "w"} + cpadwe = {"padx": 4, "pady": 2, "sticky": "we"} + + # Gruppe Start/Stop + stst = tkinter.LabelFrame(self) + stst["text"] = _("Start / Stop behavior") + stst.grid(columnspan=2, pady=2, sticky="we") + + self.var_start = tkinter.BooleanVar(stst) + self.var_reload = tkinter.BooleanVar(stst) + self.var_zexit = tkinter.BooleanVar(stst) + self.var_zerr = tkinter.BooleanVar(stst) + + ckb_start = tkinter.Checkbutton(stst) + ckb_start["text"] = _("Start program automatically") + ckb_start["state"] = xmlstate + ckb_start["variable"] = self.var_start + ckb_start.grid(**cpadw) + + ckb_reload = tkinter.Checkbutton(stst) + ckb_reload["text"] = _("Restart program after exit") + ckb_reload["state"] = xmlstate + ckb_reload["variable"] = self.var_reload + ckb_reload.grid(**cpadw) + + lbl = tkinter.Label(stst) + lbl["text"] = _("Set process image to NULL if program terminates...") + lbl.grid(**cpadw) + + ckb_zexit = tkinter.Checkbutton(stst, justify="left") + ckb_zexit["state"] = xmlstate + ckb_zexit["text"] = _("... successfully") + ckb_zexit["variable"] = self.var_zexit + ckb_zexit.grid(**cpadw) + + ckb_zerr = tkinter.Checkbutton(stst, justify="left") + ckb_zerr["state"] = xmlstate + ckb_zerr["text"] = _("... with errors") + ckb_zerr["variable"] = self.var_zerr + ckb_zerr.grid(**cpadw) + + # Gruppe Programm + prog = tkinter.LabelFrame(self) + prog["text"] = _("PLC program") + prog.grid(columnspan=2, pady=2, sticky="we") + + self.var_pythonver = tkinter.IntVar(prog) + self.var_startpy = tkinter.StringVar(prog) + self.var_startargs = tkinter.StringVar(prog) + self.var_slave = tkinter.BooleanVar(prog) + + self.var_pythonver.set(3) + + lbl = tkinter.Label(prog) + lbl["text"] = _("Python version") + lbl.grid(columnspan=2, row=0, **cpadw) + + rbn = tkinter.Radiobutton(prog) + rbn["state"] = xmlstate + rbn["text"] = "Python2" + rbn["value"] = 2 + rbn["variable"] = self.var_pythonver + rbn.grid(column=0, row=1, **cpadw) + + rbn = tkinter.Radiobutton(prog) + rbn["state"] = xmlstate + rbn["text"] = "Python3" + rbn["value"] = 3 + rbn["variable"] = self.var_pythonver + rbn.grid(column=1, row=1, **cpadw) + + # Row 2 + lbl = tkinter.Label(prog) + lbl["text"] = _("Python PLC program name") + lbl.grid(columnspan=2, **cpadw) + + # Row 3 + lst = self.xmlcli.get_filelist() + if len(lst) == 0: + lst.append("none") + opt_startpy = tkinter.OptionMenu( + prog, self.var_startpy, *lst + ) + opt_startpy["state"] = xmlstate + opt_startpy.grid(columnspan=2, **cpadwe) + + # Row 4 + lbl = tkinter.Label(prog) + lbl["text"] = _("Program arguments") + lbl.grid(columnspan=2, **cpadw) + + # Row 5 + txt = tkinter.Entry(prog) + txt["textvariable"] = self.var_startargs + txt.grid(columnspan=2, **cpadw) + + # Row 6 + ckb_slave = tkinter.Checkbutton(prog, justify="left") + ckb_slave["state"] = xmlstate + ckb_slave["text"] = _("Use RevPi as PLC-Slave") + ckb_slave["variable"] = self.var_slave + ckb_slave.grid(column=0, **cpadw) + + # Gruppe XMLRPC + xmlrpc = tkinter.LabelFrame(self) + xmlrpc["text"] = _("XML-RPC server") + xmlrpc.grid(columnspan=2, pady=2, sticky="we") + + self.var_xmlon = tkinter.BooleanVar(xmlrpc) + self.var_xmlmod2 = tkinter.BooleanVar(xmlrpc) + self.var_xmlmod3 = tkinter.BooleanVar(xmlrpc) +# self.var_xmlport = tkinter.StringVar(xmlrpc) +# self.var_xmlport.set("55123") + + ckb_xmlon = tkinter.Checkbutton(xmlrpc) + ckb_xmlon["command"] = self.askxmlon + ckb_xmlon["state"] = xmlstate + ckb_xmlon["text"] = _("Activate XML-RPC server on RevPi") + ckb_xmlon["variable"] = self.var_xmlon + ckb_xmlon.grid(**cpadw) + + self.ckb_xmlmod2 = tkinter.Checkbutton(xmlrpc, justify="left") + self.ckb_xmlmod2["command"] = self.xmlmod2_tail + self.ckb_xmlmod2["state"] = xmlstate + self.ckb_xmlmod2["text"] = \ + _("Allow download of piCtory configuration and\nPLC programm") + self.ckb_xmlmod2["variable"] = self.var_xmlmod2 + self.ckb_xmlmod2.grid(**cpadw) + + self.ckb_xmlmod3 = tkinter.Checkbutton(xmlrpc, justify="left") + self.ckb_xmlmod3["state"] = xmlstate + self.ckb_xmlmod3["text"] = \ + _("Allow upload of piCtory configuration and\nPLC programm") + self.ckb_xmlmod3["variable"] = self.var_xmlmod3 + self.ckb_xmlmod3.grid(**cpadw) + + lbl = tkinter.Label(xmlrpc) + lbl["text"] = _("XML-RPC server port") + lbl.grid(**cpadw) + +# spb_xmlport = tkinter.Spinbox(xmlrpc) +# spb_xmlport["to"] = 65535 +# spb_xmlport["from"] = 1024 +# spb_xmlport["state"] = xmlstate +# spb_xmlport["textvariable"] = self.var_xmlport +# spb_xmlport.grid(**cpadwe) + + # Buttons + btn_save = tkinter.Button(self) + btn_save["command"] = self._setappdata + btn_save["state"] = xmlstate + btn_save["text"] = _("Save") + btn_save.grid(column=0, row=3) + + btn_close = tkinter.Button(self) + btn_close["command"] = self._checkclose + btn_close["text"] = _("Close") + btn_close.grid(column=1, row=3) + + def _loadappdata(self, refresh=False): + u"""Läd aktuelle Einstellungen vom RevPi. + @param refresh Wenn True, werden Einstellungen heruntergeladen.""" + if refresh: + self.dc = self.xmlcli.get_config() + + self.var_start.set(self.dc.get("autostart", "1")) + self.var_reload.set(self.dc.get("autoreload", "1")) + self.var_zexit.set(self.dc.get("zeroonexit", "0")) + self.var_zerr.set(self.dc.get("zeroonerror", "0")) + + self.var_startpy.set(self.dc.get("plcprogram", "none.py")) + self.var_startargs.set(self.dc.get("plcarguments", "")) + self.var_pythonver.set(self.dc.get("pythonversion", "3")) + self.var_slave.set(self.dc.get("plcslave", "0")) + + self.var_xmlon.set(self.dc.get("xmlrpc", 0) >= 1) + self.var_xmlmod2.set(self.dc.get("xmlrpc", 0) >= 2) + self.mrk_var_xmlmod2 = self.var_xmlmod2.get() + self.var_xmlmod3.set(self.dc.get("xmlrpc", 0) >= 3) + self.mrk_var_xmlmod3 = self.var_xmlmod3.get() + +# self.var_xmlport.set(self.dc.get("xmlrpcport", "55123")) + + def _setappdata(self): + u"""Speichert geänderte Einstellungen auf RevPi. + @return None""" + + if not self._changesdone(): + tkmsg.showinfo( + _("Information"), + _("You have not made any changes to save."), + ) + self._checkclose() + return None + + ask = tkmsg.askyesnocancel( + _("Question"), + _("The settings are now saved on the Revolution Pi. \n\n" + "Should the new settings take effect immediately? \nThis " + "means a restart of the service and the PLC program!"), + parent=self.master + ) + if ask is not None: + self.dc["autostart"] = int(self.var_start.get()) + self.dc["autoreload"] = int(self.var_reload.get()) + self.dc["zeroonexit"] = int(self.var_zexit.get()) + self.dc["zeroonerror"] = int(self.var_zerr.get()) + + self.dc["plcprogram"] = self.var_startpy.get() + self.dc["plcarguments"] = self.var_startargs.get() + self.dc["pythonversion"] = self.var_pythonver.get() + self.dc["plcslave"] = int(self.var_slave.get()) + + self.dc["xmlrpc"] = 0 + if self.var_xmlon.get(): + self.dc["xmlrpc"] += 1 + if self.var_xmlmod2.get(): + self.dc["xmlrpc"] += 1 + if self.var_xmlmod3.get(): + self.dc["xmlrpc"] += 1 + +# self.dc["xmlrpcport"] = self.var_xmlport.get() + + if self.xmlcli.set_config(self.dc, ask): + tkmsg.showinfo( + _("Information"), + _("Settings saved"), + parent=self.master + ) + self.dorestart = ask + self._checkclose() + else: + tkmsg.showerror( + _("Error"), + _("The settings could not be saved. This can happen if " + "values are wrong!"), + parent=self.master + ) + + def askxmlon(self): + u"""Fragt Nuter, ob wirklicht abgeschaltet werden soll.""" + if not (self.var_xmlon.get() or self.mrk_xmlmodask): + self.mrk_xmlmodask = tkmsg.askyesno( + _("Question"), + _("Are you sure you want to deactivate the XML-RPC server? " + "You will NOT be able to access the Revolution Pi with " + "this program."), + parent=self.master + ) + if not self.mrk_xmlmodask: + self.var_xmlon.set(True) + + self.xmlmod_tail() + + def xmlmod_tail(self): + u"""Passt XML-Optionszugriff an.""" + if self.var_xmlon.get(): + self.var_xmlmod2.set(self.mrk_var_xmlmod2) + self.ckb_xmlmod2["state"] = "normal" + else: + self.mrk_var_xmlmod2 = self.var_xmlmod2.get() + self.var_xmlmod2.set(False) + self.ckb_xmlmod2["state"] = "disabled" + self.xmlmod2_tail() + + def xmlmod2_tail(self): + u"""Passt XML-Optionszugriff an.""" + if self.var_xmlmod2.get(): + self.var_xmlmod3.set(self.mrk_var_xmlmod3) + self.ckb_xmlmod3["state"] = "normal" + else: + self.mrk_var_xmlmod3 = self.var_xmlmod3.get() + self.var_xmlmod3.set(False) + self.ckb_xmlmod3["state"] = "disabled" diff --git a/revpipycontrol/revpilogfile.py b/revpipycontrol/revpilogfile.py index 5595303..5666b4b 100644 --- a/revpipycontrol/revpilogfile.py +++ b/revpipycontrol/revpilogfile.py @@ -1,10 +1,11 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +u"""Zeigt die Logfiles an.""" import tkinter from mytools import gettrans @@ -14,6 +15,8 @@ _ = gettrans() class RevPiLogfile(tkinter.Frame): + u"""Baut Fenster für Logfiles.""" + def __init__(self, master, xmlcli): u"""Init RevPiLogfile-Class.""" super().__init__(master) @@ -57,6 +60,7 @@ class RevPiLogfile(tkinter.Frame): self.btnapplog["command"] = self.btn_clearplc self.btnapplog["text"] = _("Clear screen") self.btnapplog.grid(column=1, row=0, sticky="e") + self.plclog = tkinter.Text(self) self.plcscr = tkinter.Scrollbar(self) self.plclog.grid(sticky="wnse", columnspan=2, column=0, row=1) @@ -72,6 +76,7 @@ class RevPiLogfile(tkinter.Frame): self.btnapplog["command"] = self.btn_clearapp self.btnapplog["text"] = _("Clear screen") self.btnapplog.grid(column=4, row=0, sticky="e") + self.applog = tkinter.Text(self) self.appscr = tkinter.Scrollbar(self) self.applog.grid(sticky="nesw", columnspan=2, column=3, row=1) @@ -150,13 +155,13 @@ class RevPiLogfile(tkinter.Frame): if full: textwidget.delete(1.0, tkinter.END) - if bytebuff == b'\x16': #  + if bytebuff == b'\x16': # 'ESC' # Kein Zugriff auf Logdatei textwidget.delete(1.0, tkinter.END) textwidget.insert( tkinter.END, _("Can not access log file on the RevPi") ) - elif bytebuff == b'\x19': #  + elif bytebuff == b'\x19': # 'EndOfMedia' # Logdatei neu begonnen startposition = 0 else: diff --git a/revpipycontrol/revpioption.py b/revpipycontrol/revpioption.py index c7bfd1d..4726293 100644 --- a/revpipycontrol/revpioption.py +++ b/revpipycontrol/revpioption.py @@ -1,12 +1,14 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +u"""Optionsfenster.""" import tkinter import tkinter.messagebox as tkmsg +from aclmanager import AclManager from mytools import gettrans # Übersetzung laden @@ -15,6 +17,8 @@ _ = gettrans() class RevPiOption(tkinter.Frame): + u"""Zeigt Optionen von RevPiPyLoad an.""" + def __init__(self, master, xmlcli): u"""Init RevPiOption-Class. @return None""" @@ -29,9 +33,10 @@ class RevPiOption(tkinter.Frame): self.master.protocol("WM_DELETE_WINDOW", self._checkclose) self.pack(expand=True, fill="both") + # XML-RPC Server konfigurieren self.xmlcli = xmlcli - self.mrk_var_xmlmod2 = False - self.mrk_var_xmlmod3 = False + self.xmlmodus = self.xmlcli.xmlmodus() + self.mrk_xmlmodask = False self.dorestart = False @@ -43,18 +48,20 @@ class RevPiOption(tkinter.Frame): u"""Prüft ob sich die Einstellungen geändert haben. @return True, wenn min. eine Einstellung geändert wurde""" return ( - self.var_start.get() != self.dc.get("autostart", "1") - or self.var_reload.get() != self.dc.get("autoreload", "1") - or self.var_zexit.get() != self.dc.get("zeroonexit", "0") - or self.var_zerr.get() != self.dc.get("zeroonerror", "0") + self.var_start.get() != self.dc.get("autostart", 1) + or self.var_reload.get() != self.dc.get("autoreload", 1) + or self.var_reload_delay.get() != + str(self.dc.get("autoreloaddelay", 5)) + or self.var_zexit.get() != self.dc.get("zeroonexit", 0) + or self.var_zerr.get() != self.dc.get("zeroonerror", 0) + # TODO: rtlevel (0) or self.var_startpy.get() != self.dc.get("plcprogram", "none.py") or self.var_startargs.get() != self.dc.get("plcarguments", "") - or self.var_pythonver.get() != self.dc.get("pythonversion", "3") - or self.var_slave.get() != self.dc.get("plcslave", "0") - or self.var_xmlon.get() != (self.dc.get("xmlrpc", 0) >= 1) - or self.var_xmlmod2.get() != (self.dc.get("xmlrpc", 0) >= 2) - or self.var_xmlmod3.get() != (self.dc.get("xmlrpc", 0) >= 3) - or self.var_xmlport.get() != self.dc.get("xmlrpcport", "55123") + or self.var_pythonver.get() != self.dc.get("pythonversion", 3) + or self.var_slave.get() != self.dc.get("plcslave", 0) + or self.var_slaveacl.get() != self.dc.get("plcslaveacl", "") + or self.var_xmlon.get() != self.dc.get("xmlrpc", 0) + or self.var_xmlacl.get() != self.dc.get("xmlrpcacl", "") ) def _checkclose(self, event=None): @@ -72,13 +79,27 @@ class RevPiOption(tkinter.Frame): if ask: self.master.destroy() + def _checkvalues(self): + u"""Prüft alle Werte auf Gültigkeit. + @return True, wenn alle Werte gültig sind""" + if not self.var_reload_delay.get().isdigit(): + tkmsg.showerror( + _("Error"), + _("The value of 'restart delay' ist not valid."), + parent=self.master + ) + return False + + return True + def _createwidgets(self): u"""Erstellt Widgets.""" self.master.wm_title(_("RevPi Python PLC Options")) self.master.wm_resizable(width=False, height=False) - xmlstate = "normal" if self.dc["xmlrpc"] >= 3 else "disabled" + xmlstate = "normal" if self.xmlmodus >= 4 else "disabled" + cpade = {"padx": 4, "pady": 2, "sticky": "e"} cpadw = {"padx": 4, "pady": 2, "sticky": "w"} cpadwe = {"padx": 4, "pady": 2, "sticky": "we"} @@ -89,6 +110,7 @@ class RevPiOption(tkinter.Frame): self.var_start = tkinter.BooleanVar(stst) self.var_reload = tkinter.BooleanVar(stst) + self.var_reload_delay = tkinter.StringVar(stst) self.var_zexit = tkinter.BooleanVar(stst) self.var_zerr = tkinter.BooleanVar(stst) @@ -96,51 +118,64 @@ class RevPiOption(tkinter.Frame): ckb_start["text"] = _("Start program automatically") ckb_start["state"] = xmlstate ckb_start["variable"] = self.var_start - ckb_start.grid(**cpadw) + ckb_start.grid(columnspan=2, **cpadw) ckb_reload = tkinter.Checkbutton(stst) ckb_reload["text"] = _("Restart program after exit") ckb_reload["state"] = xmlstate ckb_reload["variable"] = self.var_reload - ckb_reload.grid(**cpadw) + ckb_reload.grid(columnspan=2, **cpadw) + + lbl = tkinter.Label(stst) + lbl["text"] = _("Restart after n seconds of delay") + lbl.grid(**cpadw) + sbx = tkinter.Spinbox(stst) + sbx["to"] = 60 + sbx["from_"] = 5 + sbx["textvariable"] = self.var_reload_delay + sbx["width"] = 4 + sbx.grid(column=1, row=2, **cpade) + + lbl = tkinter.Label(stst) + lbl["text"] = _("Set process image to NULL if program terminates...") + lbl.grid(columnspan=2, **cpadw) ckb_zexit = tkinter.Checkbutton(stst, justify="left") ckb_zexit["state"] = xmlstate - ckb_zexit["text"] = _( - "Set process image to NULL if program\n" - "terminates successfully") + ckb_zexit["text"] = _("... successfully") ckb_zexit["variable"] = self.var_zexit ckb_zexit.grid(**cpadw) ckb_zerr = tkinter.Checkbutton(stst, justify="left") ckb_zerr["state"] = xmlstate - ckb_zerr["text"] = _( - "Set process image to NULL if program\n" - "terminates with errors") + ckb_zerr["text"] = _("... with errors") ckb_zerr["variable"] = self.var_zerr ckb_zerr.grid(**cpadw) # Gruppe Programm prog = tkinter.LabelFrame(self) + prog.columnconfigure(0, weight=1) + prog.columnconfigure(1, weight=1) prog["text"] = _("PLC program") prog.grid(columnspan=2, pady=2, sticky="we") self.var_pythonver = tkinter.IntVar(prog) self.var_startpy = tkinter.StringVar(prog) self.var_startargs = tkinter.StringVar(prog) - self.var_slave = tkinter.BooleanVar(prog) + self.var_slaveacl = tkinter.StringVar(prog) self.var_pythonver.set(3) lbl = tkinter.Label(prog) - lbl["text"] = _("Python version") + lbl["text"] = _("Python version") + ":" lbl.grid(columnspan=2, row=0, **cpadw) + rbn = tkinter.Radiobutton(prog) rbn["state"] = xmlstate rbn["text"] = "Python2" rbn["value"] = 2 rbn["variable"] = self.var_pythonver - rbn.grid(column=0, row=1, **cpadw) + rbn.grid(column=0, row=1, **cpade) rbn = tkinter.Radiobutton(prog) rbn["state"] = xmlstate @@ -149,78 +184,79 @@ class RevPiOption(tkinter.Frame): rbn["variable"] = self.var_pythonver rbn.grid(column=1, row=1, **cpadw) + # Row 2 lbl = tkinter.Label(prog) lbl["text"] = _("Python PLC program name") lbl.grid(columnspan=2, **cpadw) + # Row 3 lst = self.xmlcli.get_filelist() if len(lst) == 0: lst.append("none") opt_startpy = tkinter.OptionMenu( - prog, self.var_startpy, *lst) + prog, self.var_startpy, *lst + ) opt_startpy["state"] = xmlstate opt_startpy.grid(columnspan=2, **cpadwe) + # Row 4 lbl = tkinter.Label(prog) lbl["text"] = _("Program arguments") lbl.grid(columnspan=2, **cpadw) + # Row 5 txt = tkinter.Entry(prog) txt["textvariable"] = self.var_startargs txt.grid(columnspan=2, **cpadw) - ckb_slave = tkinter.Checkbutton(prog, justify="left") + # Gruppe Services + services = tkinter.LabelFrame(self) + services["text"] = _("RevPiPyLoad server services") + services.grid(columnspan=2, pady=2, sticky="we") + + self.var_slave = tkinter.BooleanVar(services) + self.var_xmlon = tkinter.BooleanVar(services) + self.var_xmlacl = tkinter.StringVar(services) + + # RevPiSlave Service + row = 0 + ckb_slave = tkinter.Checkbutton(services, justify="left") ckb_slave["state"] = xmlstate ckb_slave["text"] = _("Use RevPi as PLC-Slave") - ckb_slave["state"] = "disabled" ckb_slave["variable"] = self.var_slave - ckb_slave.grid(columnspan=2, **cpadw) + ckb_slave.grid(column=0, **cpadw) - # Gruppe XMLRPC - xmlrpc = tkinter.LabelFrame(self) - xmlrpc["text"] = _("XML-RPC server") - xmlrpc.grid(columnspan=2, pady=2, sticky="we") + btn_slaveacl = tkinter.Button(services, justify="center") + btn_slaveacl["command"] = self.btn_slaveacl + btn_slaveacl["text"] = _("Edit ACL") + btn_slaveacl.grid(column=1, row=row, **cpade) - self.var_xmlon = tkinter.BooleanVar(xmlrpc) - self.var_xmlmod2 = tkinter.BooleanVar(xmlrpc) - self.var_xmlmod3 = tkinter.BooleanVar(xmlrpc) - self.var_xmlport = tkinter.StringVar(xmlrpc) - self.var_xmlport.set("55123") + row = 1 + lbl = tkinter.Label(services) + lbl["text"] = _("RevPi-Slave service is:") + lbl.grid(column=0, **cpade) - ckb_xmlon = tkinter.Checkbutton(xmlrpc) + status = self.xmlcli.plcslaverunning() + lbl = tkinter.Label(services) + lbl["fg"] = "green" if status else "red" + lbl["text"] = _("running") if status else _("stopped") + lbl.grid(column=1, row=row, **cpadwe) + + # XML-RPC Service + row = 2 + ckb_xmlon = tkinter.Checkbutton(services) ckb_xmlon["command"] = self.askxmlon ckb_xmlon["state"] = xmlstate ckb_xmlon["text"] = _("Activate XML-RPC server on RevPi") ckb_xmlon["variable"] = self.var_xmlon ckb_xmlon.grid(**cpadw) - self.ckb_xmlmod2 = tkinter.Checkbutton(xmlrpc, justify="left") - self.ckb_xmlmod2["command"] = self.xmlmod2_tail - self.ckb_xmlmod2["state"] = xmlstate - self.ckb_xmlmod2["text"] = \ - _("Allow download of piCtory configuration and\nPLC programm") - self.ckb_xmlmod2["variable"] = self.var_xmlmod2 - self.ckb_xmlmod2.grid(**cpadw) + btn_slaveacl = tkinter.Button(services, justify="center") + btn_slaveacl["command"] = self.btn_xmlacl + btn_slaveacl["text"] = _("Edit ACL") + btn_slaveacl.grid(column=1, row=row, **cpade) - self.ckb_xmlmod3 = tkinter.Checkbutton(xmlrpc, justify="left") - self.ckb_xmlmod3["state"] = xmlstate - self.ckb_xmlmod3["text"] = \ - _("Allow upload of piCtory configuration and\nPLC programm") - self.ckb_xmlmod3["variable"] = self.var_xmlmod3 - self.ckb_xmlmod3.grid(**cpadw) - - lbl = tkinter.Label(xmlrpc) - lbl["text"] = _("XML-RPC server port") - lbl.grid(**cpadw) - - spb_xmlport = tkinter.Spinbox(xmlrpc) - spb_xmlport["to"] = 65535 - spb_xmlport["from"] = 1024 - spb_xmlport["state"] = xmlstate - spb_xmlport["textvariable"] = self.var_xmlport - spb_xmlport.grid(**cpadwe) - - # Buttons + # Buttons am Ende btn_save = tkinter.Button(self) btn_save["command"] = self._setappdata btn_save["state"] = xmlstate @@ -238,23 +274,22 @@ class RevPiOption(tkinter.Frame): if refresh: self.dc = self.xmlcli.get_config() - self.var_start.set(self.dc.get("autostart", "1")) - self.var_reload.set(self.dc.get("autoreload", "1")) - self.var_zexit.set(self.dc.get("zeroonexit", "0")) - self.var_zerr.set(self.dc.get("zeroonerror", "0")) + self.var_start.set(self.dc.get("autostart", 1)) + self.var_reload.set(self.dc.get("autoreload", 1)) + self.var_reload_delay.set(self.dc.get("autoreloaddelay", 5)) + self.var_zexit.set(self.dc.get("zeroonexit", 0)) + self.var_zerr.set(self.dc.get("zeroonerror", 0)) + # TODO: rtlevel (0) self.var_startpy.set(self.dc.get("plcprogram", "none.py")) self.var_startargs.set(self.dc.get("plcarguments", "")) - self.var_pythonver.set(self.dc.get("pythonversion", "3")) - self.var_slave.set(self.dc.get("plcslave", "0")) + self.var_pythonver.set(self.dc.get("pythonversion", 3)) - self.var_xmlon.set(self.dc.get("xmlrpc", 0) >= 1) - self.var_xmlmod2.set(self.dc.get("xmlrpc", 0) >= 2) - self.mrk_var_xmlmod2 = self.var_xmlmod2.get() - self.var_xmlmod3.set(self.dc.get("xmlrpc", 0) >= 3) - self.mrk_var_xmlmod3 = self.var_xmlmod3.get() + self.var_slave.set(self.dc.get("plcslave", 0)) + self.var_slaveacl.set(self.dc.get("plcslaveacl", "")) - self.var_xmlport.set(self.dc.get("xmlrpcport", "55123")) + self.var_xmlon.set(self.dc.get("xmlrpc", 0)) + self.var_xmlacl.set(self.dc.get("xmlrpcacl", "")) def _setappdata(self): u"""Speichert geänderte Einstellungen auf RevPi. @@ -268,33 +303,41 @@ class RevPiOption(tkinter.Frame): self._checkclose() return None - ask = tkmsg.askyesnocancel( + # Gültigkeitsprüfung + if not self._checkvalues(): + return None + +# ask = tkmsg.askyesnocancel( +# _("Question"), +# _("The settings are now saved on the Revolution Pi. \n\n" +# "Should the new settings take effect immediately? \nThis " +# "means a restart of the service and the PLC program!"), +# parent=self.master +# ) + ask = tkmsg.askokcancel( _("Question"), - _("The settings are now saved on the Revolution Pi. \n\n" - "Should the new settings take effect immediately? \nThis " - "means a restart of the service and the PLC program!"), + _("The settings will be set on the Revolution Pi now. \n\n" + "If you made changes on the 'PCL Program' section, your plc " + "program will restart! \n" + "ACL changes and service settings are applied immediately."), parent=self.master ) - if ask is not None: - self.dc["autostart"] = int(self.var_start.get()) + if ask: self.dc["autoreload"] = int(self.var_reload.get()) - self.dc["zeroonexit"] = int(self.var_zexit.get()) - self.dc["zeroonerror"] = int(self.var_zerr.get()) - + self.dc["autoreloaddelay"] = int(self.var_reload_delay.get()) + self.dc["autostart"] = int(self.var_start.get()) self.dc["plcprogram"] = self.var_startpy.get() self.dc["plcarguments"] = self.var_startargs.get() self.dc["pythonversion"] = self.var_pythonver.get() + # TODO: rtlevel (0) + self.dc["zeroonerror"] = int(self.var_zerr.get()) + self.dc["zeroonexit"] = int(self.var_zexit.get()) + self.dc["plcslave"] = int(self.var_slave.get()) + self.dc["plcslaveacl"] = self.var_slaveacl.get() - self.dc["xmlrpc"] = 0 - if self.var_xmlon.get(): - self.dc["xmlrpc"] += 1 - if self.var_xmlmod2.get(): - self.dc["xmlrpc"] += 1 - if self.var_xmlmod3.get(): - self.dc["xmlrpc"] += 1 - - self.dc["xmlrpcport"] = self.var_xmlport.get() + self.dc["xmlrpc"] = int(self.var_xmlon.get()) + self.dc["xmlrpcacl"] = self.var_xmlacl.get() if self.xmlcli.set_config(self.dc, ask): tkmsg.showinfo( @@ -325,25 +368,39 @@ class RevPiOption(tkinter.Frame): if not self.mrk_xmlmodask: self.var_xmlon.set(True) - self.xmlmod_tail() + def btn_slaveacl(self): + u"""Öffnet Fenster für ACL-Verwaltung.""" + win = tkinter.Toplevel(self) + win.focus_set() + win.grab_set() + slaveacl = AclManager( + win, 0, 1, + self.var_slaveacl.get(), + readonly=self.xmlmodus < 4 + ) + slaveacl.acltext = { + 0: _("read only"), + 1: _("read and write") + } + self.wait_window(win) + self.var_slaveacl.set(slaveacl.acl) - def xmlmod_tail(self): - u"""Passt XML-Optionszugriff an.""" - if self.var_xmlon.get(): - self.var_xmlmod2.set(self.mrk_var_xmlmod2) - self.ckb_xmlmod2["state"] = "normal" - else: - self.mrk_var_xmlmod2 = self.var_xmlmod2.get() - self.var_xmlmod2.set(False) - self.ckb_xmlmod2["state"] = "disabled" - self.xmlmod2_tail() - - def xmlmod2_tail(self): - u"""Passt XML-Optionszugriff an.""" - if self.var_xmlmod2.get(): - self.var_xmlmod3.set(self.mrk_var_xmlmod3) - self.ckb_xmlmod3["state"] = "normal" - else: - self.mrk_var_xmlmod3 = self.var_xmlmod3.get() - self.var_xmlmod3.set(False) - self.ckb_xmlmod3["state"] = "disabled" + def btn_xmlacl(self): + u"""Öffnet Fenster für ACL-Verwaltung.""" + win = tkinter.Toplevel(self) + win.focus_set() + win.grab_set() + slaveacl = AclManager( + win, 0, 4, + self.var_xmlacl.get(), + readonly=self.xmlmodus < 4 + ) + slaveacl.acltext = { + 0: _("Start/Stop PLC program and read logs"), + 1: _("+ read IOs in watch modus"), + 2: _("+ read properties and download PLC program"), + 3: _("+ upload PLC program"), + 4: _("+ set properties") + } + self.wait_window(win) + self.var_xmlacl.set(slaveacl.acl) diff --git a/revpipycontrol/revpiplclist.py b/revpipycontrol/revpiplclist.py index 8c59b6a..29900fd 100644 --- a/revpipycontrol/revpiplclist.py +++ b/revpipycontrol/revpiplclist.py @@ -1,36 +1,32 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +u"""Fenster um RevPi-Verbindungen einzurichten.""" import os.path import pickle import tkinter import tkinter.messagebox as tkmsg from mytools import gettrans -from os import environ +from mytools import savefile_connections as savefile +from revpiprogram import _loaddefaults as programloaddefaults +from revpiprogram import _savedefaults as programsavedefaults from os import makedirs -from sys import platform + # Übersetzungen laden _ = gettrans() -# Systemwerte -if platform == "linux": - homedir = environ["HOME"] -else: - homedir = environ["APPDATA"] -savefile = os.path.join(homedir, ".revpipyplc", "connections.dat") - def get_connections(): u"""Verbindungen aus Datei laden. @return dict() mit Verbindungen""" if os.path.exists(savefile): - fh = open(savefile, "rb") - connections = pickle.load(fh) + with open(savefile, "rb") as fh: + connections = pickle.load(fh) return connections else: return {} @@ -38,6 +34,8 @@ def get_connections(): class RevPiPlcList(tkinter.Frame): + u"""TK Fenster.""" + def __init__(self, master): u"""Init RevPiPlcList-class. @param master tkinter master""" @@ -92,11 +90,13 @@ class RevPiPlcList(tkinter.Frame): # Eingabefelder für Adresse und Namen tkinter.Label(self, text=_("Name")).grid( - column=2, row=0, sticky="wn", padx=5, pady=5) + column=2, row=0, sticky="wn", padx=5, pady=5 + ) 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 address")).grid( column=2, row=1, sticky="wn", padx=5, pady=5 @@ -104,34 +104,42 @@ class RevPiPlcList(tkinter.Frame): self.txt_address = tkinter.Entry(self, textvariable=self.var_address) self.txt_address.bind("", self.evt_keypress) self.txt_address.grid( - column=3, row=1, columnspan=3, sticky="n", padx=5, pady=5) + column=3, row=1, columnspan=3, sticky="n", padx=5, pady=5 + ) tkinter.Label(self, text=_("Port")).grid( - column=2, row=2, sticky="wn", padx=5, pady=5) + column=2, row=2, sticky="wn", padx=5, pady=5 + ) self.txt_port = tkinter.Entry(self, textvariable=self.var_port) self.txt_port.bind("", self.evt_keypress) self.txt_port.grid( - column=3, row=2, columnspan=3, sticky="n", padx=5, pady=5) + column=3, row=2, columnspan=3, sticky="n", padx=5, pady=5 + ) # Listenbutton self.btn_new = tkinter.Button( - self, text=_("New"), command=self.evt_btnnew) + self, text=_("New"), command=self.evt_btnnew + ) self.btn_new.grid(column=2, row=3, sticky="s") self.btn_add = tkinter.Button( - self, text=_("Apply"), command=self.evt_btnadd, - state="disabled") + self, text=_("Apply"), + command=self.evt_btnadd, state="disabled" + ) self.btn_add.grid(column=3, row=3, sticky="s") self.btn_remove = tkinter.Button( - self, text=_("Remove"), command=self.evt_btnremove, - state="disabled") + self, text=_("Remove"), + command=self.evt_btnremove, state="disabled" + ) self.btn_remove.grid(column=4, row=3, sticky="s") # Fensterbuttons self.btn_save = tkinter.Button( - self, text=_("Save"), command=self.evt_btnsave) + self, text=_("Save"), command=self.evt_btnsave + ) self.btn_save.grid(column=3, row=9, sticky="se") self.btn_close = tkinter.Button( - self, text=_("Close"), command=self._checkclose) + self, text=_("Close"), command=self._checkclose + ) self.btn_close.grid(column=4, row=9, sticky="se") def _saveappdata(self): @@ -139,11 +147,19 @@ class RevPiPlcList(tkinter.Frame): @return True, bei erfolgreicher Verarbeitung""" try: makedirs(os.path.dirname(savefile), exist_ok=True) - fh = open(savefile, "wb") - pickle.dump(self._connections, fh) + with open(savefile, "wb") as fh: + pickle.dump(self._connections, fh) self.changes = False except: return False + + # Andere Einstellungen aufräumen + dict = programloaddefaults() + for revpi in tuple(dict.keys()): + if revpi not in self._connections: + del dict[revpi] + programsavedefaults(None, dict) + return True def build_listconn(self): @@ -220,7 +236,6 @@ class RevPiPlcList(tkinter.Frame): self.var_port.set(self._connections[item][1]) self.btn_add["state"] == "normal" - self.btn_remove["state"] = "normal" else: self.btn_remove["state"] = "disabled" diff --git a/revpipycontrol/revpiprogram.py b/revpipycontrol/revpiprogram.py index 4eea90e..5c19f3e 100644 --- a/revpipycontrol/revpiprogram.py +++ b/revpipycontrol/revpiprogram.py @@ -1,10 +1,11 @@ +# -*- coding: utf-8 -*- # # RevPiPyControl # # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +u"""PLC Programm und Konfig hoch und runterladen.""" import gzip import os import pickle @@ -14,26 +15,56 @@ import tkinter.filedialog as tkfd import tkinter.messagebox as tkmsg import zipfile from mytools import gettrans -from os import environ +from mytools import savefile_programpath as savefile from os import makedirs from shutil import rmtree -from sys import platform from tempfile import mkstemp, mkdtemp from xmlrpc.client import Binary # Übersetzung laden _ = gettrans() -# Systemwerte -if platform == "linux": - homedir = environ["HOME"] -else: - homedir = environ["APPDATA"] -savefile = os.path.join(homedir, ".revpipyplc", "programpath.dat") + +def _loaddefaults(revpiname=None): + u"""Übernimmt für den Pi die letzen Pfade. + @param revpiname Einstellungen nur für RevPi laden + @return mit Einstellungen""" + if os.path.exists(savefile): + with open(savefile, "rb") as fh: + dict_all = pickle.load(fh) + if revpiname is None: + return dict_all + else: + return dict_all.get(revpiname, {}) + return {} + + +def _savedefaults(revpiname, settings): + u"""Schreibt fuer den Pi die letzen Pfade. + + @param revpiname Einstellungen sind für diesen RevPi + @param settings mit Einstellungen + @return True, bei erfolgreicher Verarbeitung + + """ + try: + makedirs(os.path.dirname(savefile), exist_ok=True) + if revpiname is None: + dict_all = settings + else: + dict_all = _loaddefaults() + dict_all[revpiname] = settings + with open(savefile, "wb") as fh: + pickle.dump(dict_all, fh) + except: + return False + return True class RevPiProgram(tkinter.Frame): + u"""Zeigt Programmfenster an.""" + def __init__(self, master, xmlcli, xmlmode, revpi): u"""Init RevPiProgram-Class. @return None""" @@ -48,11 +79,10 @@ class RevPiProgram(tkinter.Frame): self.uploaded = False self.revpi = revpi self.xmlcli = xmlcli - self.xmlmode = xmlmode - self.xmlstate = "normal" if xmlmode == 3 else "disabled" + self.xmlstate = "normal" if xmlmode >= 3 else "disabled" # Letzte Einstellungen übernehmen - self.opt = self._loaddefault() + self.opt = _loaddefaults() # Fenster bauen self._createwidgets() @@ -121,8 +151,9 @@ class RevPiProgram(tkinter.Frame): lbl["text"] = _("Download PLC program as:") lbl.grid(column=0, row=r, **cpadw) opt = tkinter.OptionMenu( - prog, self.var_typedown, *self.lst_typedown, - command=self._evt_optdown) + prog, self.var_typedown, command=self._evt_optdown, + *self.lst_typedown + ) opt["width"] = 10 opt.grid(column=1, row=r, **cpad) @@ -131,6 +162,7 @@ class RevPiProgram(tkinter.Frame): self.ckb_picdown["text"] = _("include piCtory configuration") 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") @@ -140,9 +172,11 @@ class RevPiProgram(tkinter.Frame): lbl = tkinter.Label(prog) lbl["text"] = _("Upload PLC program as:") lbl.grid(column=0, row=r, **cpadw) + opt = tkinter.OptionMenu( - prog, self.var_typeup, *self.lst_typeup, - command=self._evt_optup) + prog, self.var_typeup, command=self._evt_optup, + *self.lst_typeup + ) opt["state"] = self.xmlstate opt["width"] = 10 opt.grid(column=1, row=r, **cpad) @@ -160,6 +194,7 @@ class RevPiProgram(tkinter.Frame): self.ckb_picup["text"] = _("includes piCtory configuration") 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["state"] = self.xmlstate @@ -175,13 +210,16 @@ class RevPiProgram(tkinter.Frame): lbl = tkinter.Label(picto) lbl["text"] = _("Download piCtory configuration") 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"] = _("Upload piCtory configuration") lbl.grid(column=0, row=1, **cpadw) + btn = tkinter.Button(picto) btn["command"] = self.setpictoryrsc btn["state"] = self.xmlstate @@ -193,9 +231,11 @@ class RevPiProgram(tkinter.Frame): proc.columnconfigure(0, weight=1) proc["text"] = _("piControl0 process image") proc.grid(columnspan=2, pady=2, sticky="we") + lbl = tkinter.Label(proc) lbl["text"] = _("Download process image dump") lbl.grid(column=0, row=0, **cpadw) + btn = tkinter.Button(proc) btn["command"] = self.getprocimg btn["text"] = _("Download") @@ -206,9 +246,11 @@ class RevPiProgram(tkinter.Frame): picon.columnconfigure(0, weight=1) picon["text"] = _("Reset piControl") picon.grid(columnspan=2, pady=2, sticky="we") + lbl = tkinter.Label(picon) lbl["text"] = _("Execute piControlReset") lbl.grid(column=0, row=0, **cpadw) + btn = tkinter.Button(picon) btn["command"] = self.picontrolreset btn["text"] = _("execute") @@ -236,33 +278,6 @@ class RevPiProgram(tkinter.Frame): else: self.ckb_picup["state"] = "normal" - def _loaddefault(self, full=False): - u"""Übernimmt für den Pi die letzen Pfade. - @param full Einstellungen für alle Verbindungen laden - @return dict() mit Einstellungen""" - if os.path.exists(savefile): - fh = open(savefile, "rb") - dict_all = pickle.load(fh) - if full: - return dict_all - else: - return dict_all.get(self.revpi, {}) - return {} - - def _savedefaults(self): - u"""Schreibt fuer den Pi die letzen Pfade. - @return True, bei erfolgreicher Verarbeitung""" - try: - makedirs(os.path.dirname(savefile), exist_ok=True) - dict_all = self._loaddefault(full=True) - dict_all[self.revpi] = self.opt - fh = open(savefile, "wb") - pickle.dump(dict_all, fh) - self.changes = False - except: - return False - return True - def create_filelist(self, rootdir): u"""Erstellt eine Dateiliste von einem Verzeichnis. @param rootdir Verzeichnis fuer das eine Liste erstellt werden soll @@ -324,7 +339,7 @@ class RevPiProgram(tkinter.Frame): ) # Einstellungen speichern self.opt["getpictoryrsc_dir"] = os.path.dirname(fh.name) - self._savedefaults() + self._savedefaults(self.revpi, self.opt) finally: fh.close() @@ -355,7 +370,7 @@ class RevPiProgram(tkinter.Frame): ) # Einstellungen speichern self.opt["getprocimg_dir"] = os.path.dirname(fh.name) - self._savedefaults() + self._savedefaults(self.revpi, self.opt) finally: fh.close() @@ -403,7 +418,7 @@ class RevPiProgram(tkinter.Frame): # Einstellungen speichern self.opt["setpictoryrsc_dir"] = os.path.dirname(fh.name) - self._savedefaults() + self._savedefaults(self.revpi, self.opt) elif ec == -1: tkmsg.showerror( _("Error"), @@ -561,7 +576,7 @@ class RevPiProgram(tkinter.Frame): ) # Einstellungen speichern - self._savedefaults() + self._savedefaults(self.revpi, self.opt) finally: fh.close() @@ -737,7 +752,7 @@ class RevPiProgram(tkinter.Frame): self.opt["typeup"] = self.var_typeup.get() self.opt["picup"] = self.var_picup.get() - self._savedefaults() + self._savedefaults(self.revpi, self.opt) elif ec == -1: tkmsg.showerror( diff --git a/revpipycontrol/revpipycontrol.py b/revpipycontrol/revpipycontrol.py index f157ba2..cc6cea3 100755 --- a/revpipycontrol/revpipycontrol.py +++ b/revpipycontrol/revpipycontrol.py @@ -1,4 +1,5 @@ #!/usr/bin/python3 +# -*- coding: utf-8 -*- # # RevPiPyControl # Version: see global var pycontrolverion @@ -6,11 +7,12 @@ # Webpage: https://revpimodio.org/revpipyplc/ # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- +u"""Hauptprogramm.""" import revpicheckclient import revpiinfo import revpilogfile import revpioption +import revpilegacy import revpiplclist import revpiprogram import socket @@ -23,11 +25,13 @@ from xmlrpc.client import ServerProxy # Übersetzung laden _ = gettrans() -pycontrolversion = "0.4.2" +pycontrolversion = "0.6.0" class RevPiPyControl(tkinter.Frame): + u"""Baut Hauptprogramm auf.""" + def __init__(self, master=None): u"""Init RevPiPyControl-Class. @param master tkinter master""" @@ -39,6 +43,7 @@ class RevPiPyControl(tkinter.Frame): self.dict_conn = revpiplclist.get_connections() self.errcount = 0 self.revpiname = None + self.revpipyversion = [0, 0, 0] self.xmlfuncs = [] self.xmlmode = 0 @@ -202,6 +207,7 @@ class RevPiPyControl(tkinter.Frame): try: self.xmlfuncs = sp.system.listMethods() self.xmlmode = sp.xmlmodus() + self.revpipyversion = list(map(int, sp.version().split("."))) except: self.servererror() else: @@ -238,8 +244,8 @@ class RevPiPyControl(tkinter.Frame): _("Warning"), _("The watch mode ist not supported in version {} " "of RevPiPyLoad on your RevPi! You need at least version " - "0.4.0. Or the python3-revpimodio module is not installt" - "on your RevPi at least version 0.15.0." + "0.5.3! Maybe the python3-revpimodio2 module is not " + "installed on your RevPi at least version 2.0.0." "").format(self.cli.version()), parent=self.master ) @@ -256,7 +262,7 @@ class RevPiPyControl(tkinter.Frame): tkmsg.showwarning( _("Error"), _("Can not load piCtory configuration. \n" - "Have you created a hardware configuration? " + "Did you create a hardware configuration? " "Please check this in piCtory!"), parent=self.master ) @@ -318,13 +324,20 @@ class RevPiPyControl(tkinter.Frame): win = tkinter.Toplevel(self) win.focus_set() win.grab_set() - self.tkoptions = \ - revpioption.RevPiOption(win, self.cli) + + # Gegenstelle prüfen und passende Optionen laden + if self.revpipyversion[0] == 0 and self.revpipyversion[1] < 6: + self.tkoptions = \ + revpilegacy.RevPiOption(win, self.cli) + else: + self.tkoptions = \ + revpioption.RevPiOption(win, self.cli) + self.wait_window(win) if self.tkoptions.dc is not None and self.tkoptions.dorestart: # Wenn XML-Modus anders und Dienstneustart - if self.xmlmode != self.tkoptions.dc["xmlrpc"]: + if self.xmlmode != self.cli.xmlmodus(): self.serverdisconnect() self._opt_conn(self.revpiname, True) diff --git a/revpipycontrol/shared/__init__.py b/revpipycontrol/shared/__init__.py new file mode 100644 index 0000000..d059206 --- /dev/null +++ b/revpipycontrol/shared/__init__.py @@ -0,0 +1 @@ +"""Shared modules.""" diff --git a/revpipycontrol/shared/ipaclmanager.py b/revpipycontrol/shared/ipaclmanager.py new file mode 100644 index 0000000..5bf9aea --- /dev/null +++ b/revpipycontrol/shared/ipaclmanager.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +# +# IpAclManager +# +# (c) Sven Sager, License: LGPLv3 +# Version 0.1.0 +# +"""Verwaltet IP Adressen und deren ACLs.""" +from os import access, R_OK, W_OK +from re import match as rematch + + +def refullmatch(regex, string): + """re.fullmatch wegen alter python version aus wheezy nachgebaut. + + @param regex RegEx Statement + @param string Zeichenfolge gegen die getestet wird + @return True, wenn komplett passt sonst False + + """ + m = rematch(regex, string) + return m is not None and m.end() == len(string) + + +class IpAclManager(): + + """Verwaltung fuer IP Adressen und deren ACL Level.""" + + def __init__(self, minlevel, maxlevel, acl=None): + """Init IpAclManager class. + + @param minlevel Smallest access level (min. 0) + @param maxlevel Biggest access level (max. 9) + @param acl ACL Liste fuer Berechtigungen als + + """ + if type(minlevel) != int: + raise ValueError("parameter minlevel must be ") + if type(maxlevel) != int: + raise ValueError("parameter maxlevel must be ") + if minlevel < 0: + raise ValueError("minlevel must be 0 or more") + if maxlevel > 9: + raise ValueError("maxlevel maximum is 9") + if minlevel > maxlevel: + raise ValueError("minlevel is smaller than maxlevel") + + self.__dict_acl = {} + self.__dict_regex = {} + self.__dict_knownips = {} + self.__filename = None + self.__re_ipacl = "(([\\d\\*]{1,3}\\.){3}[\\d\\*]{1,3},[" \ + + str(minlevel) + "-" + str(maxlevel) + "] ?)*" + + # Liste erstellen, wenn übergeben + if acl is not None: + self.__set_acl(acl) + + def __iter__(self): + """Gibt einzelne ACLs als aus.""" + for aclip in sorted(self.__dict_acl): + yield (aclip, self.__dict_acl[aclip]) + + def __get_acl(self): + """Getter fuer den rohen ACL-String. + return ACLs als """ + str_acl = "" + for aclip in sorted(self.__dict_acl): + str_acl += "{},{} ".format(aclip, self.__dict_acl[aclip]) + return str_acl.strip() + + def __get_filename(self): + """Getter fuer Dateinamen. + @return Filename der ACL """ + return "" if self.__filename is None else self.__filename + + def __get_regex_acl(self): + """Gibt formatierten RegEx-String zurueck. + return RegEx Code als """ + return self.__re_ipacl + + def __set_acl(self, value): + """Uebernimmt neue ACL-Liste fuer die Ausertung der Level. + @param value Neue ACL-Liste als """ + if type(value) != str: + raise ValueError("parameter acl must be ") + + value = value.strip() + if not refullmatch(self.__re_ipacl, value): + raise ValueError("acl format ist not okay - 1.2.3.4,0 5.6.7.8,1") + + # Klassenwerte übernehmen + self.__dict_acl = {} + self.__dict_regex = {} + self.__dict_knownips = {} + + # Liste neu füllen mit regex Strings + for ip_level in value.split(): + ip, level = ip_level.split(",", 1) + self.__dict_acl[ip] = int(level) + self.__dict_regex[ip] = \ + ip.replace(".", "\\.").replace("*", "\\d{1,3}") + + def get_acllevel(self, ipaddress): + """Prueft IP gegen ACL List und gibt ACL-Wert aus. + @param ipaddress zum pruefen + @return ACL Wert oder -1 wenn nicht gefunden""" + # Bei bereits aufgelösten IPs direkt ACL auswerten + if ipaddress in self.__dict_knownips: + return self.__dict_knownips[ipaddress] + + for aclip in sorted(self.__dict_acl, reverse=True): + if refullmatch(self.__dict_regex[aclip], ipaddress): + # IP und Level merken + self.__dict_knownips[ipaddress] = self.__dict_acl[aclip] + + # Level zurückgeben + return self.__dict_acl[aclip] + + return -1 + + def loadacl(self, str_acl): + """Laed ACL String und gibt erfolg zurueck. + @param str_acl ACL als + @return True, wenn erfolgreich uebernommen""" + if not refullmatch(self.__re_ipacl, str_acl): + return False + self.__set_acl(str_acl) + return True + + def loadaclfile(self, filename): + """Laed ACL Definitionen aus Datei. + @param filename Dateiname fuer Definitionen + @return True, wenn Laden erfolgreich war""" + if type(filename) != str: + raise ValueError("parameter filename must be ") + + # Zugriffsrecht prüfen + if not access(filename, R_OK): + return False + + str_acl = "" + with open(filename, "r") as fh: + while True: + buff = fh.readline() + if buff == "": + break + buff = buff.split("#")[0].strip() + if len(buff) > 0: + str_acl += buff + " " + + acl_okay = self.loadacl(str_acl.strip()) + if acl_okay: + # Dateinamen für Schreiben übernehmen + self.__filename = filename + + return acl_okay + + def writeaclfile(self, filename=None, aclname=None): + """Schreibt ACL Definitionen in Datei. + @param filename Dateiname fuer Definitionen + @return True, wenn Schreiben erfolgreich war""" + if filename is not None and type(filename) != str: + raise ValueError("parameter filename must be ") + if aclname is not None and type(aclname) != str: + raise ValueError("parameter aclname must be ") + + # Dateinamen prüfen + if filename is None and self.__filename is not None: + filename = self.__filename + + # Zugriffsrecht prüfen + if not access(filename, W_OK): + return False + + header = "# {}Access Control List (acl)\n" \ + "# One entry per Line IPADRESS,LEVEL\n" \ + "#\n".format("" if aclname is None else aclname + " ") + + with open(filename, "w") as fh: + fh.write(header) + for aclip in sorted(self.__dict_acl): + fh.write("{},{}\n".format(aclip, self.__dict_acl[aclip])) + + return True + + acl = property(__get_acl, __set_acl) + filename = property(__get_filename) + regex_acl = property(__get_regex_acl) diff --git a/setup.py b/setup.py index 638f9c8..5c40fb6 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,7 @@ -#! /usr/bin/env python3 +# -*- coding: utf-8 -*- # # (c) Sven Sager, License: LGPLv3 # -# -*- coding: utf-8 -*- """Setupscript fuer RevPiPyLoad.""" import distutils.command.install_egg_info from sys import platform @@ -23,7 +22,7 @@ globsetup = { "author_email": "akira@narux.de", "url": "https://revpimodio.org/revpipyplc/", "license": "LGPLv3", - "version": "0.4.2", + "version": "0.6.0", "name": "revpipycontrol", @@ -49,6 +48,7 @@ if platform == "linux": ("share/applications", ["data/revpipycontrol.desktop"]), ("share/icons/hicolor/32x32/apps", ["data/revpipycontrol.png"]), ("share/revpipycontrol", glob("revpipycontrol/*.*")), + ("share/revpipycontrol/shared", glob("revpipycontrol/shared/*.*")), ( "share/revpipycontrol/locale/de/LC_MESSAGES", glob("revpipycontrol/locale/de/LC_MESSAGES/*.mo") diff --git a/stdeb.cfg b/stdeb.cfg index 66b7628..ea28f1d 100644 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -3,3 +3,4 @@ Debian-Version=1 Depends3=python3-tk Section=universe/x11 Suite=stable +X-Python3-Version: >=3.4