diff --git a/revpipyload.e4p b/revpipyload.e4p index b5a080e..6374bcd 100644 --- a/revpipyload.e4p +++ b/revpipyload.e4p @@ -1,7 +1,7 @@ - + en_US @@ -9,7 +9,7 @@ Python3 Console Dieser Loader wird über das Init-System geladen und führt das angegebene Pythonprogramm aus. Es ist für den RevolutionPi gedacht um automatisch das SPS-Programm zu starten. - 0.2.11 + 0.3.0 Sven Sager akira@narux.de diff --git a/revpipyload/proginit.py b/revpipyload/proginit.py index adce346..6340bd6 100644 --- a/revpipyload/proginit.py +++ b/revpipyload/proginit.py @@ -63,7 +63,7 @@ def configure(): pargs.conffile = os.path.join(startdir, pargs.conffile) if pargs.logfile is not None and os.path.dirname(pargs.logfile) == "": pargs.logfile = os.path.join(startdir, pargs.logfile) - + # Prüfen ob als Daemon ausgeführt werden soll global forked pidfile = "/var/run/revpipyload.pid" diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index 2ce5da4..2442773 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -35,8 +35,10 @@ import proginit import os import shlex import signal +import socket import subprocess import tarfile +import warnings import zipfile from concurrent import futures from configparser import ConfigParser @@ -47,13 +49,21 @@ from sys import stdout as sysstdout from tempfile import mktemp from threading import Thread, Event, Lock from time import sleep, asctime +from timeit import default_timer 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.11" +pyloadverion = "0.3.0" + + +def _zeroprocimg(): + """Setzt Prozessabbild auf NULL.""" + if os.path.exists(procimg): + f = open(procimg, "w+b", 0) + f.write(bytes(4096)) class LogReader(): @@ -304,7 +314,7 @@ class RevPiPlc(Thread): elif proginit.pargs.logfile is not None: logfile = proginit.pargs.logfile - if not logfile is None: + if logfile is not None: logfile = PipeLogwriter(logfile) proginit.logger.debug("leave RevPiPlc._configureplw()") @@ -333,12 +343,6 @@ class RevPiPlc(Thread): proginit.logger.debug("leave RevPiPlc._spopen()") return sp - def _zeroprocimg(self): - """Setzt Prozessabbild auf NULL.""" - if os.path.exists("/dev/piControl0"): - f = open("/dev/piControl0", "w+b", 0) - f.write(bytes(4096)) - def newlogfile(self): """Konfiguriert die FileHandler auf neue Logdatei.""" proginit.logger.debug("enter RevPiPlc.newlogfile()") @@ -387,7 +391,7 @@ class RevPiPlc(Thread): ) ) if self.zeroonerror: - self._zeroprocimg() + _zeroprocimg() proginit.logger.warning( "set piControl0 to ZERO after PLC program error") @@ -395,7 +399,7 @@ class RevPiPlc(Thread): # PLC Python Programm sauber beendet proginit.logger.info("plc program did a clean exit") if self.zeroonexit: - self._zeroprocimg() + _zeroprocimg() proginit.logger.info( "set piControl0 to ZERO after PLC program returns " "clean exitcode") @@ -457,7 +461,7 @@ class RevPiPlc(Thread): self.exitcode = self._procplc.poll() if self.zeroonexit and self.exitcode == 0 \ or self.zeroonerror and self.exitcode != 0: - self._zeroprocimg() + _zeroprocimg() if self._plw is not None: self._plw.stop() @@ -468,6 +472,132 @@ class RevPiPlc(Thread): proginit.logger.debug("leave RevPiPlc.stop()") +class RevPiSlave(Thread): + + def __init__(self): + """Instantiiert RevPiSlave-Klasse.""" + super().__init__() + self.deadtime = 0.5 + self._evt_exit = Event() + self.zeroonerror = False + self.zeroonexit = False + + def run(self): + proginit.logger.debug("enter RevPiSlave.run()") + + msgcli = [b'DATA', b'PICT', b'SEND'] + + # Socket öffnen und konfigurieren + self.so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + while not self._evt_exit.is_set(): + try: + self.so.bind(("", 55234)) + except: + warnings.warn("can not bind socket - retry", Warning) + self._evt_exit.wait(1) + else: + break + self.so.listen(1) + + self.rpi = None + while not self._evt_exit.is_set(): + + # Verbindung annehmen + proginit.logger.info("starte accept") + try: + self.rpi, addr = self.so.accept() + except: + proginit.logger.exception("after accept") + continue + + # Prozessabbild öffnen + fh_proc = open(procimg, "r+b", 0) + + # Erste Meldung erhalten + meldung = self.rpi.recv(12) + + comtime = 0 + while meldung[:4] in msgcli: + ot = default_timer() + + # Meldung zerlegen + command = meldung[:4] + try: + startval = int(meldung[4:-4]) + except: + startval = 0 + try: + lenval = int(meldung[-4:]) + except: + lenval = 0 + + if command == b'PICT': + # piCtory Konfiguration senden + proginit.logger.debug("transfair pictory configuration") + fh_pic = open(configrsc, "rb") + while True: + data = fh_pic.read(1024) + if data: + self.rpi.send(data) + else: + fh_pic.close() + break + + # Abschlussmeldung + self.rpi.send(b'PICOK') + + if command == b'DATA': + # Processabbild übertragen + bcount = 0 + blength = lenval + fh_proc.seek(startval) + while bcount < blength: + self.rpi.send(fh_proc.read(1024)) + bcount += 1024 + + if command == b'SEND': + # Ausgänge empfangen + block = self.rpi.recv(lenval) + fh_proc.seek(startval) + fh_proc.write(block) + + # Nächste Meldung erhalten + meldung = self.rpi.recv(12) + + # Verarbeitungszeit prüfen + comtime = default_timer() - ot + if comtime > self.deadtime: + proginit.logger.error( + "runtime more than {} ms: {}".format( + int(self.deadtime * 1000), int(comtime * 1000) + ) + ) + break + + fh_proc.close() + self.rpi.shutdown(socket.SHUT_RDWR) + self.rpi.close() + + if self.zeroonexit or comtime > self.deadtime and self.zeroonerror: + _zeroprocimg() + + # Socket schließen + self.so.close() + + proginit.logger.debug("leave RevPiSlave.run()") + + def stop(self): + """Beendet Slaveausfuehrung.""" + proginit.logger.debug("enter RevPiSlave.stop()") + self._evt_exit.set() + + if self.rpi is not None: + self.rpi.close() + self.so.shutdown(socket.SHUT_RDWR) + + proginit.logger.debug("leave RevPiSlave.stop()") + + class RevPiPyLoad(): """Hauptklasse, die alle Funktionen zur Verfuegung stellt. @@ -606,26 +736,35 @@ class RevPiPyLoad(): def _plcthread(self): """Konfiguriert den PLC-Thread fuer die Ausfuehrung. @returns: PLC-Thread Object or None""" + th_plc = None - # Prüfen ob Programm existiert - if not os.path.exists(os.path.join(self.plcworkdir, self.plcprog)): - proginit.logger.error("plc file does not exists {}".format( - os.path.join(self.plcworkdir, self.plcprog) - )) - return None + if self.plcslave: + # Slaveausfuehrung übergeben + th_plc = RevPiSlave() + th_plc.zeroonerror = self.zerooneerror + th_plc.zeroonexit = self.zeroonexit - proginit.logger.debug("create PLC watcher") - th_plc = RevPiPlc( - os.path.join(self.plcworkdir, self.plcprog), - self.plcarguments, - self.pythonver - ) - th_plc.autoreload = self.autoreload - th_plc.gid = int(self.globalconfig["DEFAULT"].get("plcgid", 65534)) - th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534)) + else: + # Prüfen ob Programm existiert + if not os.path.exists(os.path.join(self.plcworkdir, self.plcprog)): + proginit.logger.error("plc file does not exists {}".format( + os.path.join(self.plcworkdir, self.plcprog) + )) + return None + + proginit.logger.debug("create PLC watcher") + th_plc = RevPiPlc( + os.path.join(self.plcworkdir, self.plcprog), + self.plcarguments, + self.pythonver + ) + th_plc.autoreload = self.autoreload + th_plc.gid = int(self.globalconfig["DEFAULT"].get("plcgid", 65534)) + th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534)) th_plc.zeroonerror = self.zeroonerror - th_plc.zeroonexit = self.zeroonexit - proginit.logger.debug("created PLC watcher") + th_plc.zeroonexit = self.zeroonexit + proginit.logger.debug("created PLC watcher") + return th_plc def _sigexit(self, signum, frame): diff --git a/setup.py b/setup.py index 0ef2d3e..ae17d66 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( license="LGPLv3", name="revpipyload", - version="0.2.11", + version="0.3.0", scripts=["data/revpipyload"],