Umstellung auf proginit als globale Datenquelle

Aufteilung der Funktionen in mehrere Module
FileHandler von stdout schließen
This commit is contained in:
2017-09-12 16:17:03 +02:00
parent 9da1651ec1
commit 3944ec2801
15 changed files with 973 additions and 866 deletions

View File

@@ -5,3 +5,4 @@ test/*
build/* build/*
*.pyc *.pyc
deb/* deb/*
demo/*

View File

@@ -13,6 +13,12 @@ Table of contents</h1>
Modules</h3> Modules</h3>
<table> <table>
<tr> <tr>
<td><a style="color:#0000FF" href="logsystem.html">logsystem</a></td>
<td>Modul fuer die Verwaltung der Logdateien.</td>
</tr><tr>
<td><a style="color:#0000FF" href="plcsystem.html">plcsystem</a></td>
<td>Modul fuer die Verwaltung der PLC Funktionen.</td>
</tr><tr>
<td><a style="color:#0000FF" href="procimgserver.html">procimgserver</a></td> <td><a style="color:#0000FF" href="procimgserver.html">procimgserver</a></td>
<td>Stellt Funktionen bereit um das Prozessabbild zu ueberwachen.</td> <td>Stellt Funktionen bereit um das Prozessabbild zu ueberwachen.</td>
</tr><tr> </tr><tr>

248
doc/logsystem.html Normal file
View File

@@ -0,0 +1,248 @@
<!DOCTYPE html>
<html><head>
<title>logsystem</title>
<meta charset="UTF-8">
</head>
<body style="background-color:#FFFFFF;color:#000000"><a NAME="top" ID="top"></a>
<h1 style="background-color:#FFFFFF;color:#0000FF">
logsystem</h1>
<p>
Modul fuer die Verwaltung der Logdateien.
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Global Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Classes</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#LogReader">LogReader</a></td>
<td>Ermoeglicht den Zugriff auf die Logdateien.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter">PipeLogwriter</a></td>
<td>File PIPE fuer das Schreiben des APP Log.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Functions</h3>
<table>
<tr><td>None</td></tr>
</table>
<hr /><hr />
<a NAME="LogReader" ID="LogReader"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">LogReader</h2>
<p>
Ermoeglicht den Zugriff auf die Logdateien.
</p><p>
Beinhaltet Funktionen fuer den Abruf der gesamten Logdatei fuer das
RevPiPyLoad-System und die Logdatei der PLC-Anwendung.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
None
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#LogReader.__init__">LogReader</a></td>
<td>Instantiiert LogReader-Klasse.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#LogReader.closeall">closeall</a></td>
<td>Fuehrt close auf File Handler durch.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#LogReader.load_applog">load_applog</a></td>
<td>Uebertraegt Logdaten des PLC Programms Binaer.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#LogReader.load_plclog">load_plclog</a></td>
<td>Uebertraegt Logdaten des Loaders Binaer.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="LogReader.__init__" ID="LogReader.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader (Constructor)</h3>
<b>LogReader</b>(<i></i>)
<p>
Instantiiert LogReader-Klasse.
</p><a NAME="LogReader.closeall" ID="LogReader.closeall"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader.closeall</h3>
<b>closeall</b>(<i></i>)
<p>
Fuehrt close auf File Handler durch.
</p><a NAME="LogReader.load_applog" ID="LogReader.load_applog"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader.load_applog</h3>
<b>load_applog</b>(<i>start, count</i>)
<p>
Uebertraegt Logdaten des PLC Programms Binaer.
</p><dl>
<dt><i>start</i></dt>
<dd>
Startbyte
</dd><dt><i>count</i></dt>
<dd>
Max. Byteanzahl zum uebertragen
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
Binary() der Logdatei
</dd>
</dl><a NAME="LogReader.load_plclog" ID="LogReader.load_plclog"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader.load_plclog</h3>
<b>load_plclog</b>(<i>start, count</i>)
<p>
Uebertraegt Logdaten des Loaders Binaer.
</p><dl>
<dt><i>start</i></dt>
<dd>
Startbyte
</dd><dt><i>count</i></dt>
<dd>
Max. Byteanzahl zum uebertragen
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
Binary() der Logdatei
</dd>
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="PipeLogwriter" ID="PipeLogwriter"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">PipeLogwriter</h2>
<p>
File PIPE fuer das Schreiben des APP Log.
</p><p>
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.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
Thread
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.__init__">PipeLogwriter</a></td>
<td>Instantiiert PipeLogwriter-Klasse.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.__del__">__del__</a></td>
<td>Close der FileHandler.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter._configurefh">_configurefh</a></td>
<td>Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.logline">logline</a></td>
<td>Schreibt eine Zeile in die Logdatei oder stdout.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.newlogfile">newlogfile</a></td>
<td>Konfiguriert den FileHandler auf eine neue Logdatei.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.run">run</a></td>
<td>Prueft auf neue Logzeilen und schreibt diese.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.stop">stop</a></td>
<td>Beendetden Thread und die FileHandler werden geschlossen.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="PipeLogwriter.__init__" ID="PipeLogwriter.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter (Constructor)</h3>
<b>PipeLogwriter</b>(<i>logfilename</i>)
<p>
Instantiiert PipeLogwriter-Klasse.
</p><dl>
<dt><i>logfilename</i></dt>
<dd>
Dateiname fuer Logdatei
</dd>
</dl><a NAME="PipeLogwriter.__del__" ID="PipeLogwriter.__del__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.__del__</h3>
<b>__del__</b>(<i></i>)
<p>
Close der FileHandler.
</p><a NAME="PipeLogwriter._configurefh" ID="PipeLogwriter._configurefh"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter._configurefh</h3>
<b>_configurefh</b>(<i></i>)
<p>
Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.
</p><dl>
<dt>Returns:</dt>
<dd>
FileHandler-Objekt
</dd>
</dl><a NAME="PipeLogwriter.logline" ID="PipeLogwriter.logline"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.logline</h3>
<b>logline</b>(<i>message</i>)
<p>
Schreibt eine Zeile in die Logdatei oder stdout.
</p><dl>
<dt><i>message</i></dt>
<dd>
Logzeile zum Schreiben
</dd>
</dl><a NAME="PipeLogwriter.newlogfile" ID="PipeLogwriter.newlogfile"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.newlogfile</h3>
<b>newlogfile</b>(<i></i>)
<p>
Konfiguriert den FileHandler auf eine neue Logdatei.
</p><a NAME="PipeLogwriter.run" ID="PipeLogwriter.run"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.run</h3>
<b>run</b>(<i></i>)
<p>
Prueft auf neue Logzeilen und schreibt diese.
</p><a NAME="PipeLogwriter.stop" ID="PipeLogwriter.stop"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.stop</h3>
<b>stop</b>(<i></i>)
<p>
Beendetden Thread und die FileHandler werden geschlossen.
</p>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr />
</body></html>

147
doc/plcsystem.html Normal file
View File

@@ -0,0 +1,147 @@
<!DOCTYPE html>
<html><head>
<title>plcsystem</title>
<meta charset="UTF-8">
</head>
<body style="background-color:#FFFFFF;color:#000000"><a NAME="top" ID="top"></a>
<h1 style="background-color:#FFFFFF;color:#0000FF">
plcsystem</h1>
<p>
Modul fuer die Verwaltung der PLC Funktionen.
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Global Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Classes</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#RevPiPlc">RevPiPlc</a></td>
<td>Verwaltet das PLC Python Programm.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Functions</h3>
<table>
<tr><td>None</td></tr>
</table>
<hr /><hr />
<a NAME="RevPiPlc" ID="RevPiPlc"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiPlc</h2>
<p>
Verwaltet das PLC Python Programm.
</p><p>
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.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
Thread
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#RevPiPlc.__init__">RevPiPlc</a></td>
<td>Instantiiert RevPiPlc-Klasse.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._configureplw">_configureplw</a></td>
<td>Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._setuppopen">_setuppopen</a></td>
<td>Setzt UID und GID fuer das PLC Programm.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._spopen">_spopen</a></td>
<td>Startet das PLC Programm.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc.newlogfile">newlogfile</a></td>
<td>Konfiguriert die FileHandler auf neue Logdatei.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc.run">run</a></td>
<td>Fuehrt PLC-Programm aus und ueberwacht es.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc.stop">stop</a></td>
<td>Beendet PLC-Programm.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="RevPiPlc.__init__" ID="RevPiPlc.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc (Constructor)</h3>
<b>RevPiPlc</b>(<i>program, arguments, pversion</i>)
<p>
Instantiiert RevPiPlc-Klasse.
</p><a NAME="RevPiPlc._configureplw" ID="RevPiPlc._configureplw"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._configureplw</h3>
<b>_configureplw</b>(<i></i>)
<p>
Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.
</p><dl>
<dt>Returns:</dt>
<dd>
PipeLogwriter()
</dd>
</dl><a NAME="RevPiPlc._setuppopen" ID="RevPiPlc._setuppopen"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._setuppopen</h3>
<b>_setuppopen</b>(<i></i>)
<p>
Setzt UID und GID fuer das PLC Programm.
</p><a NAME="RevPiPlc._spopen" ID="RevPiPlc._spopen"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._spopen</h3>
<b>_spopen</b>(<i>lst_proc</i>)
<p>
Startet das PLC Programm.
</p><dl>
<dt><i>lst_proc</i></dt>
<dd>
Prozessliste
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
subprocess
</dd>
</dl><a NAME="RevPiPlc.newlogfile" ID="RevPiPlc.newlogfile"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc.newlogfile</h3>
<b>newlogfile</b>(<i></i>)
<p>
Konfiguriert die FileHandler auf neue Logdatei.
</p><a NAME="RevPiPlc.run" ID="RevPiPlc.run"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc.run</h3>
<b>run</b>(<i></i>)
<p>
Fuehrt PLC-Programm aus und ueberwacht es.
</p><a NAME="RevPiPlc.stop" ID="RevPiPlc.stop"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc.stop</h3>
<b>stop</b>(<i></i>)
<p>
Beendet PLC-Programm.
</p>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr />
</body></html>

View File

@@ -93,22 +93,13 @@ Static Methods</h3>
<a NAME="ProcimgServer.__init__" ID="ProcimgServer.__init__"></a> <a NAME="ProcimgServer.__init__" ID="ProcimgServer.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000"> <h3 style="background-color:#FFFFFF;color:#FF0000">
ProcimgServer (Constructor)</h3> ProcimgServer (Constructor)</h3>
<b>ProcimgServer</b>(<i>logger, xmlserver, configrsc, procimg, aclmode</i>) <b>ProcimgServer</b>(<i>xmlserver, aclmode</i>)
<p> <p>
Instantiiert RevPiCheckServer()-Klasse. Instantiiert RevPiCheckServer()-Klasse.
</p><dl> </p><dl>
<dt><i>xmlserver</i></dt> <dt><i>xmlserver</i></dt>
<dd> <dd>
XML-RPC Server XML-RPC Server
</dd><dt><i>procimg</i></dt>
<dd>
Pfad zum Prozessabbild
</dd><dt><i>configrsc</i></dt>
<dd>
Pfad zur piCtory Konfigurationsdatei
</dd><dt><i>logger</i></dt>
<dd>
Loggerinstanz
</dd><dt><i>aclmode</i></dt> </dd><dt><i>aclmode</i></dt>
<dd> <dd>
Zugriffsrechte Zugriffsrechte

View File

@@ -12,7 +12,7 @@ Main functions of our program.
<h3 style="background-color:#FFFFFF;color:#FF0000"> <h3 style="background-color:#FFFFFF;color:#FF0000">
Global Attributes</h3> Global Attributes</h3>
<table> <table>
<tr><td>forked</td></tr><tr><td>globalconffile</td></tr><tr><td>logapp</td></tr><tr><td>logger</td></tr><tr><td>logplc</td></tr><tr><td>pargs</td></tr><tr><td>startdir</td></tr> <tr><td>forked</td></tr><tr><td>globalconffile</td></tr><tr><td>logapp</td></tr><tr><td>logger</td></tr><tr><td>logplc</td></tr><tr><td>pargs</td></tr><tr><td>picontrolreset</td></tr><tr><td>rapcatalog</td></tr><tr><td>startdir</td></tr>
</table> </table>
<h3 style="background-color:#FFFFFF;color:#FF0000"> <h3 style="background-color:#FFFFFF;color:#FF0000">
Classes</h3> Classes</h3>
@@ -23,6 +23,9 @@ Classes</h3>
Functions</h3> Functions</h3>
<table> <table>
<tr> <tr>
<td><a style="color:#0000FF" href="#_zeroprocimg">_zeroprocimg</a></td>
<td>Setzt Prozessabbild auf NULL.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#cleanup">cleanup</a></td> <td><a style="color:#0000FF" href="#cleanup">cleanup</a></td>
<td>Clean up program.</td> <td>Clean up program.</td>
</tr><tr> </tr><tr>
@@ -31,6 +34,14 @@ Functions</h3>
</tr> </tr>
</table> </table>
<hr /><hr /> <hr /><hr />
<a NAME="_zeroprocimg" ID="_zeroprocimg"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">_zeroprocimg</h2>
<b>_zeroprocimg</b>(<i>self</i>)
<p>
Setzt Prozessabbild auf NULL.
</p>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="cleanup" ID="cleanup"></a> <a NAME="cleanup" ID="cleanup"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">cleanup</h2> <h2 style="background-color:#FFFFFF;color:#0000FF">cleanup</h2>
<b>cleanup</b>(<i></i>) <b>cleanup</b>(<i></i>)

View File

@@ -32,21 +32,12 @@ begrenzt werden!
<h3 style="background-color:#FFFFFF;color:#FF0000"> <h3 style="background-color:#FFFFFF;color:#FF0000">
Global Attributes</h3> Global Attributes</h3>
<table> <table>
<tr><td>configrsc</td></tr><tr><td>picontrolreset</td></tr><tr><td>procimg</td></tr><tr><td>pyloadverion</td></tr><tr><td>rapcatalog</td></tr> <tr><td>pyloadverion</td></tr>
</table> </table>
<h3 style="background-color:#FFFFFF;color:#FF0000"> <h3 style="background-color:#FFFFFF;color:#FF0000">
Classes</h3> Classes</h3>
<table> <table>
<tr> <tr>
<td><a style="color:#0000FF" href="#LogReader">LogReader</a></td>
<td>Ermoeglicht den Zugriff auf die Logdateien.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter">PipeLogwriter</a></td>
<td>File PIPE fuer das Schreiben des APP Log.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc">RevPiPlc</a></td>
<td>Verwaltet das PLC Python Programm.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPyLoad">RevPiPyLoad</a></td> <td><a style="color:#0000FF" href="#RevPiPyLoad">RevPiPyLoad</a></td>
<td>Hauptklasse, die alle Funktionen zur Verfuegung stellt.</td> <td>Hauptklasse, die alle Funktionen zur Verfuegung stellt.</td>
</tr> </tr>
@@ -57,345 +48,6 @@ Functions</h3>
<tr><td>None</td></tr> <tr><td>None</td></tr>
</table> </table>
<hr /><hr /> <hr /><hr />
<a NAME="LogReader" ID="LogReader"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">LogReader</h2>
<p>
Ermoeglicht den Zugriff auf die Logdateien.
</p><p>
Beinhaltet Funktionen fuer den Abruf der gesamten Logdatei fuer das
RevPiPyLoad-System und die Logdatei der PLC-Anwendung.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
None
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#LogReader.__init__">LogReader</a></td>
<td>Instantiiert LogReader-Klasse.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#LogReader.closeall">closeall</a></td>
<td>Fuehrt close auf File Handler durch.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#LogReader.load_applog">load_applog</a></td>
<td>Uebertraegt Logdaten des PLC Programms Binaer.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#LogReader.load_plclog">load_plclog</a></td>
<td>Uebertraegt Logdaten des Loaders Binaer.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="LogReader.__init__" ID="LogReader.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader (Constructor)</h3>
<b>LogReader</b>(<i></i>)
<p>
Instantiiert LogReader-Klasse.
</p><a NAME="LogReader.closeall" ID="LogReader.closeall"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader.closeall</h3>
<b>closeall</b>(<i></i>)
<p>
Fuehrt close auf File Handler durch.
</p><a NAME="LogReader.load_applog" ID="LogReader.load_applog"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader.load_applog</h3>
<b>load_applog</b>(<i>start, count</i>)
<p>
Uebertraegt Logdaten des PLC Programms Binaer.
</p><dl>
<dt><i>start</i></dt>
<dd>
Startbyte
</dd><dt><i>count</i></dt>
<dd>
Max. Byteanzahl zum uebertragen
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
Binary() der Logdatei
</dd>
</dl><a NAME="LogReader.load_plclog" ID="LogReader.load_plclog"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
LogReader.load_plclog</h3>
<b>load_plclog</b>(<i>start, count</i>)
<p>
Uebertraegt Logdaten des Loaders Binaer.
</p><dl>
<dt><i>start</i></dt>
<dd>
Startbyte
</dd><dt><i>count</i></dt>
<dd>
Max. Byteanzahl zum uebertragen
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
Binary() der Logdatei
</dd>
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="PipeLogwriter" ID="PipeLogwriter"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">PipeLogwriter</h2>
<p>
File PIPE fuer das Schreiben des APP Log.
</p><p>
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.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
Thread
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.__init__">PipeLogwriter</a></td>
<td>Instantiiert PipeLogwriter-Klasse.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.__del__">__del__</a></td>
<td>Close file handler.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter._configurefh">_configurefh</a></td>
<td>Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.logline">logline</a></td>
<td>Schreibt eine Zeile in die Logdatei oder stdout.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.newlogfile">newlogfile</a></td>
<td>Konfiguriert den FileHandler auf eine neue Logdatei.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.run">run</a></td>
<td>Prueft auf neue Logzeilen und schreibt diese.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#PipeLogwriter.stop">stop</a></td>
<td>Beendetden Thread und die FileHandler werden geschlossen.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="PipeLogwriter.__init__" ID="PipeLogwriter.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter (Constructor)</h3>
<b>PipeLogwriter</b>(<i>logfilename</i>)
<p>
Instantiiert PipeLogwriter-Klasse.
</p><dl>
<dt><i>logfilename</i></dt>
<dd>
Dateiname fuer Logdatei
</dd>
</dl><a NAME="PipeLogwriter.__del__" ID="PipeLogwriter.__del__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.__del__</h3>
<b>__del__</b>(<i></i>)
<p>
Close file handler.
</p><a NAME="PipeLogwriter._configurefh" ID="PipeLogwriter._configurefh"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter._configurefh</h3>
<b>_configurefh</b>(<i></i>)
<p>
Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.
</p><dl>
<dt>Returns:</dt>
<dd>
FileHandler-Objekt
</dd>
</dl><a NAME="PipeLogwriter.logline" ID="PipeLogwriter.logline"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.logline</h3>
<b>logline</b>(<i>message</i>)
<p>
Schreibt eine Zeile in die Logdatei oder stdout.
</p><dl>
<dt><i>message</i></dt>
<dd>
Logzeile zum Schreiben
</dd>
</dl><a NAME="PipeLogwriter.newlogfile" ID="PipeLogwriter.newlogfile"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.newlogfile</h3>
<b>newlogfile</b>(<i></i>)
<p>
Konfiguriert den FileHandler auf eine neue Logdatei.
</p><a NAME="PipeLogwriter.run" ID="PipeLogwriter.run"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.run</h3>
<b>run</b>(<i></i>)
<p>
Prueft auf neue Logzeilen und schreibt diese.
</p><a NAME="PipeLogwriter.stop" ID="PipeLogwriter.stop"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
PipeLogwriter.stop</h3>
<b>stop</b>(<i></i>)
<p>
Beendetden Thread und die FileHandler werden geschlossen.
</p>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="RevPiPlc" ID="RevPiPlc"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiPlc</h2>
<p>
Verwaltet das PLC Python Programm.
</p><p>
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.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
Thread
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#RevPiPlc.__init__">RevPiPlc</a></td>
<td>Instantiiert RevPiPlc-Klasse.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._configureplw">_configureplw</a></td>
<td>Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._setuppopen">_setuppopen</a></td>
<td>Setzt UID und GID fuer das PLC Programm.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._spopen">_spopen</a></td>
<td>Startet das PLC Programm.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc._zeroprocimg">_zeroprocimg</a></td>
<td>Setzt Prozessabbild auf NULL.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc.newlogfile">newlogfile</a></td>
<td>Konfiguriert die FileHandler auf neue Logdatei.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc.run">run</a></td>
<td>Fuehrt PLC-Programm aus und ueberwacht es.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiPlc.stop">stop</a></td>
<td>Beendet PLC-Programm.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="RevPiPlc.__init__" ID="RevPiPlc.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc (Constructor)</h3>
<b>RevPiPlc</b>(<i>program, arguments, pversion</i>)
<p>
Instantiiert RevPiPlc-Klasse.
</p><a NAME="RevPiPlc._configureplw" ID="RevPiPlc._configureplw"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._configureplw</h3>
<b>_configureplw</b>(<i></i>)
<p>
Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.
</p><dl>
<dt>Returns:</dt>
<dd>
PipeLogwriter()
</dd>
</dl><a NAME="RevPiPlc._setuppopen" ID="RevPiPlc._setuppopen"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._setuppopen</h3>
<b>_setuppopen</b>(<i></i>)
<p>
Setzt UID und GID fuer das PLC Programm.
</p><a NAME="RevPiPlc._spopen" ID="RevPiPlc._spopen"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._spopen</h3>
<b>_spopen</b>(<i>lst_proc</i>)
<p>
Startet das PLC Programm.
</p><dl>
<dt><i>lst_proc</i></dt>
<dd>
Prozessliste
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
subprocess
</dd>
</dl><a NAME="RevPiPlc._zeroprocimg" ID="RevPiPlc._zeroprocimg"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc._zeroprocimg</h3>
<b>_zeroprocimg</b>(<i></i>)
<p>
Setzt Prozessabbild auf NULL.
</p><a NAME="RevPiPlc.newlogfile" ID="RevPiPlc.newlogfile"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc.newlogfile</h3>
<b>newlogfile</b>(<i></i>)
<p>
Konfiguriert die FileHandler auf neue Logdatei.
</p><a NAME="RevPiPlc.run" ID="RevPiPlc.run"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc.run</h3>
<b>run</b>(<i></i>)
<p>
Fuehrt PLC-Programm aus und ueberwacht es.
</p><a NAME="RevPiPlc.stop" ID="RevPiPlc.stop"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiPlc.stop</h3>
<b>stop</b>(<i></i>)
<p>
Beendet PLC-Programm.
</p>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="RevPiPyLoad" ID="RevPiPyLoad"></a> <a NAME="RevPiPyLoad" ID="RevPiPyLoad"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiPyLoad</h2> <h2 style="background-color:#FFFFFF;color:#0000FF">RevPiPyLoad</h2>
<p> <p>

View File

@@ -1,3 +1,21 @@
logsystem.LogReader.closeall?4()
logsystem.LogReader.load_applog?4(start, count)
logsystem.LogReader.load_plclog?4(start, count)
logsystem.LogReader?1()
logsystem.PipeLogwriter.__del__?6()
logsystem.PipeLogwriter._configurefh?5()
logsystem.PipeLogwriter.logline?4(message)
logsystem.PipeLogwriter.newlogfile?4()
logsystem.PipeLogwriter.run?4()
logsystem.PipeLogwriter.stop?4()
logsystem.PipeLogwriter?1(logfilename)
plcsystem.RevPiPlc._configureplw?5()
plcsystem.RevPiPlc._setuppopen?5()
plcsystem.RevPiPlc._spopen?5(lst_proc)
plcsystem.RevPiPlc.newlogfile?4()
plcsystem.RevPiPlc.run?4()
plcsystem.RevPiPlc.stop?4()
plcsystem.RevPiPlc?1(program, arguments, pversion)
procimgserver.ProcimgServer.devices?4() procimgserver.ProcimgServer.devices?4()
procimgserver.ProcimgServer.ios?4(type) procimgserver.ProcimgServer.ios?4(type)
procimgserver.ProcimgServer.loadrevpimodio?4() procimgserver.ProcimgServer.loadrevpimodio?4()
@@ -5,7 +23,8 @@ procimgserver.ProcimgServer.setvalue?4(device, io, value)
procimgserver.ProcimgServer.start?4() procimgserver.ProcimgServer.start?4()
procimgserver.ProcimgServer.stop?4() procimgserver.ProcimgServer.stop?4()
procimgserver.ProcimgServer.values?4() procimgserver.ProcimgServer.values?4()
procimgserver.ProcimgServer?1(logger, xmlserver, configrsc, procimg, aclmode) procimgserver.ProcimgServer?1(xmlserver, aclmode)
proginit._zeroprocimg?5(self)
proginit.cleanup?4() proginit.cleanup?4()
proginit.configure?4() proginit.configure?4()
proginit.forked?7 proginit.forked?7
@@ -14,26 +33,9 @@ proginit.logapp?7
proginit.logger?7 proginit.logger?7
proginit.logplc?7 proginit.logplc?7
proginit.pargs?7 proginit.pargs?7
proginit.picontrolreset?7
proginit.rapcatalog?7
proginit.startdir?7 proginit.startdir?7
revpipyload.LogReader.closeall?4()
revpipyload.LogReader.load_applog?4(start, count)
revpipyload.LogReader.load_plclog?4(start, count)
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._loadconfig?5()
revpipyload.RevPiPyLoad._plcthread?5() revpipyload.RevPiPyLoad._plcthread?5()
revpipyload.RevPiPyLoad._sigexit?5(signum, frame) revpipyload.RevPiPyLoad._sigexit?5(signum, frame)
@@ -60,8 +62,4 @@ revpipyload.RevPiPyLoad.xml_reload?4()
revpipyload.RevPiPyLoad.xml_setconfig?4(dc, loadnow=False) revpipyload.RevPiPyLoad.xml_setconfig?4(dc, loadnow=False)
revpipyload.RevPiPyLoad.xml_setpictoryrsc?4(filebytes, reset=False) revpipyload.RevPiPyLoad.xml_setpictoryrsc?4(filebytes, reset=False)
revpipyload.RevPiPyLoad?1() revpipyload.RevPiPyLoad?1()
revpipyload.configrsc?7
revpipyload.picontrolreset?7
revpipyload.procimg?7
revpipyload.pyloadverion?7 revpipyload.pyloadverion?7
revpipyload.rapcatalog?7

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-5.1.dtd"> <!DOCTYPE Project SYSTEM "Project-5.1.dtd">
<!-- eric project file for project revpipyload --> <!-- eric project file for project revpipyload -->
<!-- Saved: 2017-07-06, 12:34:30 --> <!-- Saved: 2017-09-12, 10:20:24 -->
<!-- Copyright (C) 2017 Sven Sager, akira@narux.de --> <!-- Copyright (C) 2017 Sven Sager, akira@narux.de -->
<Project version="5.1"> <Project version="5.1">
<Language>en_US</Language> <Language>en_US</Language>
@@ -9,15 +9,17 @@
<ProgLanguage mixed="0">Python3</ProgLanguage> <ProgLanguage mixed="0">Python3</ProgLanguage>
<ProjectType>Console</ProjectType> <ProjectType>Console</ProjectType>
<Description>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.</Description> <Description>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.</Description>
<Version>0.4.2</Version> <Version>0.4.3</Version>
<Author>Sven Sager</Author> <Author>Sven Sager</Author>
<Email>akira@narux.de</Email> <Email>akira@narux.de</Email>
<Eol index="-1"/> <Eol index="1"/>
<Sources> <Sources>
<Source>revpipyload/proginit.py</Source> <Source>revpipyload/proginit.py</Source>
<Source>setup.py</Source> <Source>setup.py</Source>
<Source>revpipyload/revpipyload.py</Source> <Source>revpipyload/revpipyload.py</Source>
<Source>revpipyload/procimgserver.py</Source> <Source>revpipyload/procimgserver.py</Source>
<Source>revpipyload/logsystem.py</Source>
<Source>revpipyload/plcsystem.py</Source>
</Sources> </Sources>
<Forms/> <Forms/>
<Translations/> <Translations/>

186
revpipyload/logsystem.py Normal file
View File

@@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
#
# RevPiPyLoad
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
"""Modul fuer die Verwaltung der Logdateien."""
import os
import proginit
from threading import Event, Lock, Thread
from xmlrpc.client import Binary
class 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.
"""
def __init__(self):
"""Instantiiert LogReader-Klasse."""
self.fhapp = None
self.fhapplk = Lock()
self.fhplc = None
self.fhplclk = Lock()
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 load_applog(self, start, count):
"""Uebertraegt Logdaten des PLC Programms Binaer.
@param start Startbyte
@param count Max. Byteanzahl zum uebertragen
@return Binary() der Logdatei
"""
if not os.access(proginit.logapp, os.R_OK):
return Binary(b'\x16') # 
elif start > os.path.getsize(proginit.logapp):
return Binary(b'\x19') # 
else:
with self.fhapplk:
if self.fhapp is None or self.fhapp.closed:
self.fhapp = open(proginit.logapp, "rb")
self.fhapp.seek(start)
return Binary(self.fhapp.read(count))
def load_plclog(self, start, count):
"""Uebertraegt Logdaten des Loaders Binaer.
@param start Startbyte
@param count Max. Byteanzahl zum uebertragen
@return Binary() der Logdatei
"""
if not os.access(proginit.logplc, os.R_OK):
return Binary(b'\x16') # 
elif start > os.path.getsize(proginit.logplc):
return Binary(b'\x19') # 
else:
with self.fhplclk:
if self.fhplc is None or self.fhplc.closed:
self.fhplc = open(proginit.logplc, "rb")
self.fhplc.seek(start)
return Binary(self.fhplc.read(count))
class PipeLogwriter(Thread):
"""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._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):
"""Close der FileHandler."""
# FileHandler schließen
if self._fh is not None:
self._fh.close()
def _configurefh(self):
"""Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.
@return FileHandler-Objekt"""
proginit.logger.debug("enter PipeLogwriter._configurefh()")
logfile = None
dirname = os.path.dirname(self.logfile)
if os.access(dirname, os.R_OK | os.W_OK):
logfile = open(self.logfile, "a")
else:
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):
"""Prueft auf neue Logzeilen und schreibt diese."""
proginit.logger.debug("enter PipeLogwriter.run()")
fhread = os.fdopen(self._fdr)
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 in write log line")
finally:
self._lckfh.release()
proginit.logger.debug("leave logreader pipe loop")
proginit.logger.debug("close all pipes")
fhread.close()
os.close(self.fdw)
proginit.logger.debug("closed all pipes")
# FileHandler schließen
if self._fh is not None:
self._fh.close()
proginit.logger.debug("leave PipeLogwriter.run()")
def stop(self):
"""Beendetden Thread und die FileHandler werden geschlossen."""
proginit.logger.debug("enter PipeLogwriter.stop()")
self._exit.set()
self._lckfh.acquire()
# Letzten Log in Pipe schreiben zum befreien
try:
os.write(self.fdw, b"\n")
except:
pass
finally:
self._lckfh.release()
proginit.logger.debug("leave PipeLogwriter.stop()")

224
revpipyload/plcsystem.py Normal file
View File

@@ -0,0 +1,224 @@
# -*- coding: utf-8 -*-
#
# RevPiPyLoad
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
"""Modul fuer die Verwaltung der PLC Funktionen."""
import os
import proginit
import shlex
import subprocess
from logsystem import PipeLogwriter
from sys import stdout as sysstdout
from threading import Event, Thread
from time import sleep, asctime
class RevPiPlc(Thread):
"""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.
"""
def __init__(self, program, arguments, pversion):
"""Instantiiert RevPiPlc-Klasse."""
super().__init__()
self._arguments = arguments
self._evt_exit = Event()
self._plw = self._configureplw()
self._program = program
self._procplc = None
self._pversion = pversion
self.autoreload = False
self.exitcode = None
self.gid = 65534
self.uid = 65534
self.zeroonerror = False
self.zeroonexit = False
def _configureplw(self):
"""Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.
@return 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):
logfile = proginit.logapp
elif proginit.pargs.logfile is not None:
logfile = proginit.pargs.logfile
if logfile is not None:
logfile = PipeLogwriter(logfile)
proginit.logger.debug("leave RevPiPlc._configureplw()")
return logfile
def _setuppopen(self):
"""Setzt UID und GID fuer das PLC Programm."""
proginit.logger.info(
"set uid {} and gid {} for plc program".format(
self.uid, self.gid)
)
os.setgid(self.gid)
os.setuid(self.uid)
def _spopen(self, lst_proc):
"""Startet das PLC Programm.
@param lst_proc Prozessliste
@return subprocess"""
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=sysstdout if self._plw is None else self._plw.fdw,
stderr=subprocess.STDOUT
)
proginit.logger.debug("leave RevPiPlc._spopen()")
return sp
def newlogfile(self):
"""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()))
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
))
else:
lst_proc = shlex.split("/usr/bin/env python3 -u {} {}".format(
self._program, self._arguments
))
# Prozess erstellen
proginit.logger.info("start plc program {}".format(self._program))
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():
# Auswerten
self.exitcode = self._procplc.poll()
if self.exitcode is not None:
if self.exitcode > 0:
# PLC Python Programm abgestürzt
proginit.logger.error(
"plc program crashed - exitcode: {}".format(
self.exitcode
)
)
if self.zeroonerror:
proginit._zeroprocimg()
proginit.logger.warning(
"set piControl0 to ZERO after PLC program error")
else:
# PLC Python Programm sauber beendet
proginit.logger.info("plc program did a clean exit")
if self.zeroonexit:
proginit._zeroprocimg()
proginit.logger.info(
"set piControl0 to ZERO after PLC program returns "
"clean exitcode")
if not self._evt_exit.is_set() and self.autoreload:
# Prozess neu starten
self._procplc = self._spopen(lst_proc)
if self.exitcode == 0:
proginit.logger.warning(
"restart plc program after clean exit"
)
else:
proginit.logger.warning(
"restart plc program after crash"
)
else:
break
self._evt_exit.wait(1)
if self._plw is not None:
self._plw.logline("-" * 55)
self._plw.logline("plc: {} stopped: {}".format(
os.path.basename(self._program), asctime()
))
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()
self._plw.join()
proginit.logger.debug("log pipes successfully closed")
proginit.logger.debug("leave RevPiPlc.stop()")
return
# Prozess beenden
count = 0
proginit.logger.info("term plc program {}".format(self._program))
self._procplc.terminate()
while self._procplc.poll() is None and count < 10:
count += 1
proginit.logger.info(
"wait term plc program {} seconds".format(count * 0.5)
)
sleep(0.5)
if self._procplc.poll() is None:
proginit.logger.warning(
"can not term plc program {}".format(self._program)
)
self._procplc.kill()
proginit.logger.warning("killed plc program")
# Exitcode auswerten
self.exitcode = self._procplc.poll()
if self.zeroonexit and self.exitcode == 0 \
or self.zeroonerror and self.exitcode != 0:
proginit._zeroprocimg()
if self._plw is not None:
self._plw.stop()
self._plw.join()
proginit.logger.debug("log pipes successfully closed")
proginit.logger.debug("leave RevPiPlc.stop()")

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
# #
# RevPiPyLoad # RevPiPyLoad
# #
# Webpage: https://revpimodio.org/revpipyplc/ # Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3 # (c) Sven Sager, License: LGPLv3
# #
# -*- coding: utf-8 -*-
"""Stellt Funktionen bereit um das Prozessabbild zu ueberwachen. """Stellt Funktionen bereit um das Prozessabbild zu ueberwachen.
Bei ausreichend Rechten koennen Ausgaenge auch gesetzt werden um einen Bei ausreichend Rechten koennen Ausgaenge auch gesetzt werden um einen
@@ -12,6 +12,7 @@ IO-Check bei Inbetriebname durchzufuehren.
""" """
import pickle import pickle
import proginit
import revpimodio import revpimodio
from xmlrpc.client import Binary from xmlrpc.client import Binary
@@ -27,22 +28,17 @@ class ProcimgServer():
""" """
def __init__(self, logger, xmlserver, configrsc, procimg, aclmode): def __init__(self, xmlserver, aclmode):
"""Instantiiert RevPiCheckServer()-Klasse. """Instantiiert RevPiCheckServer()-Klasse.
@param xmlserver XML-RPC Server @param xmlserver XML-RPC Server
@param procimg Pfad zum Prozessabbild
@param configrsc Pfad zur piCtory Konfigurationsdatei
@param logger Loggerinstanz
@param aclmode Zugriffsrechte @param aclmode Zugriffsrechte
""" """
# Logger übernehmen # Logger übernehmen
self.logger = logger proginit.logger.debug("enter ProcimgServer.__init__()")
self.logger.debug("enter ProcimgServer.__init__()")
self.acl = aclmode self.acl = aclmode
self.configrsc = configrsc
self.procimg = procimg
self.rpi = None self.rpi = None
# XML-Server übernehmen # XML-Server übernehmen
@@ -60,7 +56,7 @@ class ProcimgServer():
self.loadrevpimodio() self.loadrevpimodio()
self.logger.debug("leave ProcimgServer.__init__()") proginit.logger.debug("leave ProcimgServer.__init__()")
def devices(self): def devices(self):
"""Generiert Deviceliste mit Position und Namen. """Generiert Deviceliste mit Position und Namen.
@@ -104,19 +100,19 @@ class ProcimgServer():
if self.rpi is not None: if self.rpi is not None:
self.rpi.cleanup() self.rpi.cleanup()
self.logger.debug("create revpimodio class") proginit.logger.debug("create revpimodio class")
try: try:
self.rpi = revpimodio.RevPiModIO( self.rpi = revpimodio.RevPiModIO(
configrsc=self.configrsc, configrsc=proginit.pargs.configrsc,
procimg=self.procimg, procimg=proginit.pargs.procimg
) )
except: except:
self.rpi = None self.rpi = None
self.logger.error("piCtory configuration not loadable") proginit.logger.error("piCtory configuration not loadable")
return False return False
self.rpi.devices.syncoutputs(device=0) self.rpi.devices.syncoutputs(device=0)
self.logger.debug("created revpimodio class") proginit.logger.debug("created revpimodio class")
return True return True
def setvalue(self, device, io, value): def setvalue(self, device, io, value):
@@ -171,9 +167,9 @@ class ProcimgServer():
def start(self): def start(self):
"""Registriert XML Funktionen. """Registriert XML Funktionen.
@return True, wenn erfolgreich""" @return True, wenn erfolgreich"""
self.logger.debug("enter ProcimgServer.start()") proginit.logger.debug("enter ProcimgServer.start()")
ec = False
ec = False
if self.rpi is not None: if self.rpi is not None:
# Registriere Funktionen # Registriere Funktionen
@@ -188,12 +184,12 @@ class ProcimgServer():
) )
ec = True ec = True
self.logger.debug("leave ProcimgServer.start()") proginit.logger.debug("leave ProcimgServer.start()")
return ec return ec
def stop(self): def stop(self):
"""Entfernt XML-Funktionen.""" """Entfernt XML-Funktionen."""
self.logger.debug("enter ProcimgServer.stop()") proginit.logger.debug("enter ProcimgServer.stop()")
# Entferne Funktionen # Entferne Funktionen
for xmlfunc in self.xmlreadfuncs: for xmlfunc in self.xmlreadfuncs:
@@ -204,4 +200,4 @@ class ProcimgServer():
if xmlfunc in self.xmlsrv.funcs: if xmlfunc in self.xmlsrv.funcs:
del self.xmlsrv.funcs[xmlfunc] del self.xmlsrv.funcs[xmlfunc]
self.logger.debug("leave ProcimgServer.stop()") proginit.logger.debug("leave ProcimgServer.stop()")

View File

@@ -1,17 +1,15 @@
# -*- coding: utf-8 -*-
# #
# RevPiPyLoad # RevPiPyLoad
# #
# Webpage: https://revpimodio.org/revpipyplc/ # Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3 # (c) Sven Sager, License: LGPLv3
# #
# -*- coding: utf-8 -*-
"""Main functions of our program.""" """Main functions of our program."""
import logging import logging
import os.path import os
import sys import sys
from argparse import ArgumentParser from argparse import ArgumentParser
from os import fork as osfork
forked = False forked = False
globalconffile = None globalconffile = None
@@ -19,21 +17,43 @@ logapp = "revpipyloadapp.log"
logplc = "revpipyload.log" logplc = "revpipyload.log"
logger = None logger = None
pargs = None pargs = None
picontrolreset = "/opt/KUNBUS/piControlReset"
rapcatalog = None
startdir = None startdir = None
def _zeroprocimg(self):
"""Setzt Prozessabbild auf NULL."""
procimg = "/dev/piControl0" if pargs is None else pargs.procimg
if os.access(procimg, os.W_OK):
with open(procimg, "w+b", 0) as f:
f.write(bytes(4096))
else:
if logger is not None:
logger.error("zeroprocimg can not write to piControl device")
def cleanup(): def cleanup():
"""Clean up program.""" """Clean up program."""
# NOTE: Pidfile wirklich löschen?
if pargs is not None and pargs.daemon:
if os.path.exists("/var/run/revpipyload.pid"):
os.remove("/var/run/revpipyload.pid")
# Logging beenden # Logging beenden
logging.shutdown() logging.shutdown()
# Dateihandler schließen
if pargs.daemon:
sys.stdout.close()
def configure(): def configure():
"""Initialize general program functions.""" """Initialize general program functions."""
# Command arguments # Command arguments
parser = ArgumentParser( parser = ArgumentParser(
description="RevolutionPi Python3 Loader" description="RevolutionPi Python Loader"
) )
parser.add_argument( parser.add_argument(
"-d", "--daemon", action="store_true", dest="daemon", "-d", "--daemon", action="store_true", dest="daemon",
@@ -50,6 +70,7 @@ def configure():
) )
parser.add_argument( parser.add_argument(
"--procimg", dest="procimg", "--procimg", dest="procimg",
default="/dev/piControl0",
help="Path to process image" help="Path to process image"
) )
parser.add_argument( parser.add_argument(
@@ -63,15 +84,6 @@ def configure():
global pargs global pargs
pargs = parser.parse_args() 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.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 # Prüfen ob als Daemon ausgeführt werden soll
global forked global forked
pidfile = "/var/run/revpipyload.pid" pidfile = "/var/run/revpipyload.pid"
@@ -84,7 +96,7 @@ def configure():
) )
# Zum daemon machen # Zum daemon machen
pid = osfork() pid = os.fork()
if pid > 0: if pid > 0:
with open(pidfile, "w") as f: with open(pidfile, "w") as f:
f.write(str(pid)) f.write(str(pid))
@@ -92,15 +104,58 @@ def configure():
else: else:
forked = True forked = True
# piCtory Konfiguration prüfen
if pargs.configrsc is None:
lst_rsc = ["/etc/revpi/config.rsc", "/opt/KUNBUS/config.rsc"]
for rscfile in lst_rsc:
if os.access(rscfile, os.F_OK | os.R_OK):
pargs.configrsc = rscfile
break
elif not os.access(pargs.configrsc, os.F_OK | os.R_OK):
pargs.configrsc = None
if pargs.configrsc is None:
raise RuntimeError(
"can not find known pictory configurations at {}"
"".format(", ".join(lst_rsc))
)
# piControlReset suchen
global picontrolreset
if not os.access(picontrolreset, os.F_OK | os.X_OK):
picontrolreset = "/usr/bin/piTest -x"
# rap Katalog an bekannten Stellen prüfen und laden
global rapcatalog
lst_rap = [
"/opt/KUNBUS/pictory/resources/data/rap",
"/var/www/pictory/resources/data/rap"
]
for rapfolder in lst_rap:
if os.path.isdir(rapfolder):
rapcatalog = os.listdir(rapfolder)
# 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.conffile)
if pargs.logfile is not None and os.path.dirname(pargs.logfile) == "":
pargs.logfile = os.path.join(startdir, pargs.logfile)
global logapp global logapp
global logplc global logplc
if pargs.daemon: if pargs.daemon:
# Ausgage vor Umhängen schließen
sys.stdout.close()
# Ausgaben umhängen in Logfile # Ausgaben umhängen in Logfile
logapp = "/var/log/revpipyloadapp" logapp = "/var/log/revpipyloadapp"
logplc = "/var/log/revpipyload" logplc = "/var/log/revpipyload"
pargs.conffile = "/etc/revpipyload/revpipyload.conf" pargs.conffile = "/etc/revpipyload/revpipyload.conf"
sys.stdout = open(logplc, "a") sys.stdout = open(logplc, "a")
sys.stderr = sys.stdout sys.stderr = sys.stdout
elif pargs.logfile is not None: elif pargs.logfile is not None:
logplc = pargs.logfile logplc = pargs.logfile

View File

@@ -1,4 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
# -*- coding: utf-8 -*-
# #
# RevPiPyLoad # RevPiPyLoad
# Version: see global var pyloadverion # Version: see global var pyloadverion
@@ -6,7 +7,6 @@
# Webpage: https://revpimodio.org/revpipyplc/ # Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3 # (c) Sven Sager, License: LGPLv3
# #
# -*- coding: utf-8 -*-
"""Revolution Pi Python PLC Loader. """Revolution Pi Python PLC Loader.
Stellt das RevPiPyLoad Programm bereit. Dieses Programm lauft als Daemon auf Stellt das RevPiPyLoad Programm bereit. Dieses Programm lauft als Daemon auf
@@ -31,11 +31,11 @@ begrenzt werden!
""" """
import gzip import gzip
import logsystem
import plcsystem
import proginit import proginit
import os import os
import shlex
import signal import signal
import subprocess
import tarfile import tarfile
import zipfile import zipfile
from concurrent import futures from concurrent import futures
@@ -43,394 +43,13 @@ from configparser import ConfigParser
from json import loads as jloads from json import loads as jloads
from re import match as rematch from re import match as rematch
from shutil import rmtree from shutil import rmtree
from sys import stdout as sysstdout
from tempfile import mkstemp from tempfile import mkstemp
from threading import Thread, Event, Lock from threading import Event
from time import sleep, asctime from time import asctime
from xmlrpc.client import Binary from xmlrpc.client import Binary
from xmlrpc.server import SimpleXMLRPCServer from xmlrpc.server import SimpleXMLRPCServer
configrsc = None pyloadverion = "0.4.3"
picontrolreset = "/opt/KUNBUS/piControlReset"
procimg = "/dev/piControl0"
pyloadverion = "0.4.2"
rapcatalog = None
class 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.
"""
def __init__(self):
"""Instantiiert LogReader-Klasse."""
self.fhapp = None
self.fhapplk = Lock()
self.fhplc = None
self.fhplclk = Lock()
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 load_applog(self, start, count):
"""Uebertraegt Logdaten des PLC Programms Binaer.
@param start Startbyte
@param count Max. Byteanzahl zum uebertragen
@return Binary() der Logdatei
"""
if not os.access(proginit.logapp, os.R_OK):
return Binary(b'\x16') # 
elif start > os.path.getsize(proginit.logapp):
return Binary(b'\x19') # 
else:
with self.fhapplk:
if self.fhapp is None or self.fhapp.closed:
self.fhapp = open(proginit.logapp, "rb")
self.fhapp.seek(start)
return Binary(self.fhapp.read(count))
def load_plclog(self, start, count):
"""Uebertraegt Logdaten des Loaders Binaer.
@param start Startbyte
@param count Max. Byteanzahl zum uebertragen
@return Binary() der Logdatei
"""
if not os.access(proginit.logplc, os.R_OK):
return Binary(b'\x16') # 
elif start > os.path.getsize(proginit.logplc):
return Binary(b'\x19') # 
else:
with self.fhplclk:
if self.fhplc is None or self.fhplc.closed:
self.fhplc = open(proginit.logplc, "rb")
self.fhplc.seek(start)
return Binary(self.fhplc.read(count))
class PipeLogwriter(Thread):
"""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 = 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):
"""Close file handler."""
if self._fh is not None:
self._fh.close()
def _configurefh(self):
"""Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.
@return 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:
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):
"""Prueft auf neue Logzeilen und schreibt diese."""
proginit.logger.debug("enter PipeLogwriter.run()")
fhread = os.fdopen(self._fdr)
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 in 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):
"""Beendetden Thread und die FileHandler werden geschlossen."""
proginit.logger.debug("enter PipeLogwriter.stop()")
self._exit.set()
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):
"""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.
"""
def __init__(self, program, arguments, pversion):
"""Instantiiert RevPiPlc-Klasse."""
super().__init__()
self.autoreload = False
self._arguments = arguments
self._evt_exit = Event()
self.exitcode = None
self.gid = 65534
self._plw = self._configureplw()
self._program = program
self._procplc = None
self._pversion = pversion
self.uid = 65534
self.zeroonerror = False
self.zeroonexit = False
def _configureplw(self):
"""Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.
@return 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):
logfile = proginit.logapp
elif proginit.pargs.logfile is not None:
logfile = proginit.pargs.logfile
if logfile is not None:
logfile = PipeLogwriter(logfile)
proginit.logger.debug("leave RevPiPlc._configureplw()")
return logfile
def _setuppopen(self):
"""Setzt UID und GID fuer das PLC Programm."""
proginit.logger.info(
"set uid {} and gid {} for plc program".format(
self.uid, self.gid)
)
os.setgid(self.gid)
os.setuid(self.uid)
def _spopen(self, lst_proc):
"""Startet das PLC Programm.
@param lst_proc Prozessliste
@return subprocess"""
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=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."""
if os.path.exists("/dev/piControl0"):
with open("/dev/piControl0", "w+b", 0) as f:
f.write(bytes(4096))
def newlogfile(self):
"""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()))
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
))
else:
lst_proc = shlex.split("/usr/bin/env python3 -u {} {}".format(
self._program, self._arguments
))
# Prozess erstellen
proginit.logger.info("start plc program {}".format(self._program))
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():
# Auswerten
self.exitcode = self._procplc.poll()
if self.exitcode is not None:
if self.exitcode > 0:
# PLC Python Programm abgestürzt
proginit.logger.error(
"plc program crashed - exitcode: {}".format(
self.exitcode
)
)
if self.zeroonerror:
self._zeroprocimg()
proginit.logger.warning(
"set piControl0 to ZERO after PLC program error")
else:
# PLC Python Programm sauber beendet
proginit.logger.info("plc program did a clean exit")
if self.zeroonexit:
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:
# Prozess neu starten
self._procplc = self._spopen(lst_proc)
if self.exitcode == 0:
proginit.logger.warning(
"restart plc program after clean exit"
)
else:
proginit.logger.warning(
"restart plc program after crash"
)
else:
break
self._evt_exit.wait(1)
if self._plw is not None:
self._plw.logline("-" * 55)
self._plw.logline("plc: {} stopped: {}".format(
os.path.basename(self._program), asctime()
))
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()
self._plw.join()
proginit.logger.debug("log pipes successfully closed")
proginit.logger.debug("leave RevPiPlc.stop()")
return
# Prozess beenden
count = 0
proginit.logger.info("term plc program {}".format(self._program))
self._procplc.terminate()
while self._procplc.poll() is None and count < 10:
count += 1
proginit.logger.info(
"wait term plc program {} seconds".format(count * 0.5)
)
sleep(0.5)
if self._procplc.poll() is None:
proginit.logger.warning(
"can not term plc program {}".format(self._program)
)
self._procplc.kill()
proginit.logger.warning("killed plc program")
# Exitcode auswerten
self.exitcode = self._procplc.poll()
if self.zeroonexit and self.exitcode == 0 \
or self.zeroonerror and self.exitcode != 0:
self._zeroprocimg()
if self._plw is not None:
self._plw.stop()
self._plw.join()
proginit.logger.debug("log pipes successfully closed")
proginit.logger.debug("leave RevPiPlc.stop()")
class RevPiPyLoad(): class RevPiPyLoad():
@@ -444,56 +63,21 @@ class RevPiPyLoad():
def __init__(self): def __init__(self):
"""Instantiiert RevPiPyLoad-Klasse.""" """Instantiiert RevPiPyLoad-Klasse."""
proginit.configure()
proginit.logger.debug("enter RevPiPyLoad.__init__()") proginit.logger.debug("enter RevPiPyLoad.__init__()")
# piCtory Konfiguration an bekannten Stellen prüfen
global configrsc
configrsc = proginit.pargs.configrsc
lst_rsc = ["/etc/revpi/config.rsc", "/opt/KUNBUS/config.rsc"]
for rscfile in lst_rsc:
if os.access(rscfile, os.F_OK | os.R_OK):
configrsc = rscfile
break
if configrsc is None:
raise RuntimeError(
"can not find known pictory configurations at {}"
"".format(", ".join(lst_rsc))
)
# Alternatives Processabbild verwenden
if proginit.pargs.procimg is not None:
global procimg
procimg = proginit.pargs.procimg
# rap Katalog an bekannten Stellen prüfen und laden
global rapcatalog
lst_rap = [
"/opt/KUNBUS/pictory/resources/data/rap",
"/var/www/pictory/resources/data/rap"
]
for rapfolder in lst_rap:
if os.path.isdir(rapfolder):
rapcatalog = os.listdir(rapfolder)
# piControlReset suchen
global picontrolreset
if not os.access(picontrolreset, os.F_OK | os.X_OK):
picontrolreset = "/usr/bin/piTest -x"
# Klassenattribute # Klassenattribute
self._exit = True self._exit = True
self.pictorymtime = os.path.getmtime(configrsc) self.pictorymtime = os.path.getmtime(proginit.pargs.configrsc)
self.evt_loadconfig = Event() self.evt_loadconfig = Event()
self.globalconfig = ConfigParser() self.globalconfig = ConfigParser()
self.logr = LogReader() self.logr = logsystem.LogReader()
self.plc = None self.plc = None
self.tfile = {} self.tfile = {}
self.tpe = None self.tpe = None
self.xsrv = None self.xsrv = None
self.xml_ps = None self.xml_ps = None
# Load config # Konfiguration laden
self._loadconfig() self._loadconfig()
# Signal events # Signal events
@@ -579,7 +163,7 @@ class RevPiPyLoad():
try: try:
import procimgserver import procimgserver
self.xml_ps = procimgserver.ProcimgServer( self.xml_ps = procimgserver.ProcimgServer(
proginit.logger, self.xsrv, configrsc, procimg, self.xmlrpc self.xsrv, self.xmlrpc
) )
self.xsrv.register_function(self.xml_psstart, "psstart") self.xsrv.register_function(self.xml_psstart, "psstart")
self.xsrv.register_function(self.xml_psstop, "psstop") self.xsrv.register_function(self.xml_psstop, "psstop")
@@ -612,7 +196,8 @@ class RevPiPyLoad():
self.xsrv.register_function( self.xsrv.register_function(
self.xml_plcuploadclean, "plcuploadclean") self.xml_plcuploadclean, "plcuploadclean")
self.xsrv.register_function( self.xsrv.register_function(
lambda: os.system(picontrolreset), "resetpicontrol") lambda: os.system(proginit.picontrolreset),
"resetpicontrol")
self.xsrv.register_function( self.xsrv.register_function(
self.xml_setconfig, "set_config") self.xml_setconfig, "set_config")
self.xsrv.register_function( self.xsrv.register_function(
@@ -643,7 +228,7 @@ class RevPiPyLoad():
return None return None
proginit.logger.debug("create PLC watcher") proginit.logger.debug("create PLC watcher")
th_plc = RevPiPlc( th_plc = plcsystem.RevPiPlc(
os.path.join(self.plcworkdir, self.plcprog), os.path.join(self.plcworkdir, self.plcprog),
self.plcarguments, self.plcarguments,
self.pythonver self.pythonver
@@ -660,11 +245,7 @@ class RevPiPyLoad():
def _sigexit(self, signum, frame): def _sigexit(self, signum, frame):
"""Signal handler to clean and exit program.""" """Signal handler to clean and exit program."""
proginit.logger.debug("enter RevPiPyLoad._sigexit()") proginit.logger.debug("enter RevPiPyLoad._sigexit()")
# Programm stoppen und aufräumen
self.stop() self.stop()
proginit.cleanup()
proginit.logger.debug("leave RevPiPyLoad._sigexit()") proginit.logger.debug("leave RevPiPyLoad._sigexit()")
def _sigloadconfig(self, signum, frame): def _sigloadconfig(self, signum, frame):
@@ -718,7 +299,9 @@ class RevPiPyLoad():
os.path.join(tup_dir[0], file), arcname=arcname os.path.join(tup_dir[0], file), arcname=arcname
) )
if pictory: if pictory:
fh_pack.write(configrsc, arcname="config.rsc") fh_pack.write(
proginit.pargs.configrsc, arcname="config.rsc"
)
except: except:
filename = "" filename = ""
finally: finally:
@@ -730,7 +313,7 @@ class RevPiPyLoad():
try: try:
fh_pack.add(".", arcname=os.path.basename(self.plcworkdir)) fh_pack.add(".", arcname=os.path.basename(self.plcworkdir))
if pictory: if pictory:
fh_pack.add(configrsc, arcname="config.rsc") fh_pack.add(proginit.pargs.configrsc, arcname="config.rsc")
except: except:
filename = "" filename = ""
finally: finally:
@@ -760,9 +343,9 @@ class RevPiPyLoad():
and not self.evt_loadconfig.is_set(): and not self.evt_loadconfig.is_set():
# piCtory auf Veränderung prüfen # piCtory auf Veränderung prüfen
if self.pictorymtime != os.path.getmtime(configrsc): if self.pictorymtime != os.path.getmtime(proginit.pargs.configrsc):
proginit.logger.warning("piCtory configuration was changed") proginit.logger.warning("piCtory configuration was changed")
self.pictorymtime = os.path.getmtime(configrsc) self.pictorymtime = os.path.getmtime(proginit.pargs.configrsc)
if self.xml_ps is not None: if self.xml_ps is not None:
self.xml_psstop() self.xml_psstop()
@@ -831,7 +414,7 @@ class RevPiPyLoad():
"""Gibt die config.rsc Datei von piCotry zurueck. """Gibt die config.rsc Datei von piCotry zurueck.
@return xmlrpc.client.Binary()""" @return xmlrpc.client.Binary()"""
proginit.logger.debug("xmlrpc call getpictoryrsc") proginit.logger.debug("xmlrpc call getpictoryrsc")
with open(configrsc, "rb") as fh: with open(proginit.pargs.configrsc, "rb") as fh:
buff = fh.read() buff = fh.read()
return Binary(buff) return Binary(buff)
@@ -839,7 +422,7 @@ class RevPiPyLoad():
"""Gibt die Rohdaten aus piControl0 zurueck. """Gibt die Rohdaten aus piControl0 zurueck.
@return xmlrpc.client.Binary()""" @return xmlrpc.client.Binary()"""
proginit.logger.debug("xmlrpc call getprocimg") proginit.logger.debug("xmlrpc call getprocimg")
with open(procimg, "rb") as fh: with open(proginit.pargs.procimg, "rb") as fh:
buff = fh.read() buff = fh.read()
return Binary(buff) return Binary(buff)
@@ -872,7 +455,7 @@ class RevPiPyLoad():
-3 Lief nie -3 Lief nie
""" """
proginit.logger.debug("xmlrpc call plcexitcode") # NOTE: proginit.logger.debug("xmlrpc call plcexitcode")
if self.plc is None: if self.plc is None:
return -2 return -2
elif self.plc.is_alive(): elif self.plc.is_alive():
@@ -1043,7 +626,7 @@ class RevPiPyLoad():
return -2 return -2
# Prüfen ob Modulkatalog vorhanden ist # Prüfen ob Modulkatalog vorhanden ist
if rapcatalog is None: if proginit.rapcatalog is None:
return -5 return -5
else: else:
@@ -1051,7 +634,7 @@ class RevPiPyLoad():
for picdev in jconfigrsc["Devices"]: for picdev in jconfigrsc["Devices"]:
found = False found = False
picdev = picdev["id"][7:-4] picdev = picdev["id"][7:-4]
for rapdev in rapcatalog: for rapdev in proginit.rapcatalog:
if rapdev.find(picdev) >= 0: if rapdev.find(picdev) >= 0:
found = True found = True
@@ -1060,13 +643,13 @@ class RevPiPyLoad():
return -4 return -4
try: try:
with open(configrsc, "wb") as fh: with open(proginit.pargs.configrsc, "wb") as fh:
fh.write(filebytes.data) fh.write(filebytes.data)
except: except:
return -3 return -3
else: else:
if reset: if reset:
return os.system(picontrolreset) return os.system(proginit.picontrolreset)
else: else:
return 0 return 0
@@ -1089,5 +672,12 @@ class RevPiPyLoad():
if __name__ == "__main__": if __name__ == "__main__":
# Programmeinstellungen konfigurieren
proginit.configure()
# Programm starten
root = RevPiPyLoad() root = RevPiPyLoad()
root.start() root.start()
# Aufräumen
proginit.cleanup()

View File

@@ -27,7 +27,7 @@ setup(
license="LGPLv3", license="LGPLv3",
name="revpipyload", name="revpipyload",
version="0.4.2", version="0.4.3",
scripts=["data/revpipyload"], scripts=["data/revpipyload"],