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 # Verbose logging add a -v or -vv
DAEMON_ARGS="-d" DAEMON_ARGS="-d"
# Codepage of files # Codepage for Python (do not change)
export LANG=C.UTF-8 export LANG=C.UTF-8

View File

@@ -16,11 +16,18 @@
PATH=/sbin:/usr/sbin:/bin:/usr/bin PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="RevPiPyLoad to run plc program" DESC="RevPiPyLoad to run plc program"
NAME=revpipyload NAME=revpipyload
DAEMON=/usr/share/revpipyload/revpipyload.py
DAEMON_ARGS="-d" DAEMON_ARGS="-d"
PIDFILE=/var/run/$NAME.pid PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME 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 # Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0 [ -x "$DAEMON" ] || exit 0

View File

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

View File

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

View File

@@ -44,7 +44,7 @@ setup(
long_description="" long_description=""
"Dieses Programm startet beim Systemstart ein angegebenes Python PLC\n" "Dieses Programm startet beim Systemstart ein angegebenes Python PLC\n"
"Programm. Es überwacht das Programm und startet es im Fehlerfall neu.\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" "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" "auf den RevPi geladen werden kann. Das Prozessabbild kann über ein Tool\n"
"zur Laufzeit überwacht werden.", "zur Laufzeit überwacht werden.",