From 8b46383ba17a3e6c6d8eaf7149d0d3e092b0758b Mon Sep 17 00:00:00 2001 From: NaruX Date: Mon, 27 Mar 2017 16:55:02 +0200 Subject: [PATCH 1/3] slave funktion begonnen --- revpipyload.e4p | 4 +- revpipyload/revpipyload.py | 164 ++++++++++++++++++++++++++++++------- setup.py | 2 +- 3 files changed, 138 insertions(+), 32 deletions(-) diff --git a/revpipyload.e4p b/revpipyload.e4p index 912618d..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.10 + 0.3.0 Sven Sager akira@narux.de diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index fa9f274..55a3c06 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -33,8 +33,10 @@ begrenzt werden! import gzip import proginit import os +import pickle import shlex import signal +import socket import subprocess import tarfile import zipfile @@ -53,7 +55,14 @@ from xmlrpc.server import SimpleXMLRPCServer configrsc = "/opt/KUNBUS/config.rsc" picontrolreset = "/opt/KUNBUS/piControlReset" procimg = "/dev/piControl0" -pyloadverion = "0.2.10" +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 +313,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 +342,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 +390,7 @@ class RevPiPlc(Thread): ) ) if self.zeroonerror: - self._zeroprocimg() + _zeroprocimg() proginit.logger.warning( "set piControl0 to ZERO after PLC program error") @@ -395,7 +398,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 +460,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 +471,100 @@ class RevPiPlc(Thread): proginit.logger.debug("leave RevPiPlc.stop()") +class RevPiSlave(Thread): + + def __init__(self): + """Instantiiert RevPiSlave-Klasse.""" + super().__init__() + 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) + self.so.bind(("", 55234)) + 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(4) + + while meldung in msgcli: + + if meldung == b'PICT': + # piCtory Konfiguration senden + 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') + proginit.logger.debug("pictory ende") + + if meldung == b'DATA': + # Processabbild übertragen + block = 0 + fh_proc.seek(0) + while block < 4: + self.rpi.send(fh_proc.read(1024)) + block += 1 + proginit.logger.debug("procimg ende") + + if meldung == b'SEND': + # Ausgänge empfangen + block = self.rpi.recv(1024) + test = pickle.loads(block) + fh_proc.seek(test[0]) + fh_proc.write(test[1]) + proginit.logger.debug(test) + + # Nächste Meldung erhalten + meldung = self.rpi.recv(4) + + fh_proc.close() + self.rpi.shutdown(socket.SHUT_RDWR) + self.rpi.close() + + # 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 +703,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 + + 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.zerooneerror + th_plc.zeroonexit = self.zeroonexit + proginit.logger.debug("created PLC watcher") - 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.zerooneerror - 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 7c7ca4f..ae17d66 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( license="LGPLv3", name="revpipyload", - version="0.2.10", + version="0.3.0", scripts=["data/revpipyload"], From a96abb11a6568fb8182646d5cf7d5fcccfe3e150 Mon Sep 17 00:00:00 2001 From: NaruX Date: Tue, 28 Mar 2017 08:20:48 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Laufzeit=C3=BCberwachung=20eingebaut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- revpipyload/proginit.py | 2 +- revpipyload/revpipyload.py | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) 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 55a3c06..4d9a470 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -39,6 +39,7 @@ import signal import socket import subprocess import tarfile +import warnings import zipfile from concurrent import futures from configparser import ConfigParser @@ -49,6 +50,7 @@ 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 @@ -476,6 +478,7 @@ 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 @@ -487,11 +490,17 @@ class RevPiSlave(Thread): # Socket öffnen und konfigurieren self.so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.so.bind(("", 55234)) + 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 @@ -508,10 +517,13 @@ class RevPiSlave(Thread): # Erste Meldung erhalten meldung = self.rpi.recv(4) + comtime = 0 while meldung in msgcli: + ot = default_timer() if meldung == b'PICT': # piCtory Konfiguration senden + proginit.logger.debug("transfair pictory configuration") fh_pic = open(configrsc, "rb") while True: data = fh_pic.read(1024) @@ -523,32 +535,46 @@ class RevPiSlave(Thread): # Abschlussmeldung self.rpi.send(b'PICOK') - proginit.logger.debug("pictory ende") if meldung == b'DATA': + proginit.logger.debug("read process image") + # Processabbild übertragen block = 0 fh_proc.seek(0) while block < 4: self.rpi.send(fh_proc.read(1024)) block += 1 - proginit.logger.debug("procimg ende") if meldung == b'SEND': + proginit.logger.debug("write to process image") + # Ausgänge empfangen block = self.rpi.recv(1024) test = pickle.loads(block) fh_proc.seek(test[0]) fh_proc.write(test[1]) - proginit.logger.debug(test) # Nächste Meldung erhalten meldung = self.rpi.recv(4) + # 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() @@ -562,6 +588,7 @@ class RevPiSlave(Thread): if self.rpi is not None: self.rpi.close() self.so.shutdown(socket.SHUT_RDWR) + proginit.logger.debug("leave RevPiSlave.stop()") From d98854dbca47c9d5d4a2fbd7ac308d82721ba7e9 Mon Sep 17 00:00:00 2001 From: NaruX Date: Tue, 28 Mar 2017 09:50:50 +0200 Subject: [PATCH 3/3] =?UTF-8?q?meldungen=20mit=20adress=20und=20l=C3=A4nge?= =?UTF-8?q?nangabe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- revpipyload/revpipyload.py | 44 ++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index 4d9a470..8c5add0 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -33,7 +33,6 @@ begrenzt werden! import gzip import proginit import os -import pickle import shlex import signal import socket @@ -515,13 +514,24 @@ class RevPiSlave(Thread): fh_proc = open(procimg, "r+b", 0) # Erste Meldung erhalten - meldung = self.rpi.recv(4) + meldung = self.rpi.recv(12) comtime = 0 - while meldung in msgcli: + while meldung[:4] in msgcli: ot = default_timer() - if meldung == b'PICT': + # 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") @@ -536,27 +546,23 @@ class RevPiSlave(Thread): # Abschlussmeldung self.rpi.send(b'PICOK') - if meldung == b'DATA': - proginit.logger.debug("read process image") - + if command == b'DATA': # Processabbild übertragen - block = 0 - fh_proc.seek(0) - while block < 4: + bcount = 0 + blength = lenval + fh_proc.seek(startval) + while bcount < blength: self.rpi.send(fh_proc.read(1024)) - block += 1 - - if meldung == b'SEND': - proginit.logger.debug("write to process image") + bcount += 1024 + if command == b'SEND': # Ausgänge empfangen - block = self.rpi.recv(1024) - test = pickle.loads(block) - fh_proc.seek(test[0]) - fh_proc.write(test[1]) + block = self.rpi.recv(lenval) + fh_proc.seek(startval) + fh_proc.write(block) # Nächste Meldung erhalten - meldung = self.rpi.recv(4) + meldung = self.rpi.recv(12) # Verarbeitungszeit prüfen comtime = default_timer() - ot