diff --git a/debian/revpipyload.logrotate b/debian/revpipyload.logrotate index 4097d2c..9eb1bd6 100644 --- a/debian/revpipyload.logrotate +++ b/debian/revpipyload.logrotate @@ -1,12 +1,4 @@ /var/log/revpipyload -{ - rotate 6 - weekly - compress - delaycompress - missingok - notifempty -} /var/log/revpipyloadapp { rotate 6 @@ -15,4 +7,7 @@ delaycompress missingok notifempty + postrotate + kill -SIGUSR1 `cat /var/run/revpipyload.pid` > /dev/null 2>&1 || true + endscript } diff --git a/doc/proginit.html b/doc/proginit.html new file mode 100644 index 0000000..b22ec58 --- /dev/null +++ b/doc/proginit.html @@ -0,0 +1,50 @@ + + +proginit + + + +

+proginit

+

+Main functions of our program. +

+

+Global Attributes

+ + +
forked
globalconffile
logapp
logger
logplc
pargs
+

+Classes

+ + +
None
+

+Functions

+ + + + + + + + +
cleanupClean up program.
configureInitialize general program functions.
+

+ +

cleanup

+cleanup() +

+Clean up program. +

+
Up
+

+ +

configure

+configure() +

+Initialize general program functions. +

+
Up
+
+ \ No newline at end of file diff --git a/doc/revpipyload.html b/doc/revpipyload.html new file mode 100644 index 0000000..04a1008 --- /dev/null +++ b/doc/revpipyload.html @@ -0,0 +1,699 @@ + + +revpipyload + + + +

+revpipyload

+

+Revolution Pi Python PLC Loader. +

+Stellt das RevPiPyLoad Programm bereit. Dieses Programm lauft als Daemon auf +dem Revolution Pi. Es stellt Funktionen bereit, die es ermoeglichen ein Python +Programm zu starten und fuehrt dessen Ausgaben in eine Logdatei. Die Logdaten +koennen am Pi selber oder ueber eine XML-RPC Schnittstelle ausgelesen werden. +

+Dieser Daemon prueft ob das Python Programm noch lauft und kann es im Fall +eines Absturzes neu starten. +

+Ueber diesen Daemon kann die gesamte piCtory Konfiguration exportiert oder +importiert, ein Dump vom Prozessabbild gezogen und das eigene Python +Programm hochgeladen werden. +

+Es kann von dem Python Programm auch eine Archivdatei herunterladen werden, +welche optional auch die piCtory Konfiguraiton beinhaltet. Damit kann man sehr +schnell auf einem Revolution Pi das Programm inkl. piCtory Konfiguration +austauschen. +

+Die Zugriffsmoeglichkeiten koennen ueber einen Konfigurationsparameter +begrenzt werden! +

+

+Global Attributes

+ + +
configrsc
picontrolreset
procimg
pyloadverion
+

+Classes

+ + + + + + + + + + + + + + +
LogReaderErmoeglicht den Zugriff auf die Logdateien.
PipeLogwriter
RevPiPlcVerwaltet das PLC Python Programm.
RevPiPyLoadHauptklasse, die alle Funktionen zur Verfuegung stellt.
+

+Functions

+ + +
None
+

+ +

LogReader

+

+Ermoeglicht den Zugriff auf die Logdateien. +

+ Beinhaltet Funktionen fuer den Abruf der gesamten Logdatei fuer das + RevPiPyLoad-System und die Logdatei der PLC-Anwendung. + Ausserdem koennen nur neue Zeilen abgerufen werden, um eine dynamische + Logansicht zu ermoeglichen. +

+ +

+

+Derived from

+None +

+Class Attributes

+ + +
None
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + +
LogReaderInstantiiert LogReader-Klasse.
closeallFuehrt close auf File Handler durch.
get_applinesGibt neue Zeilen ab letzen Aufruf zurueck.
get_applogGibt die gesamte Logdatei zurueck.
get_plclinesGibt neue Zeilen ab letzen Aufruf zurueck.
get_plclogGibt die gesamte Logdatei zurueck.
+

+Static Methods

+ + +
None
+ +

+LogReader (Constructor)

+LogReader() +

+Instantiiert LogReader-Klasse. +

+

+LogReader.closeall

+closeall() +

+Fuehrt close auf File Handler durch. +

+

+LogReader.get_applines

+get_applines() +

+Gibt neue Zeilen ab letzen Aufruf zurueck. +

+
Returns:
+
+list() mit neuen Zeilen +
+
+

+LogReader.get_applog

+get_applog() +

+Gibt die gesamte Logdatei zurueck. +

+
Returns:
+
+str() mit Logdaten +
+
+

+LogReader.get_plclines

+get_plclines() +

+Gibt neue Zeilen ab letzen Aufruf zurueck. +

+
Returns:
+
+list() mit neuen Zeilen +
+
+

+LogReader.get_plclog

+get_plclog() +

+Gibt die gesamte Logdatei zurueck. +

+
Returns:
+
+str() mit Logdaten +
+
+
Up
+

+ +

PipeLogwriter

+ +

+Derived from

+Thread +

+Class Attributes

+ + +
None
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + +
PipeLogwriter
run
stop
+

+Static Methods

+ + +
None
+ +

+PipeLogwriter (Constructor)

+PipeLogwriter(fh) + +

+PipeLogwriter.run

+run() + +

+PipeLogwriter.stop

+stop() + +
Up
+

+ +

RevPiPlc

+

+Verwaltet das PLC Python Programm. +

+ Dieser Thread startet das PLC Python Programm und ueberwacht es. Sollte es + abstuerzen kann es automatisch neu gestartet werden. Die Ausgaben des + Programms werden in eine Logdatei umgeleitet, damit der Entwickler sein + Programm analysieren und debuggen kann. +

+ +

+

+Derived from

+Thread +

+Class Attributes

+ + +
None
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
RevPiPlcInstantiiert RevPiPlc-Klasse.
_configurefh
_setuppopenSetzt UID und GID fuer das PLC Programm.
_spopenStartet das PLC Programm.
_zeroprocimgSetzt Prozessabbild auf NULL.
newlogfile
runFuehrt PLC-Programm aus und ueberwacht es.
stopBeendet PLC-Programm.
+

+Static Methods

+ + +
None
+ +

+RevPiPlc (Constructor)

+RevPiPlc(program, arguments, pversion) +

+Instantiiert RevPiPlc-Klasse. +

+

+RevPiPlc._configurefh

+_configurefh() + +

+RevPiPlc._setuppopen

+_setuppopen() +

+Setzt UID und GID fuer das PLC Programm. +

+

+RevPiPlc._spopen

+_spopen(lst_proc, filenum=None) +

+Startet das PLC Programm. +

+
lst_proc:
+
+Prozessliste +
+
+
Returns:
+
+subprocess +
+
+

+RevPiPlc._zeroprocimg

+_zeroprocimg() +

+Setzt Prozessabbild auf NULL. +

+

+RevPiPlc.newlogfile

+newlogfile() + +

+RevPiPlc.run

+run() +

+Fuehrt PLC-Programm aus und ueberwacht es. +

+

+RevPiPlc.stop

+stop() +

+Beendet PLC-Programm. +

+
Up
+

+ +

RevPiPyLoad

+

+Hauptklasse, die alle Funktionen zur Verfuegung stellt. +

+ Hier wird die gesamte Konfiguraiton eingelesen und der ggf. aktivierte + XML-RPC-Server gestartet. +

+ +

+

+Derived from

+None +

+Class Attributes

+ + +
root
+

+Class Methods

+ + +
None
+

+Methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RevPiPyLoadInstantiiert RevPiPyLoad-Klasse.
_loadconfigLoad configuration file and setup modul.
_plcthreadKonfiguriert den PLC-Thread fuer die Ausfuehrung.
_sigexitSignal handler to clean and exit program.
_sigloadconfigSignal handler to load configuration.
_signewlogfileSignal handler to start new logfile.
packappErzeugt aus dem PLC-Programm ein TAR-File.
startStart plcload and PLC python program.
stopStop PLC python program and plcload.
xml_getconfigUebertraegt die RevPiPyLoad Konfiguration.
xml_getfilelistUebertraegt die Dateiliste vom plcworkdir.
xml_getpictoryrscGibt die config.rsc Datei von piCotry zurueck.
xml_getprocimgGibt die Rohdaten aus piControl0 zurueck.
xml_plcdownloadUebertraegt ein Archiv vom plcworkdir.
xml_plcexitcodeGibt den aktuellen exitcode vom PLC Programm zurueck.
xml_plcrunningPrueft ob das PLC Programm noch lauft.
xml_plcstartStartet das PLC Programm.
xml_plcstopStoppt das PLC Programm.
xml_plcuploadEmpfaengt Dateien fuer das PLC Programm.
xml_plcuploadcleanLoescht das gesamte plcworkdir Verzeichnis.
xml_reloadStartet RevPiPyLoad neu und verwendet neue Konfiguraiton.
xml_setconfigEmpfaengt die RevPiPyLoad Konfiguration.
xml_setpictoryrscSchreibt die config.rsc Datei von piCotry.
+

+Static Methods

+ + +
None
+ +

+RevPiPyLoad (Constructor)

+RevPiPyLoad() +

+Instantiiert RevPiPyLoad-Klasse. +

+

+RevPiPyLoad._loadconfig

+_loadconfig() +

+Load configuration file and setup modul. +

+

+RevPiPyLoad._plcthread

+_plcthread() +

+Konfiguriert den PLC-Thread fuer die Ausfuehrung. +

+
Returns:
+
+PLC-Thread Object or None +
+
+

+RevPiPyLoad._sigexit

+_sigexit(signum, frame) +

+Signal handler to clean and exit program. +

+

+RevPiPyLoad._sigloadconfig

+_sigloadconfig(signum, frame) +

+Signal handler to load configuration. +

+

+RevPiPyLoad._signewlogfile

+_signewlogfile(signum, frame) +

+Signal handler to start new logfile. +

+

+RevPiPyLoad.packapp

+packapp(mode="tar", pictory=False) +

+Erzeugt aus dem PLC-Programm ein TAR-File. +

+
mode:
+
+Packart 'tar' oder 'zip' +
pictory:
+
+piCtory Konfiguration mit einpacken +
+
+
Returns:
+
+Dateinamen des Archivs +
+
+

+RevPiPyLoad.start

+start() +

+Start plcload and PLC python program. +

+

+RevPiPyLoad.stop

+stop() +

+Stop PLC python program and plcload. +

+

+RevPiPyLoad.xml_getconfig

+xml_getconfig() +

+Uebertraegt die RevPiPyLoad Konfiguration. +

+
Returns:
+
+dict() der Konfiguration +
+
+

+RevPiPyLoad.xml_getfilelist

+xml_getfilelist() +

+Uebertraegt die Dateiliste vom plcworkdir. +

+
Returns:
+
+list() mit Dateinamen +
+
+

+RevPiPyLoad.xml_getpictoryrsc

+xml_getpictoryrsc() +

+Gibt die config.rsc Datei von piCotry zurueck. +

+
Returns:
+
+xmlrpc.client.Binary() +
+
+

+RevPiPyLoad.xml_getprocimg

+xml_getprocimg() +

+Gibt die Rohdaten aus piControl0 zurueck. +

+
Returns:
+
+xmlrpc.client.Binary() +
+
+

+RevPiPyLoad.xml_plcdownload

+xml_plcdownload(mode="tar", pictory=False) +

+Uebertraegt ein Archiv vom plcworkdir. +

+
mode:
+
+Archivart 'tar' 'zip' +
pictory:
+
+piCtory Konfiguraiton mit einpacken +
+
+
Returns:
+
+Binary() mit Archivdatei +
+
+

+RevPiPyLoad.xml_plcexitcode

+xml_plcexitcode() +

+Gibt den aktuellen exitcode vom PLC Programm zurueck. +

+
Returns:
+
+int() exitcode oder: + -1 laeuft noch + -2 Datei nicht gefunden + -3 Lief nie +
+
+

+RevPiPyLoad.xml_plcrunning

+xml_plcrunning() +

+Prueft ob das PLC Programm noch lauft. +

+
Returns:
+
+True, wenn das PLC Programm noch lauft +
+
+

+RevPiPyLoad.xml_plcstart

+xml_plcstart() +

+Startet das PLC Programm. +

+
Returns:
+
+int() Status: + -1 Programm lauft noch + -2 Datei nicht gefunden +
+
+

+RevPiPyLoad.xml_plcstop

+xml_plcstop() +

+Stoppt das PLC Programm. +

+
Returns:
+
+int() Exitcode vom PLC Programm + -1 PLC Programm lief nicht +
+
+

+RevPiPyLoad.xml_plcupload

+xml_plcupload(filedata, filename) +

+Empfaengt Dateien fuer das PLC Programm. +

+
filedata:
+
+GZIP Binary data der datei +
filename:
+
+Name inkl. Unterverzeichnis der Datei +
+
+
Returns:
+
+Ture, wenn Datei erfolgreich gespeichert wurde +
+
+

+RevPiPyLoad.xml_plcuploadclean

+xml_plcuploadclean() +

+Loescht das gesamte plcworkdir Verzeichnis. +

+
Returns:
+
+True, wenn erfolgreich +
+
+

+RevPiPyLoad.xml_reload

+xml_reload() +

+Startet RevPiPyLoad neu und verwendet neue Konfiguraiton. +

+

+RevPiPyLoad.xml_setconfig

+xml_setconfig(dc, loadnow=False) +

+Empfaengt die RevPiPyLoad Konfiguration. +

+
Returns:
+
+True, wenn erfolgreich angewendet +
+
+

+RevPiPyLoad.xml_setpictoryrsc

+xml_setpictoryrsc(filebytes, reset=False) +

+Schreibt die config.rsc Datei von piCotry. +

+
filebytes:
+
+xmlrpc.client.Binary()-Objekt +
reset:
+
+Reset piControl Device +
+
+
Returns:
+
+Statuscode: + 0 Alles erfolgreich + -1 Kann JSON-Datei nicht laden + -2 piCtory Elemente in JSON-Datei nicht gefunden + -3 Konnte Konfiguraiton nicht schreiben + Positive Zahl ist exitcode von piControlReset +
+
+
Up
+
+ \ No newline at end of file diff --git a/revpipyload.e4p b/revpipyload.e4p index 089f580..f87c5bd 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.9 + 0.2.10 Sven Sager akira@narux.de diff --git a/revpipyload/proginit.py b/revpipyload/proginit.py index 81b35bf..4898188 100644 --- a/revpipyload/proginit.py +++ b/revpipyload/proginit.py @@ -1,108 +1,121 @@ +# +# RevPiPyLoad +# +# Webpage: https://revpimodio.org/revpipyplc/ +# (c) Sven Sager, License: LGPLv3 +# # -*- coding: utf-8 -*- """Main functions of our program.""" import logging import sys from argparse import ArgumentParser -from configparser import ConfigParser from os import fork as osfork from os.path import exists as ospexists +forked = False +globalconffile = None logapp = "revpipyloadapp.log" logplc = "revpipyload.log" logger = None pargs = None -class ProgInit(): +def cleanup(): + """Clean up program.""" + # Logging beenden + logging.shutdown() - """Programmfunktionen fuer Parameter und Logger.""" - def __del__(self): - """Clean up program.""" - # Logging beenden - logging.shutdown() +def configure(): + """Initialize general program functions.""" - def __init__(self): - """Initialize general program functions.""" + # Command arguments + parser = ArgumentParser( + description="RevolutionPi Python3 Loader" + ) + parser.add_argument( + "-d", "--daemon", action="store_true", dest="daemon", + help="Run program as a daemon in background" + ) + parser.add_argument( + "-c", "--conffile", dest="conffile", + default="revpipyload.conf", + help="Application configuration file" + ) + parser.add_argument( + "-f", "--logfile", dest="logfile", + help="Save log entries to this file" + ) + parser.add_argument( + "-v", "--verbose", action="count", dest="verbose", + help="Switch on verbose logging" + ) + global pargs + pargs = parser.parse_args() - # Command arguments - parser = ArgumentParser( - description="RevolutionPi Python3 Loader" - ) - parser.add_argument( - "-d", "--daemon", action="store_true", dest="daemon", - help="Run program as a daemon in background" - ) - parser.add_argument( - "-c", "--conffile", dest="conffile", - default="revpipyload.conf", - help="Application configuration file" - ) - parser.add_argument( - "-f", "--logfile", dest="logfile", - help="Save log entries to this file" - ) - parser.add_argument( - "-v", "--verbose", action="count", dest="verbose", - help="Switch on verbose logging" - ) - global pargs - pargs = parser.parse_args() + # Prüfen ob als Daemon ausgeführt werden soll + global forked + pidfile = "/var/run/revpipyload.pid" + pid = 0 + if pargs.daemon and not forked: + # Prüfen ob daemon schon läuft + if ospexists(pidfile): + raise SystemError( + "program already running as daemon. check {}".format(pidfile) + ) - # Prüfen ob als Daemon ausgeführt werden soll - self.pidfile = "/var/run/revpipyload.pid" - self.pid = 0 - if pargs.daemon: - # Prüfen ob daemon schon läuft - if ospexists(self.pidfile): - raise SystemError( - "program already running as daemon. check {}".format( - self.pidfile - ) - ) + # Zum daemon machen + pid = osfork() + if pid > 0: + with open(pidfile, "w") as f: + f.write(str(pid)) + sys.exit(0) + else: + forked = True - self.pid = osfork() - if self.pid > 0: - with open(self.pidfile, "w") as f: - f.write(str(self.pid)) - sys.exit(0) + if pargs.daemon: + global logapp + global logplc - global logapp - global logplc + # Ausgaben umhängen in Logfile + logapp = "/var/log/revpipyloadapp" + logplc = "/var/log/revpipyload" + pargs.conffile = "/etc/revpipyload/revpipyload.conf" + sys.stdout = open(logplc, "a") + sys.stderr = sys.stdout - # Ausgaben umhängen in Logfile - logapp = "/var/log/revpipyloadapp" - logplc = "/var/log/revpipyload" - pargs.conffile = "/etc/revpipyload/revpipyload.conf" - sys.stdout = open(logplc, "a") - sys.stderr = sys.stdout + # Initialize configparser globalconfig + global globalconffile + globalconffile = pargs.conffile - # Initialize configparser globalconfig - self.globalconffile = pargs.conffile - self.globalconfig = ConfigParser() - self.globalconfig.read(pargs.conffile) + # Program logger + global logger + logger = logging.getLogger() - # Program logger - global logger - logger = logging.getLogger() - logformat = logging.Formatter( - "{asctime} [{levelname:8}] {message}", - datefmt="%Y-%m-%d %H:%M:%S", style="{" - ) - lhandler = logging.StreamHandler(sys.stdout) + # Alle handler entfernen + for lhandler in logger.handlers: + logger.removeHandler(lhandler) + + # Neue Handler bauen + logformat = logging.Formatter( + "{asctime} [{levelname:8}] {message}", + datefmt="%Y-%m-%d %H:%M:%S", style="{" + ) + lhandler = logging.StreamHandler(sys.stdout) + lhandler.setFormatter(logformat) + logger.addHandler(lhandler) + + if pargs.logfile is not None: + lhandler = logging.FileHandler(filename=pargs.logfile) lhandler.setFormatter(logformat) logger.addHandler(lhandler) - if pargs.logfile is not None: - lhandler = logging.FileHandler(filename=pargs.logfile) - lhandler.setFormatter(logformat) - logger.addHandler(lhandler) - # Loglevel auswerten - if pargs.verbose is None: - loglevel = logging.WARNING - elif pargs.verbose == 1: - loglevel = logging.INFO - elif pargs.verbose > 1: - loglevel = logging.DEBUG - logger.setLevel(loglevel) + # Loglevel auswerten + if pargs.verbose is None: + loglevel = logging.WARNING + elif pargs.verbose == 1: + loglevel = logging.INFO + elif pargs.verbose > 1: + loglevel = logging.DEBUG + logger.setLevel(loglevel) diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index 2e19cbf..8824ac9 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -39,6 +39,7 @@ import subprocess import tarfile import zipfile from concurrent import futures +from configparser import ConfigParser from json import loads as jloads from re import match as rematch from shutil import rmtree @@ -51,7 +52,7 @@ from xmlrpc.server import SimpleXMLRPCServer configrsc = "/opt/KUNBUS/config.rsc" picontrolreset = "/opt/KUNBUS/piControlReset" procimg = "/dev/piControl0" -pyloadverion = "0.2.9" +pyloadverion = "0.2.10" class LogReader(): @@ -72,6 +73,13 @@ class LogReader(): self.fhplc = None self.posplc = 0 + def closeall(self): + """Fuehrt close auf File Handler durch.""" + if self.fhapp is not None: + self.fhapp.close() + if self.fhplc is not None: + self.fhplc.close() + def get_applines(self): """Gibt neue Zeilen ab letzen Aufruf zurueck. @returns: list() mit neuen Zeilen""" @@ -152,6 +160,39 @@ class LogReader(): return self.fhplc.read() +class PipeLogwriter(Thread): + + def __init__(self, fh): + super().__init__() + self._exit = Event() + self.fh = fh + if fh is None: + self.pipeout = None + else: + self._pipein, self.pipeout = os.pipe() + + def run(self): + if self.fh is not None: + fhread = os.fdopen(self._pipein) + proginit.logger.debug("enter logreader pipe") + while not self._exit.is_set(): + line = fhread.readline() + try: + self.fh.write(line) + self.fh.flush() + except: + pass + proginit.logger.debug("leave logreader pipe") + + def stop(self): + proginit.logger.debug("quit pipe logreader") + self._exit.set() + if self.pipeout is not None: + os.write(self.pipeout, b"\n") + os.close(self.pipeout) + os.close(self._pipein) + + class RevPiPlc(Thread): """Verwaltet das PLC Python Programm. @@ -170,8 +211,9 @@ class RevPiPlc(Thread): self._arguments = arguments self._evt_exit = Event() self.exitcode = None - self._fh = None + self._fh = self._configurefh() self.gid = 65534 + self.plw = None self._program = program self._procplc = None self._pversion = pversion @@ -179,21 +221,39 @@ class RevPiPlc(Thread): self.zeroonerror = False self.zeroonexit = False + def _configurefh(self): + # Ausgaben konfigurieren und ggf. umleiten + proginit.logger.debug("configure fh applog") + logfile = None + if proginit.pargs.daemon: + if os.access(os.path.dirname(proginit.logapp), os.R_OK | os.W_OK): + logfile = proginit.logapp + elif proginit.pargs.logfile is not None: + logfile = proginit.pargs.logfile + + if logfile is not None: + return open(logfile, "a") + else: + return None + def _setuppopen(self): """Setzt UID und GID fuer das PLC Programm.""" + proginit.logger.debug( + "set uid {} and gid {}".format(self.uid, self.gid)) os.setgid(self.gid) os.setuid(self.uid) - def _spopen(self, lst_proc): + def _spopen(self, lst_proc, filenum=None): """Startet das PLC Programm. @param lst_proc: Prozessliste @returns: subprocess""" + proginit.logger.debug("configure subprocess") return subprocess.Popen( lst_proc, preexec_fn=self._setuppopen, cwd=os.path.dirname(self._program), bufsize=1, - stdout=subprocess.STDOUT if self._fh is None else self._fh, + stdout=subprocess.STDOUT if filenum is None else filenum, stderr=subprocess.STDOUT ) @@ -203,6 +263,20 @@ class RevPiPlc(Thread): f = open("/dev/piControl0", "w+b", 0) f.write(bytes(4096)) + def newlogfile(self): + if self._fh is not None: + self._fh.close() + + self._fh = self._configurefh() + if self.plw is not None: + self.plw.fh = self._fh + + self._fh.write("-" * 55) + self._fh.write("\nstart new logfile: {}\n".format(asctime())) + self._fh.flush() + + proginit.logger.info("new plc logfile") + def run(self): """Fuehrt PLC-Programm aus und ueberwacht es.""" if self._pversion == 2: @@ -214,25 +288,21 @@ class RevPiPlc(Thread): self._program, self._arguments )) - # Ausgaben konfigurieren und ggf. umleiten - logfile = None - if proginit.pargs.daemon: - if os.access(os.path.dirname(proginit.logapp), os.R_OK | os.W_OK): - logfile = proginit.logapp - elif proginit.pargs.logfile is not None: - logfile = proginit.pargs.logfile - - if logfile is not None: - self._fh = open(logfile, "a") + # Logausgabe + if self._fh is not None: self._fh.write("-" * 55) self._fh.write("\nplc: {} started: {}\n".format( os.path.basename(self._program), asctime() )) self._fh.flush() + # LogWriter + self.plw = PipeLogwriter(self._fh) + self.plw.start() + # Prozess erstellen proginit.logger.info("start plc program {}".format(self._program)) - self._procplc = self._spopen(lst_proc) + self._procplc = self._spopen(lst_proc, self.plw.pipeout) while not self._evt_exit.is_set(): @@ -263,7 +333,7 @@ class RevPiPlc(Thread): if not self._evt_exit.is_set() and self.autoreload: # Prozess neu starten - self._procplc = self._spopen(lst_proc) + self._procplc = self._spopen(lst_proc, self.plw.pipeout) if self.exitcode == 0: proginit.logger.warning( "restart plc program after clean exit" @@ -277,8 +347,15 @@ class RevPiPlc(Thread): self._evt_exit.wait(1) + def stop(self): + """Beendet PLC-Programm.""" + proginit.logger.info("stop revpiplc thread") + self._evt_exit.set() + # Prüfen ob es einen subprocess gibt if self._procplc is None: + if self.plw is not None: + self.plw.stop() return # Prozess beenden @@ -305,12 +382,11 @@ class RevPiPlc(Thread): or self.zeroonerror and self.exitcode != 0: self._zeroprocimg() - def stop(self): - """Beendet PLC-Programm.""" - self._evt_exit.set() + if self.plw is not None: + self.plw.stop() -class RevPiPyLoad(proginit.ProgInit): +class RevPiPyLoad(): """Hauptklasse, die alle Funktionen zur Verfuegung stellt. @@ -321,10 +397,11 @@ class RevPiPyLoad(proginit.ProgInit): def __init__(self): """Instantiiert RevPiPyLoad-Klasse.""" - super().__init__() + proginit.configure() + self._exit = True self.evt_loadconfig = Event() - + self.globalconfig = ConfigParser() self.logr = LogReader() self.plc = None self.tfile = {} @@ -338,6 +415,7 @@ class RevPiPyLoad(proginit.ProgInit): signal.signal(signal.SIGINT, self._sigexit) signal.signal(signal.SIGTERM, self._sigexit) signal.signal(signal.SIGHUP, self._sigloadconfig) + signal.signal(signal.SIGUSR1, self._signewlogfile) def _loadconfig(self): """Load configuration file and setup modul.""" @@ -353,9 +431,9 @@ class RevPiPyLoad(proginit.ProgInit): # Konfigurationsdatei laden proginit.logger.info( - "loading config file: {}".format(self.globalconffile) + "loading config file: {}".format(proginit.globalconffile) ) - self.globalconfig.read(self.globalconffile) + self.globalconfig.read(proginit.globalconffile) # Konfiguration verarbeiten self.autoreload = \ @@ -473,11 +551,29 @@ class RevPiPyLoad(proginit.ProgInit): proginit.logger.debug("got exit signal") self.stop() + # Programm aufräumen + proginit.cleanup() + def _sigloadconfig(self, signum, frame): """Signal handler to load configuration.""" proginit.logger.debug("got reload config signal") self.evt_loadconfig.set() + def _signewlogfile(self, signum, frame): + """Signal handler to start new logfile.""" + proginit.logger.debug("got new logfile signal") + + # Logger neu konfigurieren + proginit.configure() + proginit.logger.info("start new logfile: {}".format(asctime())) + + # stdout für revpipyplc + if self.plc is not None: + self.plc.newlogfile() + + # Logreader schließen + self.logr.closeall() + def packapp(self, mode="tar", pictory=False): """Erzeugt aus dem PLC-Programm ein TAR-File. @@ -673,9 +769,13 @@ class RevPiPyLoad(proginit.ProgInit): def xml_plcstop(self): """Stoppt das PLC Programm. - @returns: int() Exitcode vom PLC Programm""" + + @returns: int() Exitcode vom PLC Programm + -1 PLC Programm lief nicht + + """ proginit.logger.debug("xmlrpc call plcstop") - if self.plc is not None: + if self.plc is not None and self.plc.is_alive(): self.plc.stop() self.plc.join() return self.plc.exitcode @@ -760,10 +860,10 @@ class RevPiPyLoad(proginit.ProgInit): self.globalconfig.set("DEFAULT", key, str(dc[key])) # conf-Datei schreiben - fh = open(self.globalconffile, "w") + fh = open(proginit.globalconffile, "w") self.globalconfig.write(fh) proginit.logger.info( - "got new config and wrote it to {}".format(self.globalconffile) + "got new config and wrote it to {}".format(proginit.globalconffile) ) if loadnow: diff --git a/setup.py b/setup.py index d9bcac7..7c7ca4f 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( license="LGPLv3", name="revpipyload", - version="0.2.9", + version="0.2.10", scripts=["data/revpipyload"],