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

12 Commits

Author SHA1 Message Date
76c45daaf7 docs 2017-04-11 12:45:42 +02:00
6ebf8ccc0a Logdaten werden als Binary ?bertragen
piControlReset hinzugef?gt
2017-04-11 12:39:48 +02:00
92d73e9c12 Etikett 0.2.8 zum ?nderungssatz f97f08836642 hinzugef?gt 2017-04-11 12:37:20 +02:00
3e2b5f66a9 Einstellungen vom Programmfenster werden gespeichert 2017-03-23 10:44:54 +01:00
2d04617358 Etikett 0.2.7 zum ?nderungssatz 67f6a3937a1f hinzugef?gt 2017-03-20 13:23:09 +01:00
b7115ef128 Bei fehlenden Einstellungen default Werte setzen
Programmargumente k?nnen konfiguriert werden
2017-03-20 13:22:56 +01:00
587b7d7acb Etikett 0.2.6 zu ?nderungssatz 73087f375b2d verschoben (aus dem ?nderungssatz 1b1698038e79) 2017-03-16 21:19:21 +01:00
29249462ca xml-mode abfragen und Fenster danach aufbauen
Meldungstexte ?berarbeitet
2017-03-16 21:03:12 +01:00
cc03ede267 bugfixes
Logviewer umgebaut
Fenster schlie?en bei Verbindungsfehler
Verbindungscheck mit Z?hlercounter
2017-03-16 16:32:47 +01:00
6ef28119ad Etikett 0.2.6 zum ?nderungssatz 1b1698038e79 hinzugef?gt 2017-03-15 16:15:25 +01:00
1adda9912b Python Version f?r PLC w?hlbar
setup.py aufgebaut
2017-03-15 13:02:39 +01:00
e9648994d0 Dateien werden einzeln hochgeladen 2017-03-13 15:02:36 +01:00
19 changed files with 892 additions and 213 deletions

View File

@@ -1,2 +1,8 @@
syntax: glob
*.pyc
deb_dist/*
dist/*
revpipycontrol.egg-info/*
doc/*
deb/*
.eric6project/*

5
MANIFEST.in Normal file
View File

@@ -0,0 +1,5 @@
include MANIFEST.in
include stdeb.cfg
recursive-include data *
recursive-include revpipycontrol *
global-exclude *.pyc

3
data/revpipycontrol Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
exec "/usr/share/revpipycontrol/revpipycontrol.py" "$@"

View File

@@ -0,0 +1,11 @@
[Desktop Entry]
Name=RevPi PLC Control
Comment=Controls the Python PLC program on your RevolutionPI
Name[de]=RevPi PLC Steuerung
Comment[de]=Kontrolliert das Python PLC Programm auf dem RevolutionPI
Exec=/usr/bin/revpipycontrol
Icon=revpipycontrol
Terminal=false
Type=Application
Categories=Application;
#StartupNotify=true

BIN
data/revpipycontrol.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
data/revpipycontrol.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

79
revpipycontrol.api Normal file
View File

@@ -0,0 +1,79 @@
revpicheckclient.RevPiCheckClient._autorw?5()
revpicheckclient.RevPiCheckClient._createiogroup?5(device, frame, iotype)
revpicheckclient.RevPiCheckClient._createwidgets?5()
revpicheckclient.RevPiCheckClient._readvaluesdev?5(device, iotype)
revpicheckclient.RevPiCheckClient._writevaluesdev?5(device)
revpicheckclient.RevPiCheckClient.myapp?7
revpicheckclient.RevPiCheckClient.onfrmconf?4(canvas)
revpicheckclient.RevPiCheckClient.readvalues?4()
revpicheckclient.RevPiCheckClient.root?7
revpicheckclient.RevPiCheckClient.toggleauto?4()
revpicheckclient.RevPiCheckClient.writevalues?4()
revpicheckclient.RevPiCheckClient?1(master, xmlcli)
revpilogfile.RevPiLogfile._createwidgets?5()
revpilogfile.RevPiLogfile.btn_clearapp?4()
revpilogfile.RevPiLogfile.btn_clearplc?4()
revpilogfile.RevPiLogfile.get_applines?4()
revpilogfile.RevPiLogfile.get_applog?4()
revpilogfile.RevPiLogfile.get_plclines?4()
revpilogfile.RevPiLogfile.get_plclog?4()
revpilogfile.RevPiLogfile?1(master, xmlcli)
revpioption.RevPiOption._createwidgets?5()
revpioption.RevPiOption._loadappdata?5()
revpioption.RevPiOption._setappdata?5()
revpioption.RevPiOption.askxmlon?4()
revpioption.RevPiOption.xmlmods?4()
revpioption.RevPiOption?1(master, xmlcli, xmlmode)
revpiplclist.RevPiPlcList._createwidgets?5()
revpiplclist.RevPiPlcList._loadappdata?5()
revpiplclist.RevPiPlcList._saveappdata?5()
revpiplclist.RevPiPlcList.build_listconn?4()
revpiplclist.RevPiPlcList.evt_btnadd?4()
revpiplclist.RevPiPlcList.evt_btnclose?4()
revpiplclist.RevPiPlcList.evt_btnnew?4()
revpiplclist.RevPiPlcList.evt_btnremove?4()
revpiplclist.RevPiPlcList.evt_btnsave?4()
revpiplclist.RevPiPlcList.evt_keypress?4(evt=None)
revpiplclist.RevPiPlcList.evt_listconn?4(evt=None)
revpiplclist.RevPiPlcList.myapp?7
revpiplclist.RevPiPlcList.root?7
revpiplclist.RevPiPlcList?1(master)
revpiplclist.get_connections?4()
revpiplclist.savefile?7
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()
revpiprogram.RevPiProgram.getprocimg?4()
revpiprogram.RevPiProgram.myapp?7
revpiprogram.RevPiProgram.picontrolreset?4()
revpiprogram.RevPiProgram.plcdownload?4()
revpiprogram.RevPiProgram.plcupload?4()
revpiprogram.RevPiProgram.root?7
revpiprogram.RevPiProgram.setpictoryrsc?4(filename=None)
revpiprogram.RevPiProgram?1(master, xmlcli, xmlmode, revpi)
revpiprogram.savefile?7
revpipycontrol.RevPiPyControl._btnstate?5()
revpipycontrol.RevPiPyControl._closeall?5()
revpipycontrol.RevPiPyControl._createwidgets?5()
revpipycontrol.RevPiPyControl._fillconnbar?5()
revpipycontrol.RevPiPyControl._fillmbar?5()
revpipycontrol.RevPiPyControl._opt_conn?5(text)
revpipycontrol.RevPiPyControl.myapp?7
revpipycontrol.RevPiPyControl.plclist?4()
revpipycontrol.RevPiPyControl.plclogs?4()
revpipycontrol.RevPiPyControl.plcmonitor?4()
revpipycontrol.RevPiPyControl.plcoptions?4()
revpipycontrol.RevPiPyControl.plcprogram?4()
revpipycontrol.RevPiPyControl.plcrestart?4()
revpipycontrol.RevPiPyControl.plcstart?4()
revpipycontrol.RevPiPyControl.plcstop?4()
revpipycontrol.RevPiPyControl.root?7
revpipycontrol.RevPiPyControl.servererror?4()
revpipycontrol.RevPiPyControl.tmr_plcrunning?4()
revpipycontrol.RevPiPyControl?1(master=None)
revpipycontrol.addroot?4(filename)

6
revpipycontrol.bas Normal file
View File

@@ -0,0 +1,6 @@
RevPiCheckClient tkinter.Frame
RevPiLogfile tkinter.Frame
RevPiOption tkinter.Frame
RevPiPlcList tkinter.Frame
RevPiProgram tkinter.Frame
RevPiPyControl tkinter.Frame

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-5.1.dtd">
<!-- eric project file for project revpipycontrol -->
<!-- Saved: 2017-03-09, 20:23:30 -->
<!-- Saved: 2017-04-11, 12:38:06 -->
<!-- Copyright (C) 2017 Sven Sager, akira@narux.de -->
<Project version="5.1">
<Language>en_US</Language>
@@ -9,12 +9,11 @@
<ProgLanguage mixed="0">Python3</ProgLanguage>
<ProjectType>Console</ProjectType>
<Description></Description>
<Version>0.2.1</Version>
<Version>0.2.12</Version>
<Author>Sven Sager</Author>
<Email>akira@narux.de</Email>
<Eol index="-1"/>
<Sources>
<Source>revpipycontrol/__init__.py</Source>
<Source>revpipycontrol/revpipycontrol.py</Source>
<Source>revpipycontrol/revpicheckclient.py</Source>
<Source>setup.py</Source>
@@ -27,7 +26,11 @@
<Translations/>
<Resources/>
<Interfaces/>
<Others/>
<Others>
<Other>data</Other>
<Other>doc</Other>
<Other>revpipycontrol.api</Other>
</Others>
<MainScript>revpipycontrol/revpipycontrol.py</MainScript>
<Vcs>
<VcsType>Mercurial</VcsType>
@@ -142,6 +145,122 @@
<FiletypeAssociation pattern="*.pyw" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw3" type="SOURCES"/>
</FiletypeAssociations>
<Documentation>
<DocumentationParams>
<dict>
<key>
<string>ERIC4API</string>
</key>
<value>
<dict>
<key>
<string>ignoreDirectories</string>
</key>
<value>
<list>
<string>data</string>
<string>deb</string>
<string>dist</string>
<string>doc</string>
</list>
</value>
<key>
<string>ignoreFilePatterns</string>
</key>
<value>
<list>
<string>setup.py</string>
</list>
</value>
<key>
<string>includePrivate</string>
</key>
<value>
<bool>True</bool>
</value>
<key>
<string>languages</string>
</key>
<value>
<list>
<string>Python3</string>
</list>
</value>
<key>
<string>outputFile</string>
</key>
<value>
<string>revpipycontrol.api</string>
</value>
<key>
<string>useRecursion</string>
</key>
<value>
<bool>True</bool>
</value>
</dict>
</value>
<key>
<string>ERIC4DOC</string>
</key>
<value>
<dict>
<key>
<string>ignoreDirectories</string>
</key>
<value>
<list>
<string>data</string>
<string>deb</string>
<string>dist</string>
<string>doc</string>
</list>
</value>
<key>
<string>ignoreFilePatterns</string>
</key>
<value>
<list>
<string>setup.py</string>
</list>
</value>
<key>
<string>noindex</string>
</key>
<value>
<bool>True</bool>
</value>
<key>
<string>outputDirectory</string>
</key>
<value>
<string>doc</string>
</value>
<key>
<string>qtHelpEnabled</string>
</key>
<value>
<bool>False</bool>
</value>
<key>
<string>sourceExtensions</string>
</key>
<value>
<list>
<string></string>
</list>
</value>
<key>
<string>useRecursion</string>
</key>
<value>
<bool>True</bool>
</value>
</dict>
</value>
</dict>
</DocumentationParams>
</Documentation>
<Checkers>
<CheckersParams>
<dict>

View File

@@ -1,4 +1,11 @@
# Thranks to: http://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-group-of-widgets-in-tkinter
#
# RevPiPyControl
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
# Thranks to: http://stackoverflow.com/questions/3085696/adding-a-
# scrollbar-to-a-group-of-widgets-in-tkinter
import pickle
import tkinter
@@ -10,28 +17,13 @@ from xmlrpc.client import ServerProxy, Binary, MultiCall
class RevPiCheckClient(tkinter.Frame):
def __init__(self, master, xmlcli=None):
def __init__(self, master, xmlcli):
"""Instantiiert MyApp-Klasse."""
super().__init__(master)
self.pack(fill="both", expand=True)
# Command arguments
parser = ArgumentParser(
description="Revolution Pi IO-Client"
)
parser.add_argument(
"-a", "--address", dest="address", default="127.0.0.1",
help="Server address (Default: 127.0.0.1)"
)
parser.add_argument(
"-p", "--port", dest="port", type=int, default=55074,
help="Use port to connect to server (Default: 55074)"
)
self.pargs = parser.parse_args()
self.cli = xmlcli
self.cli = xmlcli if xmlcli is not None else ServerProxy(
"http://{}:{}".format(self.pargs.address, self.pargs.port)
)
self.lst_devices = self.cli.get_devicenames()
self.lst_group = []
self.dict_inpvar = {}

View File

@@ -5,6 +5,7 @@
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
import pickle
import tkinter
@@ -21,21 +22,42 @@ class RevPiLogfile(tkinter.Frame):
def _createwidgets(self):
self.master.wm_title("RevPi Python PLC Logs")
self.rowconfigure(0, weight=0)
self.rowconfigure(1, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=0)
self.columnconfigure(2, weight=0)
self.columnconfigure(3, weight=1)
self.columnconfigure(4, weight=0)
self.columnconfigure(5, weight=0)
# PLC Log
self.lblapplog = tkinter.Label(self)
self.lblapplog["text"] = "RevPyPyLoad - Logfile"
self.lblapplog.grid(column=0, row=0, sticky="w")
self.btnapplog = tkinter.Button(self)
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.pack(side="left", expand=True, fill="both")
self.plcscr.pack(side="left", fill="y")
# self.plclog["state"] = "disabled"
self.plclog.grid(sticky="wnse", columnspan=2, column=0, row=1)
self.plcscr.grid(sticky="ns", column=2, row=1)
self.plclog["yscrollcommand"] = self.plcscr.set
self.plcscr["command"] = self.plclog.yview
# APP Log
self.lblapplog = tkinter.Label(self)
self.lblapplog["text"] = "Python PLC program - Logfile"
self.lblapplog.grid(column=3, row=0, sticky="w")
self.btnapplog = tkinter.Button(self)
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.appscr.pack(side="right", fill="y")
self.applog.pack(side="right", expand=True, fill="both")
# self.applog["state"] = "disabled"
self.applog.grid(sticky="nesw", columnspan=2, column=3, row=1)
self.appscr.grid(sticky="ns", column=5, row=1)
self.applog["yscrollcommand"] = self.appscr.set
self.appscr["command"] = self.applog.yview
@@ -46,28 +68,40 @@ class RevPiLogfile(tkinter.Frame):
self.master.after(1000, self.get_applines)
self.master.after(1000, self.get_plclines)
def btn_clearapp(self):
self.applog.delete(1.0, tkinter.END)
def btn_clearplc(self):
self.plclog.delete(1.0, tkinter.END)
def get_applines(self):
roll = self.applog.yview()[1] == 1.0
for line in self.xmlcli.get_applines():
self.applog.insert(tkinter.END, line)
try:
for line in pickle.loads(self.xmlcli.get_applines().data):
self.applog.insert(tkinter.END, line)
except:
pass
if roll:
self.applog.see(tkinter.END)
self.master.after(1000, self.get_applines)
def get_applog(self):
self.applog.delete(1.0, tkinter.END)
self.applog.insert(1.0, self.xmlcli.get_applog())
self.applog.insert(1.0, pickle.loads(self.xmlcli.get_applog().data))
self.applog.see(tkinter.END)
def get_plclines(self):
roll = self.plclog.yview()[1] == 1.0
for line in self.xmlcli.get_plclines():
self.plclog.insert(tkinter.END, line)
try:
for line in pickle.loads(self.xmlcli.get_plclines().data):
self.plclog.insert(tkinter.END, line)
except:
pass
if roll:
self.plclog.see(tkinter.END)
self.master.after(1000, self.get_plclines)
def get_plclog(self):
self.plclog.delete(1.0, tkinter.END)
self.plclog.insert(1.0, self.xmlcli.get_plclog())
self.plclog.insert(1.0, pickle.loads(self.xmlcli.get_plclog().data))
self.plclog.see(tkinter.END)

View File

@@ -11,18 +11,23 @@ import tkinter.messagebox as tkmsg
class RevPiOption(tkinter.Frame):
def __init__(self, master, xmlcli):
def __init__(self, master, xmlcli, xmlmode):
if xmlmode < 2:
return None
super().__init__(master)
self.pack(expand=True, fill="both")
self.xmlcli = xmlcli
self.xmlmode = xmlmode
self.xmlstate = "normal" if xmlmode == 3 else "disabled"
# Fenster bauen
self._createwidgets()
self._loadappdata()
def _createwidgets(self):
self.master.wm_title("RevPi Python PLC Connections")
self.master.wm_title("RevPi Python PLC Options")
self.master.wm_resizable(width=False, height=False)
cpadw = {"padx": 4, "pady": 2, "sticky": "w"}
@@ -40,45 +45,85 @@ class RevPiOption(tkinter.Frame):
ckb_start = tkinter.Checkbutton(stst)
ckb_start["text"] = "Programm automatisch starten"
ckb_start["state"] = self.xmlstate
ckb_start["variable"] = self.var_start
ckb_start.grid(**cpadw)
ckb_reload = tkinter.Checkbutton(stst)
ckb_reload["text"] = "Programm nach Absturz neustarten"
ckb_reload["text"] = "Programm nach Beenden neu starten"
ckb_reload["state"] = self.xmlstate
ckb_reload["variable"] = self.var_reload
ckb_reload.grid(**cpadw)
ckb_zexit = tkinter.Checkbutton(stst, justify="left")
ckb_zexit["text"] = "Prozessabbild auf NULL setzen, wenn "
"Programm\nerfolgreich beendet wird"
ckb_zexit["state"] = self.xmlstate
ckb_zexit["text"] = "Prozessabbild auf NULL setzen, wenn " \
"Programm\nerfolgreich beendet wird"
ckb_zexit["variable"] = self.var_zexit
ckb_zexit.grid(**cpadw)
ckb_zerr = tkinter.Checkbutton(stst, justify="left")
ckb_zerr["text"] = "Prozessabbild auf NULL setzen, wenn "
"Programm\ndurch Absturz beendet wird"
ckb_zerr["state"] = self.xmlstate
ckb_zerr["text"] = "Prozessabbild auf NULL setzen, wenn " \
"Programm\ndurch Absturz beendet wird"
ckb_zerr["variable"] = self.var_zerr
ckb_zerr.grid(**cpadw)
# Gruppe Programm
prog = tkinter.LabelFrame(self)
prog["text"] = "Programm"
prog["text"] = "PLC Programm"
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"] = self.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"] = self.xmlstate
rbn["text"] = "Python3"
rbn["value"] = 3
rbn["variable"] = self.var_pythonver
rbn.grid(column=1, row=1, **cpadw)
lbl = tkinter.Label(prog)
lbl["text"] = "Python PLC Programname"
lbl.grid(**cpadw)
# txt_startpy = tkinter.Entry(prog)
# txt_startpy["textvariable"] = self.var_startpy
# txt_startpy.grid(**cpadwe)
lbl.grid(columnspan=2, **cpadw)
lst = self.xmlcli.get_filelist()
if len(lst) == 0:
lst.append("none")
opt_startpy = tkinter.OptionMenu(
prog, self.var_startpy, *self.xmlcli.get_filelist())
opt_startpy.grid(**cpadwe)
prog, self.var_startpy, *lst)
opt_startpy["state"] = self.xmlstate
opt_startpy.grid(columnspan=2, **cpadwe)
lbl = tkinter.Label(prog)
lbl["text"] = "Programm Argumente"
lbl.grid(columnspan=2, **cpadw)
txt = tkinter.Entry(prog)
txt["textvariable"] = self.var_startargs
txt.grid(columnspan=2, **cpadw)
ckb_slave = tkinter.Checkbutton(prog, justify="left")
ckb_slave["state"] = self.xmlstate
ckb_slave["text"] = "RevPi als PLC-Slave verwenden"
ckb_slave["state"] = "disabled"
ckb_slave["variable"] = self.var_slave
ckb_slave.grid(**cpadw)
ckb_slave.grid(columnspan=2, **cpadw)
# Gruppe XMLRPC
xmlrpc = tkinter.LabelFrame(self)
@@ -86,26 +131,48 @@ class RevPiOption(tkinter.Frame):
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"] = self.xmlstate
ckb_xmlon["text"] = "XML-RPC Server aktiv auf RevPi"
ckb_xmlon["variable"] = self.var_xmlon
ckb_xmlon.grid(**cpadw)
self.ckb_xmlmod2 = tkinter.Checkbutton(xmlrpc, justify="left")
self.ckb_xmlmod2["command"] = self.xmlmods
self.ckb_xmlmod2["state"] = self.xmlstate
self.ckb_xmlmod2["text"] = \
"Download von piCtory Konfiguration und\nPLC Programm zulassen"
self.ckb_xmlmod2["variable"] = self.var_xmlmod2
self.ckb_xmlmod2.grid(**cpadw)
self.ckb_xmlmod3 = tkinter.Checkbutton(xmlrpc, justify="left")
self.ckb_xmlmod3["state"] = self.xmlstate
self.ckb_xmlmod3["text"] = \
"Upload von piCtory Konfiguration und\nPLC Programm zualssen"
self.ckb_xmlmod3["variable"] = self.var_xmlmod3
self.ckb_xmlmod3.grid(**cpadw)
lbl = tkinter.Label(xmlrpc)
lbl["text"] = "XML-RPC Serverport"
lbl.grid(**cpadw)
spb_xmlport = tkinter.Spinbox(xmlrpc)
spb_xmlport["to"] = 65535
spb_xmlport["from"] = 1024
spb_xmlport["state"] = self.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"] = self.xmlstate
btn_save["text"] = "Speichern"
btn_save.grid(column=0, row=3)
@@ -117,16 +184,21 @@ class RevPiOption(tkinter.Frame):
def _loadappdata(self):
dc = self.xmlcli.get_config()
self.var_start.set(dc["autostart"])
self.var_reload.set(dc["autoreload"])
self.var_zexit.set(dc["zeroonexit"])
self.var_zerr.set(dc["zeroonerror"])
self.var_start.set(dc.get("autostart", "1"))
self.var_reload.set(dc.get("autoreload", "1"))
self.var_zexit.set(dc.get("zeroonexit", "0"))
self.var_zerr.set(dc.get("zeroonerror", "0"))
self.var_startpy.set(dc["plcprogram"])
self.var_slave.set(dc["plcslave"])
self.var_startpy.set(dc.get("plcprogram", "none.py"))
self.var_startargs.set(dc.get("plcarguments", ""))
self.var_pythonver.set(dc.get("pythonversion", "3"))
self.var_slave.set(dc.get("plcslave", "0"))
self.var_xmlon.set(dc["xmlrpc"])
self.var_xmlport.set(dc["xmlrpcport"])
self.var_xmlon.set(dc.get("xmlrpc", 0) >= 1)
self.var_xmlmod2.set(dc.get("xmlrpc", 0) >= 2)
self.var_xmlmod3.set(dc.get("xmlrpc", 0) >= 3)
self.var_xmlport.set(dc.get("xmlrpcport", "55123"))
def _setappdata(self):
dc = {}
@@ -136,29 +208,54 @@ class RevPiOption(tkinter.Frame):
dc["zeroonerror"] = int(self.var_zerr.get())
dc["plcprogram"] = self.var_startpy.get()
dc["plcarguments"] = self.var_startargs.get()
dc["pythonversion"] = self.var_pythonver.get()
dc["plcslave"] = int(self.var_slave.get())
dc["xmlrpc"] = int(self.var_xmlon.get())
dc["xmlrpc"] = 0
if self.var_xmlon.get():
dc["xmlrpc"] += 1
if self.var_xmlmod2.get():
dc["xmlrpc"] += 1
if self.var_xmlmod3.get():
dc["xmlrpc"] += 1
dc["xmlrpcport"] = self.var_xmlport.get()
self.xmlmode = dc["xmlrpc"]
ask = tkmsg.askyesnocancel(
"Frage", "Die Einstellungen werden jetzt auf den Revolution Pi "
"Frage", "Die Einstellungen werden jetzt auf dem Revolution Pi "
"gespeichert. \n\nSollen die neuen Einstellungen sofort in Kraft "
"treten? \nDies bedeutet einen Neustart des Dienstes und des ggf. "
"laufenden PLC-Programms!", parent=self.master
)
if ask is not None:
self.xmlcli.set_config(dc, ask)
tkmsg.showinfo(
"Information", "Einstellungen gespeichert.", parent=self.master
)
if self.xmlcli.set_config(dc, ask):
tkmsg.showinfo(
"Information", "Einstellungen gespeichert.",
parent=self.master
)
else:
tkmsg.showerror(
"Fehler", "Die Einstellungen konnten nicht gesichert"
"werden. Dies kann passieren, wenn Werte falsch sind!",
parent=self.master
)
def askxmlon(self):
if not self.var_xmlon.get():
ask = tkmsg.askyesno(
"Frage", "Soll der XML-RPC Server wirklich beendet werden? "
"Sie können dann mit diesem Programm NICHT mehr auf den "
"Sie können dann NICHT mehr mit diesem Programm auf den "
"Revolution Pi zugreifen.", parent=self.master
)
if not ask:
self.var_xmlon.set(True)
self.xmlmods()
def xmlmods(self):
self.ckb_xmlmod2["state"] = \
"normal" if self.var_xmlon.get() else "disabled"
self.ckb_xmlmod3["state"] = \
"normal" if self.var_xmlmod2.get() else "disabled"

View File

@@ -21,11 +21,14 @@ else:
savefile = os.path.join(homedir, ".revpipyplc", "connections.dat")
# Für andere Module zum Laden der Connections
def get_connections():
if os.path.exists(savefile):
fh = open(savefile, "rb")
connections = pickle.load(fh)
return connections
return connections
else:
return {}
class RevPiPlcList(tkinter.Frame):
@@ -34,6 +37,8 @@ class RevPiPlcList(tkinter.Frame):
super().__init__(master)
self.pack()
self.changes = False
# Daten laden
self._connections = {}
@@ -115,6 +120,7 @@ class RevPiPlcList(tkinter.Frame):
makedirs(os.path.dirname(savefile), exist_ok=True)
fh = open(savefile, "wb")
pickle.dump(self._connections, fh)
self.changes = False
except:
return False
return True
@@ -131,14 +137,18 @@ class RevPiPlcList(tkinter.Frame):
self.build_listconn()
self.evt_btnnew()
self.changes = True
def evt_btnclose(self):
ask = tkmsg.askyesno(
"Frage...",
"Wollen Sie wirklich beenden?\n"
"Nicht gespeicherte Änderungen gehen verloren",
parent=self.master
)
if self.changes:
ask = tkmsg.askyesno(
parent=self.master, title="Frage...",
message="Wollen Sie wirklich beenden?\n"
"Nicht gespeicherte Änderungen gehen verloren",
)
else:
ask = True
if ask:
self.master.destroy()
@@ -166,6 +176,7 @@ class RevPiPlcList(tkinter.Frame):
del self._connections[item]
self.build_listconn()
self.evt_btnnew()
self.changes = True
def evt_btnsave(self):
if self._saveappdata():

View File

@@ -5,19 +5,36 @@
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
import gzip
import os
import pickle
import tarfile
import tkinter
import tkinter.filedialog as tkfd
import tkinter.messagebox as tkmsg
import zipfile
from tempfile import mktemp
from os import environ
from os import makedirs
from shutil import rmtree
from sys import platform
from tempfile import mktemp, mkdtemp
from xmlrpc.client import Binary
# Systemwerte
if platform == "linux":
homedir = environ["HOME"]
else:
homedir = environ["APPDATA"]
savefile = os.path.join(homedir, ".revpipyplc", "programpath.dat")
class RevPiProgram(tkinter.Frame):
def __init__(self, master, xmlcli, revpi):
def __init__(self, master, xmlcli, xmlmode, revpi):
if xmlmode < 2:
return None
super().__init__(master)
# master.protocol("WM_DELETE_WINDOW", self._checkclose)
self.pack(expand=True, fill="both")
@@ -25,6 +42,11 @@ class RevPiProgram(tkinter.Frame):
self.uploaded = False
self.revpi = revpi
self.xmlcli = xmlcli
self.xmlmode = xmlmode
self.xmlstate = "normal" if xmlmode == 3 else "disabled"
# Letzte Einstellungen übernehmen
self.opt = self._loaddefault()
# Fenster bauen
self._createwidgets()
@@ -54,7 +76,7 @@ class RevPiProgram(tkinter.Frame):
# Gruppe Programm
prog = tkinter.LabelFrame(self)
prog.columnconfigure(0, weight=1)
prog["text"] = "PLC Pythonprogramm"
prog["text"] = "PLC Python programm"
prog.grid(columnspan=2, pady=2, sticky="we")
# Variablen vorbereiten
@@ -66,8 +88,10 @@ class RevPiProgram(tkinter.Frame):
self.lst_typedown = ["Dateien", "Zip Archiv", "TGZ Archiv"]
self.lst_typeup = ["Dateien", "Ordner", "Zip Archiv", "TGZ Archiv"]
self.var_typedown.set(self.lst_typedown[0])
self.var_typeup.set(self.lst_typeup[0])
self.var_picdown.set(self.opt.get("picdown", False))
self.var_picup.set(self.opt.get("picup", False))
self.var_typedown.set(self.opt.get("typedown", self.lst_typedown[0]))
self.var_typeup.set(self.opt.get("typeup", self.lst_typeup[0]))
r = 0
lbl = tkinter.Label(prog)
@@ -96,22 +120,26 @@ class RevPiProgram(tkinter.Frame):
opt = tkinter.OptionMenu(
prog, self.var_typeup, *self.lst_typeup,
command=self._evt_optup)
opt["state"] = self.xmlstate
opt["width"] = 10
opt.grid(column=1, row=r, **cpad)
r = 3
ckb = tkinter.Checkbutton(prog)
ckb["state"] = self.xmlstate
ckb["text"] = "vorher alles im Uploadverzeichnis löschen"
ckb["variable"] = self.var_cleanup
ckb.grid(column=0, row=r, columnspan=2, **cpadw)
r = 4
self.ckb_picup = tkinter.Checkbutton(prog)
self.ckb_picup["state"] = self.xmlstate
self.ckb_picup["text"] = "enthält piCtory Konfiguration"
self.ckb_picup["variable"] = self.var_picup
self.ckb_picup.grid(column=0, row=r, **cpadw)
btn = tkinter.Button(prog)
btn["command"] = self.plcupload
btn["state"] = self.xmlstate
btn["text"] = "Upload"
btn.grid(column=1, row=r, **cpad)
@@ -133,6 +161,7 @@ class RevPiProgram(tkinter.Frame):
lbl.grid(column=0, row=1, **cpadw)
btn = tkinter.Button(picto)
btn["command"] = self.setpictoryrsc
btn["state"] = self.xmlstate
btn["text"] = "Upload"
btn.grid(column=1, row=1, **cpad)
@@ -142,13 +171,26 @@ class RevPiProgram(tkinter.Frame):
proc["text"] = "piControl0 Prozessabbild"
proc.grid(columnspan=2, pady=2, sticky="we")
lbl = tkinter.Label(proc)
lbl["text"] = "Prozessabbild herunterladen"
lbl["text"] = "Prozessabbild-Dump herunterladen"
lbl.grid(column=0, row=0, **cpadw)
btn = tkinter.Button(proc)
btn["command"] = self.getprocimg
btn["text"] = "Download"
btn.grid(column=1, row=0, **cpad)
# Gruppe piControlReset
picon = tkinter.LabelFrame(self)
picon.columnconfigure(0, weight=1)
picon["text"] = "piControl Reset"
picon.grid(columnspan=2, pady=2, sticky="we")
lbl = tkinter.Label(picon)
lbl["text"] = "piControlReset ausführen"
lbl.grid(column=0, row=0, **cpadw)
btn = tkinter.Button(picon)
btn["command"] = self.picontrolreset
btn["text"] = "ausführen"
btn.grid(column=1, row=0, **cpad)
# Beendenbutton
btn = tkinter.Button(self)
btn["command"] = self.master.destroy
@@ -169,19 +211,70 @@ class RevPiProgram(tkinter.Frame):
else:
self.ckb_picup["state"] = "normal"
def _loaddefault(self):
# TODO: letzte Einstellungen laden
pass
def _loaddefault(self, full=False):
"""Uebernimmt fuer den Pi die letzen Pfade."""
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):
# TODO: Einstellungen sichern
pass
"""Schreibt fuer den Pi die letzen Pfade."""
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):
"""Erstellt eine Dateiliste von einem Verzeichnis.
@param rootdir: Verzeichnis fuer das eine Liste erstellt werden soll
@returns: Dateiliste"""
filelist = []
for tup_dir in os.walk(rootdir):
for fname in tup_dir[2]:
filelist.append(os.path.join(tup_dir[0], fname))
return filelist
def check_replacedir(self, rootdir):
"""Gibt das rootdir von einem entpackten Verzeichnis zurueck.
Dabei wird geprueft, ob es sich um einen einzelnen Ordner handelt
und ob es eine piCtory Konfiguraiton im rootdir gibt.
@param rootdir: Verzeichnis fuer Pruefung
@returns: Abgeaendertes rootdir
"""
lst_dir = os.listdir(rootdir)
if len(lst_dir) == 1 and \
os.path.isdir(os.path.join(rootdir, lst_dir[0])):
return (os.path.join(rootdir, lst_dir[0]), None)
if len(lst_dir) == 2:
rscfile = None
for fname in lst_dir:
if fname.find(".rsc"):
rscfile = os.path.join(rootdir, fname)
return (os.path.join(rootdir, lst_dir[0]), rscfile)
else:
return (rootdir, None)
def getpictoryrsc(self):
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
title="Speichern als...",
initialdir=self.opt.get("getpictoryrsc_dir", ""),
initialfile=self.revpi + ".rsc",
filetypes=(("piCtory Config", "*.rsc"), ("All Files", "*.*"))
)
@@ -200,6 +293,9 @@ class RevPiProgram(tkinter.Frame):
message="Datei erfolgreich vom Revolution Pi geladen "
"und gespeichert.",
)
# Einstellungen speichern
self.opt["getpictoryrsc_dir"] = os.path.dirname(fh.name)
self._savedefaults()
finally:
fh.close()
@@ -208,6 +304,7 @@ class RevPiProgram(tkinter.Frame):
mode="wb", parent=self.master,
confirmoverwrite=True,
title="Speichern als...",
initialdir=self.opt.get("getprocimg_dir", ""),
initialfile=self.revpi + ".img",
filetypes=(("Imagefiles", "*.img"), ("All Files", "*.*"))
)
@@ -226,26 +323,33 @@ class RevPiProgram(tkinter.Frame):
message="Datei erfolgreich vom Revolution Pi geladen "
"und gespeichert.",
)
# Einstellungen speichern
self.opt["getprocimg_dir"] = os.path.dirname(fh.name)
self._savedefaults()
finally:
fh.close()
def setpictoryrsc(self, fh=None):
if fh is None:
def setpictoryrsc(self, filename=None):
if filename is None:
fh = tkfd.askopenfile(
mode="rb", parent=self.master,
title="piCtory Datei öffnen...",
initialdir=self.opt.get("setpictoryrsc_dir", ""),
initialfile=self.revpi + ".rsc",
filetypes=(("piCtory Config", "*.rsc"), ("All Files", "*.*"))
)
else:
fh = open(filename, "rb")
if fh is not None:
ask = tkmsg.askyesno(
parent=self.master, title="Frage",
message="Soll nach dem Hochladen der piCtory Konfiguration "
"ein reset am piControl Treiber durchgeführt werden?"
"ein Reset am piControl Treiber durchgeführt werden?"
)
ec = self.xmlcli.set_pictoryrsc(Binary(fh.read()), ask)
print(ec)
if ec == 0:
if ask:
tkmsg.showinfo(
@@ -258,6 +362,10 @@ class RevPiProgram(tkinter.Frame):
parent=self.master, title="Erfolgreich",
message="Die Übertragung der piCtory Konfiguration "
"wurde erfolgreich ausgeführt")
#Einstellungen speichern
self.opt["setpictoryrsc_dir"] = os.path.dirname(fh.name)
self._savedefaults()
elif ec < 0:
tkmsg.showerror(
parent=self.master, title="Fehler",
@@ -272,6 +380,26 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def picontrolreset(self):
ask = tkmsg.askyesno(
parent=self.master, title="Frage...",
message="Soll piControlReset wirklich durchgeführt werden? \n"
"Das Prozessabbild und die Steuerung werden dann unterbrochen!!!"
)
if ask:
ec = self.xmlcli.resetpicontrol()
if ec == 0:
tkmsg.showinfo(
parent=self.master, title="Erfolgreich",
message="piControlReset erfolgreich durchgeführt"
)
else:
tkmsg.showerror(
parten=self.master, title="Fehler",
message="piControlReset konnte nicht erfolgreich "
"durchgeführt werden"
)
def plcdownload(self):
tdown = self.lst_typedown.index(self.var_typedown.get())
fh = None
@@ -280,8 +408,11 @@ class RevPiProgram(tkinter.Frame):
if tdown == 0:
# Ordner
dirselect = tkfd.askdirectory(
parent=self.master, title="Verzeichnis zum Ablegen",
mustexist=False, initialdir=self.revpi)
parent=self.master,
title="Verzeichnis zum Ablegen",
mustexist=False,
initialdir=self.opt.get("plcdownload_dir", self.revpi)
)
if type(dirselect) == str and dirselect != "":
fh = open(mktemp(), "wb")
@@ -292,6 +423,7 @@ class RevPiProgram(tkinter.Frame):
mode="wb", parent=self.master,
confirmoverwrite=True,
title="Speichern als...",
initialdir=self.opt.get("plcdownload_file", ""),
initialfile=self.revpi + ".zip",
filetypes=(("Zip Archiv", "*.zip"), ("All Files", "*.*"))
)
@@ -302,6 +434,7 @@ class RevPiProgram(tkinter.Frame):
mode="wb", parent=self.master,
confirmoverwrite=True,
title="Speichern als...",
initialdir=self.opt.get("plcdownload_file", ""),
initialfile=self.revpi + ".tar.gz",
filetypes=(("Tar Archiv", "*.tar.gz"), ("All Files", "*.*"))
)
@@ -327,10 +460,16 @@ class RevPiProgram(tkinter.Frame):
for taritem in fh_pack.getmembers():
print(rootname)
if not taritem.name == "revpipyload":
taritem.name = taritem.name.replace("revpipyload/", "")
taritem.name = \
taritem.name.replace("revpipyload/", "")
fh_pack.extract(taritem, dirselect)
fh_pack.close()
self.opt["plcdownload_dir"] = dirselect
else:
self.opt["plcdownload_file"] = os.path.dirname(fh.name)
self.opt["typedown"] = self.var_typedown.get()
self.opt["picdown"] = self.var_picdown.get()
except:
raise
@@ -345,162 +484,178 @@ class RevPiProgram(tkinter.Frame):
message="Datei erfolgreich vom Revolution Pi geladen "
"und gespeichert.",
)
# Einstellungen speichern
self._savedefaults()
finally:
fh.close()
def plcupload(self):
tup = self.lst_typeup.index(self.var_typeup.get())
fh = None
dirselect = ""
dirtmp = None
filelist = []
fileselect = None
rscfile = None
if tup == 0:
# Datei
fileselect = tkfd.askopenfilenames(
parent=self.master, title="Pythonprogramm übertragen...",
parent=self.master,
title="Python Programm übertragen...",
initialdir=self.opt.get("plcupload_dir", ""),
filetypes=(("Python", "*.py"), ("All Files", "*.*"))
)
if type(fileselect) == tuple and len(fileselect) > 0:
# Datei als TAR packen
tmpfile = mktemp()
noerr = True
try:
fh_pack = tarfile.open(
name=tmpfile, mode="w:gz", dereference=True)
for file in fileselect:
fh_pack.add(
file, arcname=os.path.basename(file))
except:
noerr = False
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Die Datei konnte für die Übertragung nicht "
"gepackt werden")
finally:
fh_pack.close()
if noerr:
# fh für Versand öffnen
fh = open(tmpfile, "rb")
for file in fileselect:
filelist.append(file)
elif tup == 1:
# Ordner
dirselect = tkfd.askdirectory(
parent=self.master, title="Verzeichnis zum Hochladen",
mustexist=True, initialdir=self.revpi)
parent=self.master,
title="Verzeichnis zum Hochladen",
mustexist=True,
initialdir=self.opt.get("plcupload_dir", self.revpi)
)
if type(dirselect) == str and dirselect != "":
# Ordner als TAR packen
tmpfile = mktemp()
noerr = True
try:
fh_pack = tarfile.open(
name=tmpfile, mode="w:gz", dereference=True)
fh_pack.add(
#dirselect, arcname=os.path.basename(dirselect))
dirselect, arcname="")
except:
noerr = False
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Der Ordner konnte für die Übertragung nicht "
"gepackt werden")
finally:
fh_pack.close()
if noerr:
# fh für Versand öffnen
fh = open(tmpfile, "rb")
filelist = self.create_filelist(dirselect)
elif tup == 2:
# Zip
fh = tkfd.askopenfile(
mode="rb", parent=self.master,
fileselect = tkfd.askopenfilename(
parent=self.master,
title="Zip-Archive übertragen...",
initialdir=self.opt.get("plcupload_file", ""),
initialfile=self.revpi + ".zip",
filetypes=(("Zip Archiv", "*.zip"), ("All Files", "*.*"))
)
if not zipfile.is_zipfile(fh.name):
if type(fileselect) == str and fileselect != "":
# Zipdatei prüfen
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Die angegebene Datei ist kein ZIP-Archiv.")
fh.close()
fh = None
if zipfile.is_zipfile(fileselect):
dirtmp = mkdtemp()
fhz = zipfile.ZipFile(fileselect)
fhz.extractall(dirtmp)
fhz.close()
filelist = self.create_filelist(dirtmp)
dirselect, rscfile = self.check_replacedir(dirtmp)
else:
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Die angegebene Datei ist kein ZIP-Archiv.")
return False
elif tup == 3:
# TarGz
fh = tkfd.askopenfile(
mode="rb", parent=self.master,
fileselect = tkfd.askopenfilename(
parent=self.master,
title="TarGz-Archiv übertragen...",
initialdir=self.opt.get("plcupload_file", ""),
initialfile=self.revpi + ".tar.gz",
filetypes=(("Tar Archiv", "*.tar.gz"), ("All Files", "*.*"))
)
if not tarfile.is_tarfile(fh.name):
# Zipdatei prüfen
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Die angegebene Datei ist kein TAR-Archiv.")
fh.close()
fh = None
if type(fileselect) == str and fileselect != "":
# Wenn kein fh existiert abbrachen
if fh is None:
return False
# Tar-Datei prüfen
if tarfile.is_tarfile(fileselect):
dirtmp = mkdtemp()
fht = tarfile.open(fileselect)
fht.extractall(dirtmp)
fht.close()
filelist = self.create_filelist(dirtmp)
dirselect, rscfile = self.check_replacedir(dirtmp)
else:
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Die angegebene Datei ist kein TAR-Archiv.")
return False
# Wenn keine Dateien gewählt
if len(filelist) == 0:
return True
# Vor Übertragung aufräumen wenn ausgewählt
if self.var_cleanup.get() and not self.xmlcli.plcuploadclean():
tkmsg.showerror(
parent=self.masger, title="Fehler",
parent=self.masger, title="Fehler",
message="Beim Löschen der Dateien auf dem Revolution Pi ist "
"ein Fehler aufgetreten.")
return False
# Flag setzen, weil ab hier Veränderungen existieren
self.uploaded = True
ec = 0
for fname in filelist:
# Archiv prüfen und umpacken
if tup >= 2:
# TODO: Archive umpacken
pass
if fname == rscfile:
continue
# TODO: Fehlerabfang bei Dateilesen
with open(fname, "rb") as fh:
# piControlReset abfragen
if self.var_picup.get():
pass
# Dateinamen ermitteln
if dirselect == "":
sendname = os.path.basename(fname)
else:
sendname = fname.replace(dirselect, "")[1:]
# Datei übertragen
try:
ustatus = self.xmlcli.plcupload(
Binary(gzip.compress(fh.read())), sendname)
except:
ec = -2
break
if not ustatus:
ec = -1
break
# TODO: Fehlerabfang bei Dateilesen
xmldata = Binary(fh.read())
ec = self.xmlcli.plcupload(xmldata)
if ec == 0:
tkmsg.showinfo(
parent=self.master, title="Erfolgreich",
message="Die Übertragung war erfolgreich.")
elif ec > 0:
tkmsg.showwarning(
parent=self.master, title="Warnung",
message="Die Übertragung war erfolgreich. \n"
"Beim piControl Reset trat allerdings ein Fehler auf!")
if self.var_picup.get():
if rscfile is not None:
self.setpictoryrsc(rscfile)
else:
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Es wurde im Archiv keine piCtory "
"Konfiguration gefunden")
# Einstellungen speichern
if tup == 0:
self.opt["plcupload_dir"] = os.path.dirname(fileselect[0])
elif tup == 1:
self.opt["plcupload_dir"] = dirselect
else:
self.opt["plcupload_file"] = os.path.dirname(fileselect)
self.opt["typeup"] = self.var_typeup.get()
self.opt["picup"] = self.var_picup.get()
self._savedefaults()
elif ec == -1:
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Der Revoluton Pi konnte die übertragene Datei nicht "
message="Der Revoluton Pi konnte Teile der Übertragung nicht "
"verarbeiten.")
elif ec < -1:
tkmsg.showwarning(
parent=self.master, title="Warnung",
message="Die Übertragung war erfolgreich. \n"
"Beim verarbeiten der piCtory Konfiguration trat allerdings "
"ein Fehler auf!")
fh.close()
elif ec == -2:
tkmsg.showerror(
parent=self.master, title="Fehler",
message="Bei der Übertragung traten Fehler auf")
# Temp-File aufräumen
if tup <= 1:
os.remove(fh.name)
# Temp-Dir aufräumen
if dirtmp is not None:
rmtree(dirtmp)
if __name__ == "__main__":

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

122
revpipycontrol/revpipycontrol.py Normal file → Executable file
View File

@@ -1,24 +1,40 @@
#!/usr/bin/python3
#
# RevPiPyControl
# Version: 0.2.1
# Version: 0.2.12
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
import revpicheckclient
import revpilogfile
import revpioption
import revpiplclist
import revpiprogram
import socket
import sys
import tkinter
import tkinter.messagebox as tkmsg
from functools import partial
from os.path import dirname
from os.path import join as pathjoin
from xmlrpc.client import ServerProxy
socket.setdefaulttimeout(3)
def addroot(filename):
u"""Hängt root-dir der Anwendung vor Dateinamen.
Je nach Ausführungsart der Anwendung muss das root-dir über
andere Arten abgerufen werden.
@param filename: Datei oder Ordnername
@returns: root dir
"""
if getattr(sys, "frozen", False):
return pathjoin(dirname(sys.executable), filename)
else:
return pathjoin(dirname(__file__), filename)
class RevPiPyControl(tkinter.Frame):
@@ -29,7 +45,14 @@ class RevPiPyControl(tkinter.Frame):
self.cli = None
self.dict_conn = revpiplclist.get_connections()
self.errcount = 0
self.revpiname = None
self.xmlmode = 0
# Globale Fenster
self.tklogs = None
self.tkoptions = None
self.tkprogram = None
# Fenster aufbauen
self._createwidgets()
@@ -48,6 +71,9 @@ class RevPiPyControl(tkinter.Frame):
"""Erstellt den Fensterinhalt."""
# Hauptfenster
self.master.wm_title("RevPi Python PLC Loader")
self.master.wm_iconphoto(
True, tkinter.PhotoImage(file=addroot("revpipycontrol.png"))
)
self.master.wm_resizable(width=False, height=False)
# Menü ganz oben
@@ -98,7 +124,7 @@ class RevPiPyControl(tkinter.Frame):
# PLC Menü
self.mplc = tkinter.Menu(self.mbar, tearoff=False)
self.mplc.add_command(label="PLC log...", command=self.plclogs)
self.mplc.add_command(label="PLC monitor...", command=self.plcmonitor)
#self.mplc.add_command(label="PLC monitor...", command=self.plcmonitor)
self.mplc.add_command(label="PLC options...", command=self.plcoptions)
self.mplc.add_command(label="PLC program...", command=self.plcprogram)
self.mbar.add_cascade(label="PLC", menu=self.mplc, state="disabled")
@@ -115,6 +141,7 @@ class RevPiPyControl(tkinter.Frame):
)
def _opt_conn(self, text):
socket.setdefaulttimeout(2)
sp = ServerProxy(
"http://{}:{}".format(
self.dict_conn[text][0], int(self.dict_conn[text][1])
@@ -122,17 +149,31 @@ class RevPiPyControl(tkinter.Frame):
)
# Server prüfen
try:
sp.system.listMethods()
self.xmlmode = sp.xmlmodus()
except:
self.servererror()
else:
self.cli = sp
self._closeall()
socket.setdefaulttimeout(15)
self.cli = ServerProxy(
"http://{}:{}".format(
self.dict_conn[text][0], int(self.dict_conn[text][1])
)
)
self.revpiname = text
self.var_conn.set("{} - {}:{}".format(
text, self.dict_conn[text][0], int(self.dict_conn[text][1])
))
self.mbar.entryconfig("PLC", state="normal")
def _closeall(self):
if self.tklogs is not None:
self.tklogs.master.destroy()
if self.tkoptions is not None:
self.tkoptions.destroy()
if self.tkprogram is not None:
self.tkprogram.destroy()
def plclist(self):
win = tkinter.Toplevel(self)
revpiplclist.RevPiPlcList(win)
@@ -143,29 +184,46 @@ class RevPiPyControl(tkinter.Frame):
self._fillconnbar()
def plclogs(self):
# TODO: nicht doppelt starten
win = tkinter.Toplevel(self)
self.tklogs = revpilogfile.RevPiLogfile(win, self.cli)
if self.tklogs is None or len(self.tklogs.children) == 0:
win = tkinter.Toplevel(self)
self.tklogs = revpilogfile.RevPiLogfile(win, self.cli)
else:
self.tklogs.focus_set()
def plcmonitor(self):
# TODO: Monitorfenster
#self.tkmonitor = revpicheckclient.RevPiCheckClient(self.master, self.cli)
pass
def plcoptions(self):
win = tkinter.Toplevel(self)
revpioption.RevPiOption(win, self.cli)
win.focus_set()
win.grab_set()
self.wait_window(win)
if self.xmlmode < 2:
tkmsg.showwarning(
parent=self.master, title="Warnung",
message="Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch "
"genug eingestellt, um diesen Dialog zu verwenden!"
)
else:
win = tkinter.Toplevel(self)
self.tkoptions = \
revpioption.RevPiOption(win, self.cli, self.xmlmode)
win.focus_set()
win.grab_set()
self.wait_window(win)
self.xmlmode = self.tkoptions.xmlmode
def plcprogram(self):
# TODO: Programfenster
win = tkinter.Toplevel(self)
revpiprogram.RevPiProgram(win, self.cli, self.revpiname)
win.focus_set()
win.grab_set()
self.wait_window(win)
if self.xmlmode < 2:
tkmsg.showwarning(
parent=self.master, title="Warnung",
message="Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch "
"genug eingestellt, um diesen Dialog zu verwenden!"
)
else:
win = tkinter.Toplevel(self)
self.tkprogram = revpiprogram.RevPiProgram(
win, self.cli, self.xmlmode, self.revpiname)
win.focus_set()
win.grab_set()
self.wait_window(win)
def plcstart(self):
self.cli.plcstart()
@@ -178,10 +236,13 @@ class RevPiPyControl(tkinter.Frame):
self.cli.plcstart()
def servererror(self):
"""Setzt alles auf NULL."""
socket.setdefaulttimeout(2)
self.cli = None
self._btnstate()
self.mbar.entryconfig("PLC", state="disabled")
self.var_conn.set("")
self._closeall()
tkmsg.showerror("Fehler", "Server ist nicht erreichbar!")
def tmr_plcrunning(self):
@@ -191,20 +252,25 @@ class RevPiPyControl(tkinter.Frame):
self.var_status.set("NOT CONNECTED")
else:
try:
if self.cli.plcrunning():
self.txt_status["readonlybackground"] = "green"
else:
self.txt_status["readonlybackground"] = "red"
plcec = self.cli.plcexitcode()
except:
self.var_status.set("SERVER ERROR")
self.servererror()
self.errcount += 1
if self.errcount >= 5:
self.var_status.set("SERVER ERROR")
self.servererror()
else:
self.errcount = 0
self.txt_status["readonlybackground"] = \
"green" if plcec == -1 else "red"
if plcec == -1:
plcec = "RUNNING"
elif plcec == -2:
plcec = "FILE NOT FOUND"
elif plcec == -9:
plcec = "PROGRAM KILLED"
elif plcec == -15:
plcec = "PROGRAMS TERMED"
elif plcec == 0:
plcec = "NOT RUNNING"
self.var_status.set(plcec)

90
setup.py Normal file
View File

@@ -0,0 +1,90 @@
#! /usr/bin/env python3
#
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
"""Setupscript fuer RevPiPyLoad."""
import distutils.command.install_egg_info
from sys import platform
from glob import glob
class MyEggInfo(distutils.command.install_egg_info.install_egg_info):
u"""Disable egg_info installation, seems pointless for a non-library."""
def run(self):
u"""just pass egg_info."""
pass
globsetup = {
"author": "Sven Sager",
"author_email": "akira@narux.de",
"url": "https://revpimodio.org/revpipyplc/",
"license": "LGPLv3",
"version": "0.2.12",
"name": "revpipycontrol",
"description": "PLC Loader für Python-Projekte auf den RevolutionPi",
"long_description": ""
"Dieses Programm startet beim Systemstart ein angegebenes Python PLC\n"
"Programm. Es überwacht das Programm und startet es im Fehlerfall neu.\n"
"Bei Abstruz kann das gesamte /dev/piControl0 auf 0x00 gesettz werden.\n"
"Außerdem stellt es einen XML-RPC Server bereit, über den die Software\n"
"auf den RevPi geladen werden kann. Das Prozessabbild kann über ein Tool\n"
"zur Laufzeit überwacht werden.",
}
if platform == "linux":
from setuptools import setup
setup(
maintainer="Sven Sager",
maintainer_email="akira@revpimodio.org",
scripts=["data/revpipycontrol"],
data_files=[
("share/applications", ["data/revpipycontrol.desktop"]),
("share/icons/hicolor/32x32/apps", ["data/revpipycontrol.png"]),
("share/revpipycontrol", glob("revpipycontrol/*.*")),
],
install_requires=["tkinter"],
classifiers=[
"License :: OSI Approved :: "
"GNU Lesser General Public License v3 (LGPLv3)",
"Operating System :: POSIX :: Linux",
],
cmdclass={"install_egg_info": MyEggInfo},
**globsetup
)
elif platform == "win32":
import sys
from cx_Freeze import setup, Executable
sys.path.append("revpipycontrol")
exe = Executable(
script="revpipycontrol/revpipycontrol.py",
base="Win32GUI",
compress=False,
copyDependentFiles=True,
appendScriptToExe=True,
appendScriptToLibrary=False,
icon="data/revpipycontrol.ico"
)
setup(
options={"build_exe": {
"include_files": [
"revpipycontrol/revpipycontrol.png",
# "m4server/locale"
]
}},
executables=[exe],
**globsetup
)

5
stdeb.cfg Normal file
View File

@@ -0,0 +1,5 @@
[DEFAULT]
Debian-Version=1
Depends3=python3-tk
Section=universe/x11
Suite=stable