diff --git a/.hgignore b/.hgignore index 6f58155..f1479c8 100644 --- a/.hgignore +++ b/.hgignore @@ -4,3 +4,4 @@ test/* .* build/* *.pyc +deb/* diff --git a/doc/proginit.html b/doc/proginit.html index b22ec58..d9f6e46 100644 --- a/doc/proginit.html +++ b/doc/proginit.html @@ -12,7 +12,7 @@ Main functions of our program.

Global Attributes

- +
forked
globalconffile
logapp
logger
logplc
pargs
forked
globalconffile
logapp
logger
logplc
pargs
startdir

Classes

diff --git a/doc/revpipyload.html b/doc/revpipyload.html index 04a1008..eff826f 100644 --- a/doc/revpipyload.html +++ b/doc/revpipyload.html @@ -42,7 +42,7 @@ Classes Ermoeglicht den Zugriff auf die Logdateien. PipeLogwriter - +File PIPE fuer das Schreiben des APP Log. RevPiPlc Verwaltet das PLC Python Programm. @@ -171,7 +171,16 @@ str() mit Logdaten

PipeLogwriter

+

+File PIPE fuer das Schreiben des APP Log. +

+ Spezieller LogFile-Handler fuer die Ausgabe des subprocess fuer das Python + PLC Programm. Die Ausgabe kann nicht auf einen neuen FileHandler + umgeschrieben werden. Dadurch waere es nicht moeglich nach einem logrotate + die neue Datei zu verwenden. Ueber die PIPE wird dies umgangen. +

+

Derived from

Thread @@ -190,13 +199,25 @@ Methods + + + + + + + + + + + + - + - +
PipeLogwriterInstantiiert PipeLogwriter-Klasse.
__del__
_configurefhKonfiguriert den FileHandler fuer Ausgaben der PLCAPP.
loglineSchreibt eine Zeile in die Logdatei oder stdout.
newlogfileKonfiguriert den FileHandler auf eine neue Logdatei.
runPrueft auf neue Logzeilen und schreibt diese.
stopBeendetden Thread und die FileHandler werden geschlossen.

@@ -207,16 +228,59 @@ Static Methods

PipeLogwriter (Constructor)

-PipeLogwriter(fh) - +PipeLogwriter(logfilename) +

+Instantiiert PipeLogwriter-Klasse. +

+
logfilename:
+
+Dateiname fuer Logdatei +
+
+

+PipeLogwriter.__del__

+__del__() + +

+PipeLogwriter._configurefh

+_configurefh() +

+Konfiguriert den FileHandler fuer Ausgaben der PLCAPP. +

+
Returns:
+
+FileHandler-Objekt +
+
+

+PipeLogwriter.logline

+logline(message) +

+Schreibt eine Zeile in die Logdatei oder stdout. +

+
message:
+
+Logzeile zum Schreiben +
+
+

+PipeLogwriter.newlogfile

+newlogfile() +

+Konfiguriert den FileHandler auf eine neue Logdatei. +

PipeLogwriter.run

run() - +

+Prueft auf neue Logzeilen und schreibt diese. +

PipeLogwriter.stop

stop() - +

+Beendetden Thread und die FileHandler werden geschlossen. +

Up


@@ -251,8 +315,8 @@ Methods RevPiPlc Instantiiert RevPiPlc-Klasse. -_configurefh - +_configureplw +Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP. _setuppopen Setzt UID und GID fuer das PLC Programm. @@ -264,7 +328,7 @@ Methods Setzt Prozessabbild auf NULL. newlogfile - +Konfiguriert die FileHandler auf neue Logdatei. run Fuehrt PLC-Programm aus und ueberwacht es. @@ -284,11 +348,18 @@ RevPiPlc (Constructor) RevPiPlc(program, arguments, pversion)

Instantiiert RevPiPlc-Klasse. -

+

-RevPiPlc._configurefh

-_configurefh() - +RevPiPlc._configureplw +_configureplw() +

+Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP. +

+
Returns:
+
+PipeLogwriter() +
+

RevPiPlc._setuppopen

_setuppopen() @@ -297,7 +368,7 @@ Setzt UID und GID fuer das PLC Programm.

RevPiPlc._spopen

-_spopen(lst_proc, filenum=None) +_spopen(lst_proc)

Startet das PLC Programm.

@@ -320,7 +391,9 @@ Setzt Prozessabbild auf NULL.

RevPiPlc.newlogfile

newlogfile() - +

+Konfiguriert die FileHandler auf neue Logdatei. +

RevPiPlc.run

run() diff --git a/doc/revpipyload.revpipyload.html b/doc/revpipyload.revpipyload.html deleted file mode 100644 index 42bae03..0000000 --- a/doc/revpipyload.revpipyload.html +++ /dev/null @@ -1,555 +0,0 @@ - - -revpipyload.revpipyload - - - -

-revpipyload.revpipyload

- -

-Global Attributes

- - -
configrsc
picontrolreset
procimg
pyloadverion
-

-Classes

- - - - - - - - - - - -
LogReaderErmoeglicht den Zugriff auf die Logdateien.
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.
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.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
-

- -

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.
_zeroprocimgSetzt Prozessabbild auf NULL.
runFuehrt PLC-Programm aus und ueberwacht es.
stopBeendet PLC-Programm.
-

-Static Methods

- - -
None
- -

-RevPiPlc (Constructor)

-RevPiPlc(program, pversion) -

-Instantiiert RevPiPlc-Klasse. -

-

-RevPiPlc._zeroprocimg

-_zeroprocimg() -

-Setzt Prozessabbild auf NULL. -

-

-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

-proginit.ProgInit -

-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.
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.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 lauuft noch -2 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 - 100 Fehler -
-
-

-RevPiPyLoad.xml_plcstop

-xml_plcstop() -

-Stoppt das PLC Programm. -

-
Returns:
-
-int() Exitcode vom PLC Programm -
-
-

-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. -

-

-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.api b/revpipyload.api new file mode 100644 index 0000000..080cf22 --- /dev/null +++ b/revpipyload.api @@ -0,0 +1,58 @@ +proginit.cleanup?4() +proginit.configure?4() +proginit.forked?7 +proginit.globalconffile?7 +proginit.logapp?7 +proginit.logger?7 +proginit.logplc?7 +proginit.pargs?7 +proginit.startdir?7 +revpipyload.LogReader.closeall?4() +revpipyload.LogReader.get_applines?4() +revpipyload.LogReader.get_applog?4() +revpipyload.LogReader.get_plclines?4() +revpipyload.LogReader.get_plclog?4() +revpipyload.LogReader?1() +revpipyload.PipeLogwriter.__del__?6() +revpipyload.PipeLogwriter._configurefh?5() +revpipyload.PipeLogwriter.logline?4(message) +revpipyload.PipeLogwriter.newlogfile?4() +revpipyload.PipeLogwriter.run?4() +revpipyload.PipeLogwriter.stop?4() +revpipyload.PipeLogwriter?1(logfilename) +revpipyload.RevPiPlc._configureplw?5() +revpipyload.RevPiPlc._setuppopen?5() +revpipyload.RevPiPlc._spopen?5(lst_proc) +revpipyload.RevPiPlc._zeroprocimg?5() +revpipyload.RevPiPlc.newlogfile?4() +revpipyload.RevPiPlc.run?4() +revpipyload.RevPiPlc.stop?4() +revpipyload.RevPiPlc?1(program, arguments, pversion) +revpipyload.RevPiPyLoad._loadconfig?5() +revpipyload.RevPiPyLoad._plcthread?5() +revpipyload.RevPiPyLoad._sigexit?5(signum, frame) +revpipyload.RevPiPyLoad._sigloadconfig?5(signum, frame) +revpipyload.RevPiPyLoad._signewlogfile?5(signum, frame) +revpipyload.RevPiPyLoad.packapp?4(mode="tar", pictory=False) +revpipyload.RevPiPyLoad.root?7 +revpipyload.RevPiPyLoad.start?4() +revpipyload.RevPiPyLoad.stop?4() +revpipyload.RevPiPyLoad.xml_getconfig?4() +revpipyload.RevPiPyLoad.xml_getfilelist?4() +revpipyload.RevPiPyLoad.xml_getpictoryrsc?4() +revpipyload.RevPiPyLoad.xml_getprocimg?4() +revpipyload.RevPiPyLoad.xml_plcdownload?4(mode="tar", pictory=False) +revpipyload.RevPiPyLoad.xml_plcexitcode?4() +revpipyload.RevPiPyLoad.xml_plcrunning?4() +revpipyload.RevPiPyLoad.xml_plcstart?4() +revpipyload.RevPiPyLoad.xml_plcstop?4() +revpipyload.RevPiPyLoad.xml_plcupload?4(filedata, filename) +revpipyload.RevPiPyLoad.xml_plcuploadclean?4() +revpipyload.RevPiPyLoad.xml_reload?4() +revpipyload.RevPiPyLoad.xml_setconfig?4(dc, loadnow=False) +revpipyload.RevPiPyLoad.xml_setpictoryrsc?4(filebytes, reset=False) +revpipyload.RevPiPyLoad?1() +revpipyload.configrsc?7 +revpipyload.picontrolreset?7 +revpipyload.procimg?7 +revpipyload.pyloadverion?7 diff --git a/revpipyload.bas b/revpipyload.bas new file mode 100644 index 0000000..9aaa29e --- /dev/null +++ b/revpipyload.bas @@ -0,0 +1,2 @@ +PipeLogwriter Thread +RevPiPlc Thread diff --git a/revpipyload/proginit.py b/revpipyload/proginit.py index 4898188..ac51060 100644 --- a/revpipyload/proginit.py +++ b/revpipyload/proginit.py @@ -7,10 +7,10 @@ # -*- coding: utf-8 -*- """Main functions of our program.""" import logging +import os.path import sys from argparse import ArgumentParser from os import fork as osfork -from os.path import exists as ospexists forked = False @@ -19,6 +19,7 @@ logapp = "revpipyloadapp.log" logplc = "revpipyload.log" logger = None pargs = None +startdir = None def cleanup(): @@ -54,13 +55,22 @@ def configure(): global pargs pargs = parser.parse_args() + # Pfade absolut umschreiben + global startdir + if startdir is None: + startdir = os.path.abspath(".") + if pargs.conffile is not None and os.path.dirname(pargs.conffile) == "": + pargs.conffile = os.path.join(startdir, pargs.confffile) + 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" pid = 0 if pargs.daemon and not forked: # Prüfen ob daemon schon läuft - if ospexists(pidfile): + if os.path.exists(pidfile): raise SystemError( "program already running as daemon. check {}".format(pidfile) ) @@ -74,16 +84,17 @@ def configure(): else: forked = True + global logapp + global logplc if pargs.daemon: - 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 + elif pargs.logfile is not None: + logplc = pargs.logfile # Initialize configparser globalconfig global globalconffile diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index 8824ac9..fa9f274 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -43,8 +43,9 @@ from configparser import ConfigParser from json import loads as jloads from re import match as rematch from shutil import rmtree +from sys import stdout as sysstdout from tempfile import mktemp -from threading import Thread, Event +from threading import Thread, Event, Lock from time import sleep, asctime from xmlrpc.client import Binary from xmlrpc.server import SimpleXMLRPCServer @@ -162,35 +163,107 @@ class LogReader(): class PipeLogwriter(Thread): - def __init__(self, fh): + """File PIPE fuer das Schreiben des APP Log. + + Spezieller LogFile-Handler fuer die Ausgabe des subprocess fuer das Python + PLC Programm. Die Ausgabe kann nicht auf einen neuen FileHandler + umgeschrieben werden. Dadurch waere es nicht moeglich nach einem logrotate + die neue Datei zu verwenden. Ueber die PIPE wird dies umgangen. + + """ + + def __init__(self, logfilename): + """Instantiiert PipeLogwriter-Klasse. + @param logfilename: Dateiname fuer Logdatei""" super().__init__() self._exit = Event() - self.fh = fh - if fh is None: - self.pipeout = None + self._fh = None + self._lckfh = Lock() + self.logfile = logfilename + + # Logdatei öffnen + self._fh = self._configurefh() + + # Pipes öffnen + self._fdr, self.fdw = os.pipe() + proginit.logger.debug("pipe fd read: {} / write: {}".format( + self._fdr, self.fdw + )) + + def __del__(self): + if self._fh is not None: + self._fh.close() + + def _configurefh(self): + """Konfiguriert den FileHandler fuer Ausgaben der PLCAPP. + @returns: FileHandler-Objekt""" + proginit.logger.debug("enter PipeLogwriter._configurefh()") + + dirname = os.path.dirname(self.logfile) + + proginit.logger.debug("dirname = {}".format(os.path.abspath(dirname))) + + if os.access(dirname, os.R_OK | os.W_OK): + logfile = open(self.logfile, "a") else: - self._pipein, self.pipeout = os.pipe() + raise RuntimeError("can not open logfile {}".format(self.logfile)) + + proginit.logger.debug("leave PipeLogwriter._configurefh()") + return logfile + + def logline(self, message): + """Schreibt eine Zeile in die Logdatei oder stdout. + @param message: Logzeile zum Schreiben""" + with self._lckfh: + self._fh.write("{}\n".format(message)) + self._fh.flush() + + def newlogfile(self): + """Konfiguriert den FileHandler auf eine neue Logdatei.""" + proginit.logger.debug("enter RevPiPlc.newlogfile()") + with self._lckfh: + self._fh.close() + self._fh = self._configurefh() + proginit.logger.debug("leave RevPiPlc.newlogfile()") 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") + """Prueft auf neue Logzeilen und schreibt diese.""" + proginit.logger.debug("enter PipeLogwriter.run()") + + fhread = os.fdopen(self._fdr) + proginit.logger.debug("enter logreader pipe loop") + while not self._exit.is_set(): + line = fhread.readline() + self._lckfh.acquire() + try: + self._fh.write(line) + self._fh.flush() + except: + proginit.logger.exception("PipeLogwriter write log line") + finally: + self._lckfh.release() + proginit.logger.debug("leave logreader pipe loop") + + proginit.logger.debug("close all pipes") + os.close(self._fdr) + os.close(self.fdw) + proginit.logger.debug("closed all pipes") + + proginit.logger.debug("leave PipeLogwriter.run()") def stop(self): - proginit.logger.debug("quit pipe logreader") + """Beendetden Thread und die FileHandler werden geschlossen.""" + proginit.logger.debug("enter PipeLogwriter.stop()") self._exit.set() - if self.pipeout is not None: - os.write(self.pipeout, b"\n") - os.close(self.pipeout) - os.close(self._pipein) + self._lckfh.acquire() + try: + os.write(self.fdw, b"\n") + except: + pass + finally: + self._lckfh.release() + + proginit.logger.debug("leave PipeLogwriter.stop()") class RevPiPlc(Thread): @@ -211,9 +284,8 @@ class RevPiPlc(Thread): self._arguments = arguments self._evt_exit = Event() self.exitcode = None - self._fh = self._configurefh() self.gid = 65534 - self.plw = None + self._plw = self._configureplw() self._program = program self._procplc = None self._pversion = pversion @@ -221,9 +293,10 @@ class RevPiPlc(Thread): self.zeroonerror = False self.zeroonexit = False - def _configurefh(self): - # Ausgaben konfigurieren und ggf. umleiten - proginit.logger.debug("configure fh applog") + def _configureplw(self): + """Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP. + @returns: PipeLogwriter()""" + proginit.logger.debug("enter RevPiPlc._configureplw()") logfile = None if proginit.pargs.daemon: if os.access(os.path.dirname(proginit.logapp), os.R_OK | os.W_OK): @@ -231,10 +304,11 @@ class RevPiPlc(Thread): elif proginit.pargs.logfile is not None: logfile = proginit.pargs.logfile - if logfile is not None: - return open(logfile, "a") - else: - return None + if not logfile is None: + logfile = PipeLogwriter(logfile) + + proginit.logger.debug("leave RevPiPlc._configureplw()") + return logfile def _setuppopen(self): """Setzt UID und GID fuer das PLC Programm.""" @@ -243,19 +317,21 @@ class RevPiPlc(Thread): os.setgid(self.gid) os.setuid(self.uid) - def _spopen(self, lst_proc, filenum=None): + def _spopen(self, lst_proc): """Startet das PLC Programm. @param lst_proc: Prozessliste @returns: subprocess""" - proginit.logger.debug("configure subprocess") - return subprocess.Popen( + proginit.logger.debug("enter RevPiPlc._spopen({})".format(lst_proc)) + sp = subprocess.Popen( lst_proc, preexec_fn=self._setuppopen, cwd=os.path.dirname(self._program), bufsize=1, - stdout=subprocess.STDOUT if filenum is None else filenum, + stdout=sysstdout if self._plw is None else self._plw.fdw, stderr=subprocess.STDOUT ) + proginit.logger.debug("leave RevPiPlc._spopen()") + return sp def _zeroprocimg(self): """Setzt Prozessabbild auf NULL.""" @@ -264,21 +340,18 @@ class RevPiPlc(Thread): f.write(bytes(4096)) def newlogfile(self): - if self._fh is not None: - self._fh.close() + """Konfiguriert die FileHandler auf neue Logdatei.""" + proginit.logger.debug("enter RevPiPlc.newlogfile()") + if self._plw is not None: + self._plw.newlogfile() + self._plw.logline("-" * 55) + self._plw.logline("start new logfile: {}".format(asctime())) - 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") + proginit.logger.debug("leave RevPiPlc.newlogfile()") def run(self): """Fuehrt PLC-Programm aus und ueberwacht es.""" + proginit.logger.debug("enter RevPiPlc.run()") if self._pversion == 2: lst_proc = shlex.split("/usr/bin/env python2 -u {} {}".format( self._program, self._arguments @@ -288,21 +361,17 @@ class RevPiPlc(Thread): self._program, self._arguments )) - # 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.plw.pipeout) + self._procplc = self._spopen(lst_proc) + + # LogWriter starten und Logausgaben schreiben + if self._plw is not None: + self._plw.logline("-" * 55) + self._plw.logline("plc: {} started: {}".format( + os.path.basename(self._program), asctime() + )) + self._plw.start() while not self._evt_exit.is_set(): @@ -333,7 +402,7 @@ class RevPiPlc(Thread): if not self._evt_exit.is_set() and self.autoreload: # Prozess neu starten - self._procplc = self._spopen(lst_proc, self.plw.pipeout) + self._procplc = self._spopen(lst_proc) if self.exitcode == 0: proginit.logger.warning( "restart plc program after clean exit" @@ -347,15 +416,23 @@ class RevPiPlc(Thread): self._evt_exit.wait(1) + proginit.logger.debug("leave RevPiPlc.run()") + def stop(self): """Beendet PLC-Programm.""" + proginit.logger.debug("enter RevPiPlc.stop()") 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() + if self._plw is not None: + self._plw.stop() + proginit.logger.debug("join after NONE pipe thread") + self._plw.join() + proginit.logger.debug("joined after NONE pipe thread") + + proginit.logger.debug("leave RevPiPlc.stop()") return # Prozess beenden @@ -382,8 +459,13 @@ class RevPiPlc(Thread): or self.zeroonerror and self.exitcode != 0: self._zeroprocimg() - if self.plw is not None: - self.plw.stop() + if self._plw is not None: + self._plw.stop() + proginit.logger.debug("join pipe thread") + self._plw.join() + proginit.logger.debug("joined pipe thread") + + proginit.logger.debug("leave RevPiPlc.stop()") class RevPiPyLoad(): @@ -554,6 +636,8 @@ class RevPiPyLoad(): # Programm aufräumen proginit.cleanup() + proginit.logger.debug("end revpipyload program") + def _sigloadconfig(self, signum, frame): """Signal handler to load configuration.""" proginit.logger.debug("got reload config signal")