xml_plcupload um pictory-flags erweitert

docstrings
defaultwerte geändert und für deb vorbereitet
pythonversion für plc programm kann angegeben werden
This commit is contained in:
2017-03-10 16:27:19 +01:00
parent e2f7ed41ea
commit 6cb1a93dae
6 changed files with 102 additions and 46 deletions

View File

@@ -3,5 +3,5 @@
# Verbose logging add a -v or -vv
DAEMON_ARGS="-d"
# Codepage of files
export LANG=C.UTF-8
# Codepage for Python (do not change)
export LANG=C.UTF-8

View File

@@ -16,11 +16,18 @@
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="RevPiPyLoad to run plc program"
NAME=revpipyload
DAEMON=/usr/share/revpipyload/revpipyload.py
DAEMON_ARGS="-d"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Check install dir
if [ -d /usr/local/share/revpipyload ]
then
DAEMON=/usr/share/revpipyload/revpipyload.py
else
DAEMON=/usr/share/revpipyload/revpipyload.py
fi
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

View File

@@ -4,7 +4,8 @@ autostart=1
plcworkdir=/var/lib/revpipyload
plcprogram=program.py
plcslave=0
xmlrpc=1
pythonversion=3
xmlrpc=0
xmlrpcport=55123
zeroonerror=1
zeroonexit=1

View File

@@ -1,3 +1,7 @@
#!/bin/sh
exec "/usr/share/revpipyload/revpipyload.py" "$@"
if [ -d /usr/local/share/revpipyload ]
then
exec "/usr/local/share/revpipyload/revpipyload.py" "$@"
else
exec "/usr/share/revpipyload/revpipyload.py" "$@"
fi

View File

@@ -15,7 +15,7 @@ import subprocess
import tarfile
import zipfile
from concurrent import futures
from shutil import rmtree
from shutil import rmtree, copyfile
from tempfile import mktemp
from threading import Thread, Event
from time import sleep, asctime
@@ -23,6 +23,7 @@ from xmlrpc.client import Binary
from xmlrpc.server import SimpleXMLRPCServer
configrsc = "/opt/KUNBUS/config.rsc"
picontrolreset = "/opt/KUNBUS/piControlReset"
procimg = "/dev/piControl0"
pyloadverion = "0.2.3"
@@ -49,10 +50,7 @@ class LogReader():
"""Gibt neue Zeilen ab letzen Aufruf zurueck.
@returns: list() mit neuen Zeilen"""
if not os.access(proginit.logapp, os.R_OK):
proginit.logger.error(
"can not access logfile {}".format(proginit.logapp)
)
return None
return []
else:
if self.fhapp is None or self.fhapp.closed:
self.fhapp = open(proginit.logapp)
@@ -79,7 +77,7 @@ class LogReader():
proginit.logger.error(
"can not access logfile {}".format(proginit.logapp)
)
return None
return ""
else:
if self.fhapp is None or self.fhapp.closed:
self.fhapp = open(proginit.logapp)
@@ -93,7 +91,7 @@ class LogReader():
proginit.logger.error(
"can not access logfile {}".format(proginit.logplc)
)
return None
return []
else:
if self.fhplc is None or self.fhplc.closed:
self.fhplc = open(proginit.logplc)
@@ -120,7 +118,7 @@ class LogReader():
proginit.logger.error(
"can not access logfile {}".format(proginit.logplc)
)
return None
return ""
else:
if self.fhplc is None or self.fhplc.closed:
self.fhplc = open(proginit.logplc)
@@ -130,7 +128,7 @@ class LogReader():
class RevPiPlc(Thread):
def __init__(self, program):
def __init__(self, program, pversion):
"""Instantiiert RevPiPlc-Klasse."""
super().__init__()
self.autoreload = False
@@ -138,6 +136,7 @@ class RevPiPlc(Thread):
self.exitcode = None
self._program = program
self._procplc = None
self._pversion = pversion
self.zeroonerror = False
self.zeroonexit = False
@@ -146,11 +145,13 @@ class RevPiPlc(Thread):
if os.path.exists("/dev/piControl0"):
f = open("/dev/piControl0", "w+b", 0)
f.write(bytes(4096))
proginit.logger.warning("set piControl0 to ZERO")
def run(self):
"""Fuehrt PLC-Programm aus und ueberwacht es."""
lst_proc = shlex.split("/usr/bin/env python3 -u " + self._program)
if self._pversion == 2:
lst_proc = shlex.split("/usr/bin/env python2 -u " + self._program)
else:
lst_proc = shlex.split("/usr/bin/env python3 -u " + self._program)
# Ausgaben konfigurieren und ggf. umleiten
fh = None
@@ -169,7 +170,8 @@ class RevPiPlc(Thread):
# Prozess erstellen
proginit.logger.info("start plc program {}".format(self._program))
self._procplc = subprocess.Popen(
lst_proc, bufsize=1, stdout=fh, stderr=subprocess.STDOUT
lst_proc, cwd=os.path.dirname(self._program),
bufsize=1, stdout=fh, stderr=subprocess.STDOUT
)
while not self._evt_exit.is_set():
@@ -188,18 +190,23 @@ class RevPiPlc(Thread):
)
if self.zeroonerror:
self._zeroprocimg()
proginit.logger.warning(
"set piControl0 to ZERO after PLC program error")
else:
# PLC Python Programm sauber beendet
proginit.logger.info("plc program did a clean exit")
if self.zeroonexit:
self._zeroprocimg()
proginit.logger.info(
"set piControl0 to ZERO after PLC program returns "
"clean exitcode")
if not self._evt_exit.is_set() and self.autoreload:
# Prozess neu starten
self._procplc = subprocess.Popen(
lst_proc, bufsize=1, stdout=fh,
stderr=subprocess.STDOUT
lst_proc, cwd=os.path.dirname(self._program),
bufsize=1, stdout=fh, stderr=subprocess.STDOUT
)
if self.exitcode == 0:
proginit.logger.warning(
@@ -217,6 +224,7 @@ class RevPiPlc(Thread):
# Prozess beenden
count = 0
proginit.logger.info("term plc program {}".format(self._program))
# TODO: Prüfen ob es überhautp noch läuft
self._procplc.terminate()
while self._procplc.poll() is None and count < 10:
count += 1
@@ -288,16 +296,15 @@ class RevPiPyLoad(proginit.ProgInit):
self.autostart = int(self.globalconfig["DEFAULT"].get("autostart", 0))
self.plcprog = self.globalconfig["DEFAULT"].get("plcprogram", None)
self.plcworkdir = self.globalconfig["DEFAULT"].get(
"plcworkdir", "/var/lib/revpipyload"
)
"plcworkdir", "/var/lib/revpipyload")
self.plcslave = int(self.globalconfig["DEFAULT"].get("plcslave", 0))
self.pythonver = int(
self.globalconfig["DEFAULT"].get("pythonversion", 3))
self.xmlrpc = int(self.globalconfig["DEFAULT"].get("xmlrpc", 1))
self.zerooneerror = int(
self.globalconfig["DEFAULT"].get("zeroonerror", 1)
)
self.globalconfig["DEFAULT"].get("zeroonerror", 1))
self.zeroonexit = int(
self.globalconfig["DEFAULT"].get("zeroonexit", 1)
)
self.globalconfig["DEFAULT"].get("zeroonexit", 1))
# Workdirectory wechseln
os.chdir(self.plcworkdir)
@@ -361,7 +368,8 @@ class RevPiPyLoad(proginit.ProgInit):
return
proginit.logger.debug("create PLC watcher")
th_plc = RevPiPlc(os.path.join(self.plcworkdir, self.plcprog))
th_plc = RevPiPlc(
os.path.join(self.plcworkdir, self.plcprog), self.pythonver)
th_plc.autoreload = self.autoreload
th_plc.zeroonerror = self.zerooneerror
th_plc.zeroonexit = self.zeroonexit
@@ -369,7 +377,7 @@ class RevPiPyLoad(proginit.ProgInit):
return th_plc
def _sigexit(self, signum, frame):
"""Signal handler to clean an exit program."""
"""Signal handler to clean and exit program."""
proginit.logger.debug("got exit signal")
self.stop()
@@ -378,19 +386,28 @@ class RevPiPyLoad(proginit.ProgInit):
proginit.logger.debug("got reload config signal")
self.evt_loadconfig.set()
def packapp(self, mode="tar"):
"""Erzeugt aus dem PLC-Programm ein TAR-File."""
def packapp(self, mode="tar", pictory=False):
"""Erzeugt aus dem PLC-Programm ein TAR-File.
@param mode: Packart 'tar' oder 'zip'
@param pictory: piCtory Konfiguration mit einpacken"""
filename = mktemp(suffix=".packed", prefix="plc")
# try:
# TODO: Fehlerabfang
if mode == "zip":
fh_pack = zipfile.ZipFile(filename, mode="w")
wd = os.walk("./")
for tup_dir in wd:
for file in tup_dir[2]:
fh_pack.write(os.path.join(tup_dir[0], file)[2:])
arcname = os.path.join(
os.path.basename(self.plcworkdir), tup_dir[0][2:], file)
fh_pack.write(os.path.join(tup_dir[0], file), arcname=arcname)
if pictory:
fh_pack.write(configrsc, arcname="config.rsc")
else:
fh_pack = tarfile.open(name=filename, mode="w:gz")
fh_pack.add(".")
fh_pack = tarfile.open(
name=filename, mode="w:gz", dereference=True)
fh_pack.add(".", arcname=os.path.basename(self.plcworkdir))
if pictory:
fh_pack.add(configrsc, arcname="config.rsc")
fh_pack.close()
return filename
@@ -458,23 +475,25 @@ class RevPiPyLoad(proginit.ProgInit):
return lst_file
def xml_getpictoryrsc(self):
"""Gibt die config.rsc Datei von piCotry zurueck."""
"""Gibt die config.rsc Datei von piCotry zurueck.
@returns: xmlrpc.client.Binary()"""
proginit.logger.debug("xmlrpc call getpictoryrsc")
with open(configrsc, "rb") as fh:
buff = fh.read()
return Binary(buff)
def xml_getprocimg(self):
"""Gibt die Rohdaten aus piControl0 zurueck."""
"""Gibt die Rohdaten aus piControl0 zurueck.
@returns: xmlrpc.client.Binary()"""
proginit.logger.debug("xmlrpc call getprocimg")
with open(procimg, "rb") as fh:
buff = fh.read()
return Binary(buff)
def xml_plcdownload(self, mode="tar"):
def xml_plcdownload(self, mode="tar", pictory=False):
proginit.logger.debug("xmlrpc call plcdownload")
# TODO: Daten blockweise übertragen
file = self.packapp(mode)
file = self.packapp(mode, pictory)
if os.path.exists(file):
fh = open(file, "rb")
xmldata = Binary(fh.read())
@@ -516,7 +535,7 @@ class RevPiPyLoad(proginit.ProgInit):
else:
return -1
def xml_plcupload(self, filedata):
def xml_plcupload(self, filedata, pictory=False, reset=False):
proginit.logger.debug("xmlrpc call plcupload")
# TODO: Daten blockweise annehmen
if filedata is None:
@@ -533,17 +552,36 @@ class RevPiPyLoad(proginit.ProgInit):
if tarfile.is_tarfile(filename):
fh_pack = tarfile.open(filename)
elif zipfile.is_zipfile(filename):
fh_pack = zipfile.open(filename)
fh_pack = zipfile.ZipFile.open(filename)
if fh_pack is not None:
fh_pack.extractall()
fh_pack.extractall(".")
fh_pack.close()
os.remove(filename)
return True
if pictory and os.path.exists("./config.rsc"):
try:
# Nur Daten kopieren damit Eigenschaften gleich bleiben
copyfile("./config.rsc", configrsc)
os.remove("./config.rsc")
except:
return -3
else:
if reset:
return os.system(picontrolreset)
else:
return 0
elif pictory:
return -2
else:
# Sauber
return 0
# Kein Archiv
os.remove(filename)
return False
return -1
def xml_plcuploadclean(self):
proginit.logger.debug("xmlrpc call plcuploadclean")
@@ -587,7 +625,13 @@ class RevPiPyLoad(proginit.ProgInit):
self.evt_loadconfig.set()
def xml_setpictoryrsc(self, filebytes, reset=False):
"""Schreibt die config.rsc Datei von piCotry."""
"""Schreibt die config.rsc Datei von piCotry.
@param filebytes: xmlrpc.client.Binary()-Objekt
@param reset: Reset piControl Device
@returns: Statuscode
"""
proginit.logger.debug("xmlrpc call setpictoryrsc")
try:
with open(configrsc, "wb") as fh:
@@ -596,7 +640,7 @@ class RevPiPyLoad(proginit.ProgInit):
return -1
else:
if reset:
return os.system("/opt/KUNBUS/piControlReset")
return os.system(picontrolreset)
else:
return 0

View File

@@ -44,7 +44,7 @@ setup(
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"
"Bei Absturz kann das gesamte /dev/piControl0 auf 0x00 gesetzt 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.",