diff --git a/MANIFEST.in b/MANIFEST.in index 6cf5516..daa6a75 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ recursive-include data * +recursive-include debian * recursive-include revpipyload * global-exclude test/* global-exclude *.pyc diff --git a/debian/revpipyload.init b/debian/revpipyload.init new file mode 100755 index 0000000..7ddc1b3 --- /dev/null +++ b/debian/revpipyload.init @@ -0,0 +1,135 @@ +#! /bin/bash +### BEGIN INIT INFO +# Provides: revpipyload +# Required-Start: $remote_fs $syslog $piControl +# Required-Stop: $remote_fs $syslog $piControl +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start RevPiPyLoad to execute python plc program +# Description: This file starts the RevPiPyLoad on system +# boot. The Loader starts your python plc program and +# check whether it is running. +### END INIT INFO + +# Author: Akira Naru Takizawa + +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="RevPiPyLoad to run plc program" +NAME=revpipyload +DAEMON_ARGS="-d" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Check install dir +if [ -d /usr/local/share/revpipyload ] +then + DAEMON=/usr/share/revpipyload/revpipyload.py +else + DAEMON=/usr/share/revpipyload/revpipyload.py +fi + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME.py + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME.py + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + reload) + log_daemon_msg "Reloading $DESC" "$NAME" + do_reload + log_end_msg $? + ;; + restart) + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload}" >&2 + exit 3 + ;; +esac diff --git a/debian/revpipyload.logrotate b/debian/revpipyload.logrotate new file mode 100644 index 0000000..4097d2c --- /dev/null +++ b/debian/revpipyload.logrotate @@ -0,0 +1,18 @@ +/var/log/revpipyload +{ + rotate 6 + weekly + compress + delaycompress + missingok + notifempty +} +/var/log/revpipyloadapp +{ + rotate 6 + weekly + compress + delaycompress + missingok + notifempty +} diff --git a/doc/revpipyload.revpipyload.html b/doc/revpipyload.revpipyload.html new file mode 100644 index 0000000..42bae03 --- /dev/null +++ b/doc/revpipyload.revpipyload.html @@ -0,0 +1,555 @@ + + +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.e4p b/revpipyload.e4p index 51bc687..d025b86 100644 --- a/revpipyload.e4p +++ b/revpipyload.e4p @@ -1,7 +1,7 @@ - + en_US @@ -9,7 +9,7 @@ Python3 Console Dieser Loader wird über das Init-System geladen und führt das angegebene Pythonprogramm aus. Es ist für den RevolutionPi gedacht um automatisch das SPS-Programm zu starten. - 0.2.3 + 0.2.5 Sven Sager akira@narux.de @@ -26,6 +26,7 @@ data MANIFEST.in + doc Mercurial @@ -140,6 +141,69 @@ + + + + + ERIC4DOC + + + + + ignoreDirectories + + + + data + deb + dist + + + + ignoreFilePatterns + + + + + + + + noindex + + + True + + + outputDirectory + + + doc + + + qtHelpEnabled + + + False + + + sourceExtensions + + + + + + + + useRecursion + + + True + + + + + + diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index ea879dd..2464404 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -16,6 +16,7 @@ import subprocess import tarfile import zipfile from concurrent import futures +from json import loads as jloads from shutil import rmtree from tempfile import mktemp from threading import Thread, Event @@ -26,7 +27,7 @@ from xmlrpc.server import SimpleXMLRPCServer configrsc = "/opt/KUNBUS/config.rsc" picontrolreset = "/opt/KUNBUS/piControlReset" procimg = "/dev/piControl0" -pyloadverion = "0.2.3" +pyloadverion = "0.2.5" class LogReader(): @@ -129,6 +130,15 @@ class LogReader(): 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, pversion): """Instantiiert RevPiPlc-Klasse.""" super().__init__() @@ -255,6 +265,13 @@ class RevPiPlc(Thread): class RevPiPyLoad(proginit.ProgInit): + """Hauptklasse, die alle Funktionen zur Verfuegung stellt. + + Hier wird die gesamte Konfiguraiton eingelesen und der ggf. aktivierte + XML-RPC-Server gestartet. + + """ + def __init__(self): """Instantiiert RevPiPyLoad-Klasse.""" super().__init__() @@ -393,8 +410,12 @@ class RevPiPyLoad(proginit.ProgInit): def packapp(self, mode="tar", pictory=False): """Erzeugt aus dem PLC-Programm ein TAR-File. + @param mode: Packart 'tar' oder 'zip' - @param pictory: piCtory Konfiguration mit einpacken""" + @param pictory: piCtory Konfiguration mit einpacken + @returns: Dateinamen des Archivs + + """ filename = mktemp(suffix=".packed", prefix="plc") # TODO: Fehlerabfang @@ -458,6 +479,8 @@ class RevPiPyLoad(proginit.ProgInit): self.xsrv.server_close() def xml_getconfig(self): + """Uebertraegt die RevPiPyLoad Konfiguration. + @returns: dict() der Konfiguration""" proginit.logger.debug("xmlrpc call getconfig") dc = {} dc["autoreload"] = self.autoreload @@ -473,6 +496,8 @@ class RevPiPyLoad(proginit.ProgInit): return dc def xml_getfilelist(self): + """Uebertraegt die Dateiliste vom plcworkdir. + @returns: list() mit Dateinamen""" proginit.logger.debug("xmlrpc call getfilelist") lst_file = [] wd = os.walk("./") @@ -498,6 +523,13 @@ class RevPiPyLoad(proginit.ProgInit): return Binary(buff) def xml_plcdownload(self, mode="tar", pictory=False): + """Uebertraegt ein Archiv vom plcworkdir. + + @param mode: Archivart 'tar' 'zip' + @param pictory: piCtory Konfiguraiton mit einpacken + @returns: Binary() mit Archivdatei + + """ proginit.logger.debug("xmlrpc call plcdownload") # TODO: Daten blockweise übertragen @@ -511,6 +543,8 @@ class RevPiPyLoad(proginit.ProgInit): return xmldata def xml_plcexitcode(self): + """Gibt den aktuellen exitcode vom PLC Programm zurueck. + @returns: int() exitcode oder -1 lauuft noch -2 lief nie""" proginit.logger.debug("xmlrpc call plcexitcode") if self.plc is None: return -2 @@ -520,10 +554,19 @@ class RevPiPyLoad(proginit.ProgInit): return self.plc.exitcode def xml_plcrunning(self): + """Prueft ob das PLC Programm noch lauft. + @returns: True, wenn das PLC Programm noch lauft""" proginit.logger.debug("xmlrpc call plcrunning") return False if self.plc is None else self.plc.is_alive() def xml_plcstart(self): + """Startet das PLC Programm. + + @returns: int() Status: + -1 Programm lauft noch + 100 Fehler + + """ proginit.logger.debug("xmlrpc call plcstart") if self.plc is not None and self.plc.is_alive(): return -1 @@ -536,6 +579,8 @@ class RevPiPyLoad(proginit.ProgInit): return 0 def xml_plcstop(self): + """Stoppt das PLC Programm. + @returns: int() Exitcode vom PLC Programm""" proginit.logger.debug("xmlrpc call plcstop") if self.plc is not None: self.plc.stop() @@ -545,6 +590,13 @@ class RevPiPyLoad(proginit.ProgInit): return -1 def xml_plcupload(self, filedata, filename): + """Empfaengt Dateien fuer das PLC Programm. + + @param filedata: GZIP Binary data der datei + @param filename: Name inkl. Unterverzeichnis der Datei + @returns: Ture, wenn Datei erfolgreich gespeichert wurde + + """ proginit.logger.debug("xmlrpc call plcupload") noerr = False @@ -571,6 +623,8 @@ class RevPiPyLoad(proginit.ProgInit): return noerr def xml_plcuploadclean(self): + """Loescht das gesamte plcworkdir Verzeichnis. + @returns: True, wenn erfolgreich""" proginit.logger.debug("xmlrpc call plcuploadclean") try: rmtree(".", ignore_errors=True) @@ -579,10 +633,12 @@ class RevPiPyLoad(proginit.ProgInit): return True def xml_reload(self): + """Startet RevPiPyLoad neu und verwendet neue Konfiguraiton.""" proginit.logger.debug("xmlrpc call reload") self.evt_loadconfig.set() def xml_setconfig(self, dc, loadnow=False): + """Empfaengt die RevPiPyLoad Konfiguration.""" proginit.logger.debug("xmlrpc call setconfig") keys = [ "autoreload", @@ -616,18 +672,33 @@ class RevPiPyLoad(proginit.ProgInit): @param filebytes: xmlrpc.client.Binary()-Objekt @param reset: Reset piControl Device - @returns: Statuscode + @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 """ proginit.logger.debug("xmlrpc call setpictoryrsc") - # TODO: Prüfen ob es wirklich eine piCtory Datei ist + # Datei als JSON laden + try: + jconfigrsc = jloads(filebytes.data.decode()) + except: + return -1 + + # Elemente prüfen + lst_check = ["Devices", "Sumary", "App"] + for chk in lst_check: + if chk not in jconfigrsc: + return -2 try: with open(configrsc, "wb") as fh: fh.write(filebytes.data) except: - return -1 + return -3 else: if reset: return os.system(picontrolreset) diff --git a/setup.py b/setup.py index d330dd1..2610c42 100644 --- a/setup.py +++ b/setup.py @@ -5,8 +5,8 @@ # -*- coding: utf-8 -*- """Setupscript fuer RevPiPyLoad.""" import distutils.command.install_egg_info -from distutils.core import setup from glob import glob +from setuptools import setup class MyEggInfo(distutils.command.install_egg_info.install_egg_info): @@ -27,14 +27,12 @@ setup( license="LGPLv3", name="revpipyload", - version="0.2.3", + version="0.2.5", scripts=["data/revpipyload"], data_files=[ ("/etc/default", ["data/etc/default/revpipyload"]), - ("/etc/init.d", ["data/etc/init.d/revpipyload"]), - ("/etc/logrotate.d", ["data/etc/logrotate.d/revpipyload"]), ("/etc/revpipyload", ["data/etc/revpipyload/revpipyload.conf"]), ("share/revpipyload", glob("revpipyload/*.*")), ("/var/lib/revpipyload", ["data/var/lib/revpipyload/.placeholder"])