diff --git a/debian/revpipyload.init b/debian/revpipyload.init
index 5d19d9c..fa2f8e0 100755
--- a/debian/revpipyload.init
+++ b/debian/revpipyload.init
@@ -5,8 +5,8 @@
# 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
+# Short-Description: RevPiPyLoad to execute python plc program
+# Description: This file manages the RevPiPyLoad on system
# boot. The Loader starts your python plc program and
# check whether it is running.
### END INIT INFO
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..418bfe4
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,26 @@
+
+
+ Diese Klasse registriert zusaetzliche Funktionen an einem besthenden
+ XML-RPC-Server. Der Funktionsumfang wird erweitert um zyklisch das
+ Prozessabbild zu empfangen und bei ausreichend Rechten Ausgaenge zu
+ setzen.
+
+
+
\ No newline at end of file
diff --git a/doc/revpipyload.html b/doc/revpipyload.html
index 16b3a4e..8edc9dc 100644
--- a/doc/revpipyload.html
+++ b/doc/revpipyload.html
@@ -32,7 +32,7 @@ begrenzt werden!
Global Attributes
-
configrsc
picontrolreset
procimg
pyloadverion
+
configrsc
picontrolreset
procimg
pyloadverion
rapcatalog
Classes
@@ -64,8 +64,6 @@ 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.
Statuscode:
- 0 Alles erfolgreich
+ -0 Alles erfolgreich
-1 Kann JSON-Datei nicht laden
-2 piCtory Elemente in JSON-Datei nicht gefunden
-3 Konnte Konfiguraiton nicht schreiben
+ -4 Module in Konfiguration enthalten, die es nicht gibt
+ -5 Kein RAP Katalog zur Ueberpruefung gefunden
Positive Zahl ist exitcode von piControlReset
diff --git a/eric-revpipyload.api b/eric-revpipyload.api
index 080cf22..bdcbe29 100644
--- a/eric-revpipyload.api
+++ b/eric-revpipyload.api
@@ -1,3 +1,11 @@
+procimgserver.ProcimgServer.devices?4()
+procimgserver.ProcimgServer.ios?4(type)
+procimgserver.ProcimgServer.loadrevpimodio?4()
+procimgserver.ProcimgServer.setvalue?4(device, io, value)
+procimgserver.ProcimgServer.start?4()
+procimgserver.ProcimgServer.stop?4()
+procimgserver.ProcimgServer.values?4()
+procimgserver.ProcimgServer?1(logger, xmlserver, configrsc, procimg, aclmode)
proginit.cleanup?4()
proginit.configure?4()
proginit.forked?7
@@ -8,10 +16,8 @@ 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.load_applog?4(start, count)
+revpipyload.LogReader.load_plclog?4(start, count)
revpipyload.LogReader?1()
revpipyload.PipeLogwriter.__del__?6()
revpipyload.PipeLogwriter._configurefh?5()
@@ -48,6 +54,8 @@ 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_psstart?4()
+revpipyload.RevPiPyLoad.xml_psstop?4()
revpipyload.RevPiPyLoad.xml_reload?4()
revpipyload.RevPiPyLoad.xml_setconfig?4(dc, loadnow=False)
revpipyload.RevPiPyLoad.xml_setpictoryrsc?4(filebytes, reset=False)
@@ -56,3 +64,4 @@ revpipyload.configrsc?7
revpipyload.picontrolreset?7
revpipyload.procimg?7
revpipyload.pyloadverion?7
+revpipyload.rapcatalog?7
diff --git a/revpipyload.e4p b/revpipyload.e4p
index 279b131..eff7f78 100644
--- a/revpipyload.e4p
+++ b/revpipyload.e4p
@@ -1,7 +1,7 @@
-
+
en_US
@@ -9,7 +9,7 @@
Python3ConsoleDieser 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.3.0
+ 0.4.2Sven Sagerakira@narux.de
@@ -17,6 +17,7 @@
revpipyload/proginit.pysetup.pyrevpipyload/revpipyload.py
+ revpipyload/procimgserver.py
@@ -28,7 +29,9 @@
docdebianeric-revpipyload.api
+ stdeb.cfg
+ revpipyload/revpipyload.pyMercurial
@@ -209,12 +212,6 @@
setup.py
-
- noindex
-
-
- True
- outputDirectory
diff --git a/revpipyload/procimgserver.py b/revpipyload/procimgserver.py
new file mode 100644
index 0000000..c135843
--- /dev/null
+++ b/revpipyload/procimgserver.py
@@ -0,0 +1,207 @@
+#
+# RevPiPyLoad
+#
+# Webpage: https://revpimodio.org/revpipyplc/
+# (c) Sven Sager, License: LGPLv3
+#
+# -*- coding: utf-8 -*-
+"""Stellt Funktionen bereit um das Prozessabbild zu ueberwachen.
+
+Bei ausreichend Rechten koennen Ausgaenge auch gesetzt werden um einen
+IO-Check bei Inbetriebname durchzufuehren.
+
+"""
+import pickle
+import revpimodio
+from xmlrpc.client import Binary
+
+
+class ProcimgServer():
+
+ """Serverkomponente fuer zusaetzliche XML-RPC Funktionen.
+
+ Diese Klasse registriert zusaetzliche Funktionen an einem besthenden
+ XML-RPC-Server. Der Funktionsumfang wird erweitert um zyklisch das
+ Prozessabbild zu empfangen und bei ausreichend Rechten Ausgaenge zu
+ setzen.
+
+ """
+
+ def __init__(self, logger, xmlserver, configrsc, procimg, aclmode):
+ """Instantiiert RevPiCheckServer()-Klasse.
+
+ @param xmlserver XML-RPC Server
+ @param procimg Pfad zum Prozessabbild
+ @param configrsc Pfad zur piCtory Konfigurationsdatei
+ @param logger Loggerinstanz
+ @param aclmode Zugriffsrechte
+
+ """
+ # Logger übernehmen
+ self.logger = logger
+ self.logger.debug("enter ProcimgServer.__init__()")
+ self.acl = aclmode
+ self.configrsc = configrsc
+ self.procimg = procimg
+ self.rpi = None
+
+ # XML-Server übernehmen
+ self.xmlsrv = xmlserver
+ self.xmlreadfuncs = {
+ "ps_devices": self.devices,
+ "ps_inps": lambda: self.ios("inp"),
+ "ps_outs": lambda: self.ios("out"),
+ "ps_mems": lambda: self.ios("mem"),
+ "ps_values": self.values,
+ }
+ self.xmlwritefuncs = {
+ "ps_setvalue": self.setvalue,
+ }
+
+ self.loadrevpimodio()
+
+ self.logger.debug("leave ProcimgServer.__init__()")
+
+ def devices(self):
+ """Generiert Deviceliste mit Position und Namen.
+ @return list() mit Tuple (pos, name)"""
+ return [
+ (dev.position, dev.name) for dev in self.rpi.devices
+ ]
+
+ def ios(self, type):
+ """Generiert ein dict() der Devices und IOs.
+ @param type IO Typ inp/out
+ @return pickled dict()"""
+ dict_ios = {}
+ for dev in self.rpi.devices:
+ dict_ios[dev.position] = []
+
+ # IO Typen auswerten
+ if type == "inp":
+ lst_io = dev.get_inps()
+ elif type == "out":
+ lst_io = dev.get_outs()
+ elif type == "mem":
+ lst_io = dev.get_mems()
+ else:
+ lst_io = []
+
+ for io in lst_io:
+ dict_ios[dev.position].append([
+ io.name,
+ 1 if io._bitlength == 1 else int(io._bitlength / 8),
+ io.slc_address.start + dev.offset,
+ io.bmk,
+ io._bitaddress,
+ ])
+ return Binary(pickle.dumps(dict_ios))
+
+ def loadrevpimodio(self):
+ """Instantiiert das RevPiModIO Modul.
+ @return True, wenn erfolgreich, sonst False"""
+ # RevPiModIO-Modul Instantiieren
+ if self.rpi is not None:
+ self.rpi.cleanup()
+
+ self.logger.debug("create revpimodio class")
+ try:
+ self.rpi = revpimodio.RevPiModIO(
+ configrsc=self.configrsc,
+ procimg=self.procimg,
+ )
+ except:
+ self.rpi = None
+ self.logger.error("piCtory configuration not loadable")
+ return False
+
+ self.rpi.devices.syncoutputs(device=0)
+ self.logger.debug("created revpimodio class")
+ return True
+
+ def setvalue(self, device, io, value):
+ """Setzt einen Wert auf dem RevPi.
+
+ @param device Device Position oder Name
+ @param io IO Name fuer neuen Wert
+ @param value Neuer Wert
+ @return list() [device, io, status, msg]
+
+ """
+ # Zugriffsrechte prüfen
+ if self.acl < 3:
+ return [
+ device, io, False,
+ "not allowed in XML-RPC permission mode {}".format(self.acl)
+ ]
+
+ # Binary() in bytes() umwandeln
+ if type(value) == Binary:
+ value = value.data
+
+ self.rpi.devices.syncoutputs(device=device)
+
+ try:
+ # Neuen Wert übernehmen
+ if type(value) == bytes or type(value) == bool:
+ self.rpi.devices[device][io].set_value(value)
+ else:
+ self.rpi.devices[device][io].set_value(
+ value.to_bytes(
+ self.rpi.devices[device][io].length, byteorder="little"
+ )
+ )
+ except Exception as e:
+ return [device, io, False, str(e)]
+
+ self.rpi.devices.writeprocimg(device=device)
+ return [device, io, True, ""]
+
+ def values(self):
+ """Liefert Prozessabbild an Client.
+ @return Binary() bytes or None"""
+ if self.rpi.devices.readprocimg() and self.rpi.devices.syncoutputs():
+ bytebuff = b''
+ for dev in self.rpi.devices:
+ bytebuff += bytes(dev)
+ return Binary(bytebuff)
+ else:
+ return None
+
+ def start(self):
+ """Registriert XML Funktionen.
+ @return True, wenn erfolgreich"""
+ self.logger.debug("enter ProcimgServer.start()")
+ ec = False
+
+ if self.rpi is not None:
+
+ # Registriere Funktionen
+ for xmlfunc in self.xmlreadfuncs:
+ self.xmlsrv.register_function(
+ self.xmlreadfuncs[xmlfunc], xmlfunc
+ )
+ if self.acl >= 3:
+ for xmlfunc in self.xmlwritefuncs:
+ self.xmlsrv.register_function(
+ self.xmlwritefuncs[xmlfunc], xmlfunc
+ )
+ ec = True
+
+ self.logger.debug("leave ProcimgServer.start()")
+ return ec
+
+ def stop(self):
+ """Entfernt XML-Funktionen."""
+ self.logger.debug("enter ProcimgServer.stop()")
+
+ # Entferne Funktionen
+ for xmlfunc in self.xmlreadfuncs:
+ if xmlfunc in self.xmlsrv.funcs:
+ del self.xmlsrv.funcs[xmlfunc]
+ if self.acl >= 3:
+ for xmlfunc in self.xmlwritefuncs:
+ if xmlfunc in self.xmlsrv.funcs:
+ del self.xmlsrv.funcs[xmlfunc]
+
+ self.logger.debug("leave ProcimgServer.stop()")
diff --git a/revpipyload/proginit.py b/revpipyload/proginit.py
index 6340bd6..3bc0e98 100644
--- a/revpipyload/proginit.py
+++ b/revpipyload/proginit.py
@@ -102,7 +102,8 @@ def configure():
# Program logger
global logger
- logger = logging.getLogger()
+ if logger is None:
+ logger = logging.getLogger()
# Alle handler entfernen
for lhandler in logger.handlers:
diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py
index 2540acd..964d841 100755
--- a/revpipyload/revpipyload.py
+++ b/revpipyload/revpipyload.py
@@ -33,7 +33,6 @@ begrenzt werden!
import gzip
import proginit
import os
-import pickle
import shlex
import signal
import socket
@@ -47,7 +46,7 @@ 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 tempfile import mkstemp
from threading import Thread, Event, Lock
from time import sleep, asctime
from timeit import default_timer
@@ -57,7 +56,8 @@ from xmlrpc.server import SimpleXMLRPCServer
configrsc = None
picontrolreset = "/opt/KUNBUS/piControlReset"
procimg = "/dev/piControl0"
-pyloadverion = "0.3.0"
+pyloadverion = "0.4.2"
+rapcatalog = None
def _zeroprocimg():
@@ -73,17 +73,15 @@ class LogReader():
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.
"""
def __init__(self):
"""Instantiiert LogReader-Klasse."""
self.fhapp = None
- self.posapp = 0
+ self.fhapplk = Lock()
self.fhplc = None
- self.posplc = 0
+ self.fhplclk = Lock()
def closeall(self):
"""Fuehrt close auf File Handler durch."""
@@ -92,84 +90,45 @@ class LogReader():
if self.fhplc is not None:
self.fhplc.close()
- def get_applines(self):
- """Gibt neue Zeilen ab letzen Aufruf zurueck.
- @returns: list() mit neuen Zeilen"""
+ 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(pickle.dumps([]))
+ return Binary(b'\x16') #
+ elif start > os.path.getsize(proginit.logapp):
+ return Binary(b'\x19') #
else:
- if self.fhapp is None or self.fhapp.closed:
- self.fhapp = open(proginit.logapp)
+ with self.fhapplk:
+ if self.fhapp is None or self.fhapp.closed:
+ self.fhapp = open(proginit.logapp, "rb")
- lst_new = []
- while True:
- self.posapp = self.fhapp.tell()
- line = self.fhapp.readline()
- if line:
- lst_new.append(line)
- else:
- self.fhapp.seek(self.posapp)
- break
+ self.fhapp.seek(start)
+ return Binary(self.fhapp.read(count))
- proginit.logger.debug(
- "got {} new app log lines".format(len(lst_new))
- )
- return Binary(pickle.dumps(lst_new))
+ def load_plclog(self, start, count):
+ """Uebertraegt Logdaten des Loaders Binaer.
- def get_applog(self):
- """Gibt die gesamte Logdatei zurueck.
- @returns: str() mit Logdaten"""
- if not os.access(proginit.logapp, os.R_OK):
- proginit.logger.error(
- "can not access logfile {}".format(proginit.logapp)
- )
- return Binary(pickle.dumps(""))
- else:
- if self.fhapp is None or self.fhapp.closed:
- self.fhapp = open(proginit.logapp)
- self.fhapp.seek(0)
- return Binary(pickle.dumps(self.fhapp.read()))
+ @param start Startbyte
+ @param count Max. Byteanzahl zum uebertragen
+ @return Binary() der Logdatei
- def get_plclines(self):
- """Gibt neue Zeilen ab letzen Aufruf zurueck.
- @returns: list() mit neuen Zeilen"""
+ """
if not os.access(proginit.logplc, os.R_OK):
- proginit.logger.error(
- "can not access logfile {}".format(proginit.logplc)
- )
- return Binary(pickle.dumps([]))
+ return Binary(b'\x16') #
+ elif start > os.path.getsize(proginit.logplc):
+ return Binary(b'\x19') #
else:
- if self.fhplc is None or self.fhplc.closed:
- self.fhplc = open(proginit.logplc)
+ with self.fhplclk:
+ if self.fhplc is None or self.fhplc.closed:
+ self.fhplc = open(proginit.logplc, "rb")
- lst_new = []
- while True:
- self.posplc = self.fhplc.tell()
- line = self.fhplc.readline()
- if line:
- lst_new.append(line)
- else:
- self.fhplc.seek(self.posplc)
- break
-
- proginit.logger.debug(
- "got {} new pyloader log lines".format(len(lst_new))
- )
- return Binary(pickle.dumps(lst_new))
-
- def get_plclog(self):
- """Gibt die gesamte Logdatei zurueck.
- @returns: str() mit Logdaten"""
- if not os.access(proginit.logplc, os.R_OK):
- proginit.logger.error(
- "can not access logfile {}".format(proginit.logplc)
- )
- return Binary(pickle.dumps(""))
- else:
- if self.fhplc is None or self.fhplc.closed:
- self.fhplc = open(proginit.logplc)
- self.fhplc.seek(0)
- return Binary(pickle.dumps(self.fhplc.read()))
+ self.fhplc.seek(start)
+ return Binary(self.fhplc.read(count))
class PipeLogwriter(Thread):
@@ -185,7 +144,7 @@ class PipeLogwriter(Thread):
def __init__(self, logfilename):
"""Instantiiert PipeLogwriter-Klasse.
- @param logfilename: Dateiname fuer Logdatei"""
+ @param logfilename Dateiname fuer Logdatei"""
super().__init__()
self._exit = Event()
self._fh = None
@@ -208,11 +167,10 @@ class PipeLogwriter(Thread):
def _configurefh(self):
"""Konfiguriert den FileHandler fuer Ausgaben der PLCAPP.
- @returns: FileHandler-Objekt"""
+ @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):
@@ -225,7 +183,7 @@ class PipeLogwriter(Thread):
def logline(self, message):
"""Schreibt eine Zeile in die Logdatei oder stdout.
- @param message: Logzeile zum Schreiben"""
+ @param message Logzeile zum Schreiben"""
with self._lckfh:
self._fh.write("{}\n".format(message))
self._fh.flush()
@@ -243,7 +201,6 @@ class PipeLogwriter(Thread):
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()
@@ -251,7 +208,7 @@ class PipeLogwriter(Thread):
self._fh.write(line)
self._fh.flush()
except:
- proginit.logger.exception("PipeLogwriter write log line")
+ proginit.logger.exception("PipeLogwriter in write log line")
finally:
self._lckfh.release()
proginit.logger.debug("leave logreader pipe loop")
@@ -307,7 +264,7 @@ class RevPiPlc(Thread):
def _configureplw(self):
"""Konfiguriert den PipeLogwriter fuer Ausgaben der PLCAPP.
- @returns: PipeLogwriter()"""
+ @return PipeLogwriter()"""
proginit.logger.debug("enter RevPiPlc._configureplw()")
logfile = None
if proginit.pargs.daemon:
@@ -324,15 +281,17 @@ class RevPiPlc(Thread):
def _setuppopen(self):
"""Setzt UID und GID fuer das PLC Programm."""
- proginit.logger.debug(
- "set uid {} and gid {}".format(self.uid, self.gid))
+ 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
- @returns: subprocess"""
+ @param lst_proc Prozessliste
+ @return subprocess"""
proginit.logger.debug("enter RevPiPlc._spopen({})".format(lst_proc))
sp = subprocess.Popen(
lst_proc,
@@ -388,7 +347,7 @@ class RevPiPlc(Thread):
if self.exitcode > 0:
# PLC Python Programm abgestürzt
proginit.logger.error(
- "plc program chrashed - exitcode: {}".format(
+ "plc program crashed - exitcode: {}".format(
self.exitcode
)
)
@@ -422,6 +381,12 @@ class RevPiPlc(Thread):
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):
@@ -434,9 +399,8 @@ class RevPiPlc(Thread):
if self._procplc is None:
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("log pipes successfully closed")
proginit.logger.debug("leave RevPiPlc.stop()")
return
@@ -467,9 +431,8 @@ class RevPiPlc(Thread):
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("log pipes successfully closed")
proginit.logger.debug("leave RevPiPlc.stop()")
@@ -675,12 +638,10 @@ class RevPiPyLoad():
def __init__(self):
"""Instantiiert RevPiPyLoad-Klasse."""
proginit.configure()
-
- # Globale Werte anpassen
- global configrsc
- global picontrolreset
+ proginit.logger.debug("enter RevPiPyLoad.__init__()")
# piCtory Konfiguration an bekannten Stellen prüfen
+ global 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):
@@ -688,16 +649,28 @@ class RevPiPyLoad():
break
if configrsc is None:
raise RuntimeError(
- "can not access known pictory configurations at {}"
+ "can not find known pictory configurations at {}"
"".format(", ".join(lst_rsc))
)
+ # 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
self._exit = True
+ self.pictorymtime = os.path.getmtime(configrsc)
self.evt_loadconfig = Event()
self.globalconfig = ConfigParser()
self.logr = LogReader()
@@ -705,6 +678,7 @@ class RevPiPyLoad():
self.tfile = {}
self.tpe = None
self.xsrv = None
+ self.xml_ps = None
# Load config
self._loadconfig()
@@ -715,8 +689,12 @@ class RevPiPyLoad():
signal.signal(signal.SIGHUP, self._sigloadconfig)
signal.signal(signal.SIGUSR1, self._signewlogfile)
+ proginit.logger.debug("leave RevPiPyLoad.__init__()")
+
def _loadconfig(self):
"""Load configuration file and setup modul."""
+ proginit.logger.debug("enter RevPiPyLoad._loadconfig()")
+
self.evt_loadconfig.clear()
pauseproc = False
@@ -773,18 +751,34 @@ class RevPiPyLoad():
allow_none=True
)
self.xsrv.register_introspection_functions()
+ self.xsrv.register_multicall_functions()
# XML Modus 1 Nur Logs lesen und PLC Programm neu starten
- self.xsrv.register_function(self.logr.get_applines, "get_applines")
- self.xsrv.register_function(self.logr.get_applog, "get_applog")
- self.xsrv.register_function(self.logr.get_plclines, "get_plclines")
- self.xsrv.register_function(self.logr.get_plclog, "get_plclog")
+ self.xsrv.register_function(self.logr.load_applog, "load_applog")
+ self.xsrv.register_function(self.logr.load_plclog, "load_plclog")
self.xsrv.register_function(self.xml_plcexitcode, "plcexitcode")
self.xsrv.register_function(self.xml_plcrunning, "plcrunning")
self.xsrv.register_function(self.xml_plcstart, "plcstart")
self.xsrv.register_function(self.xml_plcstop, "plcstop")
self.xsrv.register_function(self.xml_reload, "reload")
+ # Erweiterte Funktionen anmelden
+ try:
+ import procimgserver
+ self.xml_ps = procimgserver.ProcimgServer(
+ proginit.logger, self.xsrv, configrsc, procimg, self.xmlrpc
+ )
+ self.xsrv.register_function(self.xml_psstart, "psstart")
+ self.xsrv.register_function(self.xml_psstop, "psstop")
+ except:
+ self.xml_ps = None
+ proginit.logger.warning(
+ "can not load revpimodio module. maybe its not installed "
+ "or an old version (required at least 0.15.0). if you "
+ "like to use the process monitor feature, update/install "
+ "revpimodio: 'apt-get install python3-revpimodio'"
+ )
+
# XML Modus 2 Einstellungen lesen und Programm herunterladen
if self.xmlrpc >= 2:
self.xsrv.register_function(
@@ -821,9 +815,11 @@ class RevPiPyLoad():
)
self.start()
+ proginit.logger.debug("leave RevPiPyLoad._loadconfig()")
+
def _plcthread(self):
"""Konfiguriert den PLC-Thread fuer die Ausfuehrung.
- @returns: PLC-Thread Object or None"""
+ @return PLC-Thread Object or None"""
th_plc = None
if self.plcslave:
@@ -851,28 +847,29 @@ class RevPiPyLoad():
th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534))
th_plc.zeroonerror = self.zeroonerror
th_plc.zeroonexit = self.zeroonexit
- proginit.logger.debug("created PLC watcher")
+ proginit.logger.debug("leave RevPiPyLoad._plcthread()")
return th_plc
def _sigexit(self, signum, frame):
"""Signal handler to clean and exit program."""
- proginit.logger.debug("got exit signal")
- self.stop()
+ proginit.logger.debug("enter RevPiPyLoad._sigexit()")
- # Programm aufräumen
+ # Programm stoppen und aufräumen
+ self.stop()
proginit.cleanup()
- proginit.logger.debug("end revpipyload program")
+ proginit.logger.debug("leave RevPiPyLoad._sigexit()")
def _sigloadconfig(self, signum, frame):
"""Signal handler to load configuration."""
- proginit.logger.debug("got reload config signal")
+ proginit.logger.debug("enter RevPiPyLoad._sigloadconfig()")
self.evt_loadconfig.set()
+ proginit.logger.debug("leave RevPiPyLoad._sigloadconfig()")
def _signewlogfile(self, signum, frame):
"""Signal handler to start new logfile."""
- proginit.logger.debug("got new logfile signal")
+ proginit.logger.debug("enter RevPiPyLoad._signewlogfile()")
# Logger neu konfigurieren
proginit.configure()
@@ -885,15 +882,20 @@ class RevPiPyLoad():
# Logreader schließen
self.logr.closeall()
- def packapp(self, mode="tar", pictory=False):
- """Erzeugt aus dem PLC-Programm ein TAR-File.
+ proginit.logger.debug("leave RevPiPyLoad._signewlogfile()")
- @param mode: Packart 'tar' oder 'zip'
- @param pictory: piCtory Konfiguration mit einpacken
- @returns: Dateinamen des Archivs
+ def packapp(self, mode="tar", pictory=False):
+ """Erzeugt aus dem PLC-Programm ein TAR/Zip-File.
+
+ @param mode Packart 'tar' oder 'zip'
+ @param pictory piCtory Konfiguration mit einpacken
+ @return Dateinamen des Archivs
"""
- filename = mktemp(suffix=".packed", prefix="plc")
+ proginit.logger.debug("enter RevPiPyLoad.packapp()")
+
+ tup_file = mkstemp(suffix="_packed", prefix="plc_")
+ filename = tup_file[1]
if mode == "zip":
fh_pack = zipfile.ZipFile(filename, mode="w")
@@ -928,10 +930,13 @@ class RevPiPyLoad():
finally:
fh_pack.close()
+ proginit.logger.debug("leave RevPiPyLoad.packapp()")
return filename
def start(self):
- """Start plcload and PLC python program."""
+ """Start revpipyload."""
+ proginit.logger.debug("enter RevPiPyLoad.start()")
+
proginit.logger.info("starting revpipyload")
self._exit = False
@@ -947,14 +952,28 @@ class RevPiPyLoad():
while not self._exit \
and not self.evt_loadconfig.is_set():
+
+ # piCtory auf Veränderung prüfen
+ if self.pictorymtime != os.path.getmtime(configrsc):
+ proginit.logger.warning("piCtory configuration was changed")
+ self.pictorymtime = os.path.getmtime(configrsc)
+
+ if self.xml_ps is not None:
+ self.xml_psstop()
+ self.xml_ps.loadrevpimodio()
+
self.evt_loadconfig.wait(1)
if not self._exit:
proginit.logger.info("exit python plc program to reload config")
self._loadconfig()
+ proginit.logger.debug("leave RevPiPyLoad.start()")
+
def stop(self):
- """Stop PLC python program and plcload."""
+ """Stop revpipyload."""
+ proginit.logger.debug("enter RevPiPyLoad.stop()")
+
proginit.logger.info("stopping revpipyload")
self._exit = True
@@ -962,6 +981,7 @@ class RevPiPyLoad():
proginit.logger.debug("stopping revpiplc-thread")
self.plc.stop()
self.plc.join()
+ proginit.logger.debug("revpiplc thread successfully closed")
if self.xmlrpc >= 1:
proginit.logger.info("shutting down xmlrpc-server")
@@ -969,9 +989,11 @@ class RevPiPyLoad():
self.tpe.shutdown()
self.xsrv.server_close()
+ proginit.logger.debug("leave RevPiPyLoad.stop()")
+
def xml_getconfig(self):
"""Uebertraegt die RevPiPyLoad Konfiguration.
- @returns: dict() der Konfiguration"""
+ @return dict() der Konfiguration"""
proginit.logger.debug("xmlrpc call getconfig")
dc = {}
dc["autoreload"] = self.autoreload
@@ -990,7 +1012,7 @@ class RevPiPyLoad():
def xml_getfilelist(self):
"""Uebertraegt die Dateiliste vom plcworkdir.
- @returns: list() mit Dateinamen"""
+ @return list() mit Dateinamen"""
proginit.logger.debug("xmlrpc call getfilelist")
lst_file = []
wd = os.walk("./")
@@ -1001,7 +1023,7 @@ class RevPiPyLoad():
def xml_getpictoryrsc(self):
"""Gibt die config.rsc Datei von piCotry zurueck.
- @returns: xmlrpc.client.Binary()"""
+ @return xmlrpc.client.Binary()"""
proginit.logger.debug("xmlrpc call getpictoryrsc")
with open(configrsc, "rb") as fh:
buff = fh.read()
@@ -1009,7 +1031,7 @@ class RevPiPyLoad():
def xml_getprocimg(self):
"""Gibt die Rohdaten aus piControl0 zurueck.
- @returns: xmlrpc.client.Binary()"""
+ @return xmlrpc.client.Binary()"""
proginit.logger.debug("xmlrpc call getprocimg")
with open(procimg, "rb") as fh:
buff = fh.read()
@@ -1018,9 +1040,9 @@ class RevPiPyLoad():
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
+ @param mode Archivart 'tar' 'zip'
+ @param pictory piCtory Konfiguraiton mit einpacken
+ @return Binary() mit Archivdatei
"""
proginit.logger.debug("xmlrpc call plcdownload")
@@ -1039,7 +1061,7 @@ class RevPiPyLoad():
def xml_plcexitcode(self):
"""Gibt den aktuellen exitcode vom PLC Programm zurueck.
- @returns: int() exitcode oder:
+ @return int() exitcode oder:
-1 laeuft noch
-2 Datei nicht gefunden
-3 Lief nie
@@ -1055,14 +1077,15 @@ class RevPiPyLoad():
def xml_plcrunning(self):
"""Prueft ob das PLC Programm noch lauft.
- @returns: True, wenn das PLC Programm noch lauft"""
+ @return 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:
+ @return int() Status:
+ -0 Erfolgreich
-1 Programm lauft noch
-2 Datei nicht gefunden
@@ -1081,7 +1104,8 @@ class RevPiPyLoad():
def xml_plcstop(self):
"""Stoppt das PLC Programm.
- @returns: int() Exitcode vom PLC Programm
+ @return int() Exitcode vom PLC Programm
+ -0 Erfolgreich
-1 PLC Programm lief nicht
"""
@@ -1089,16 +1113,17 @@ class RevPiPyLoad():
if self.plc is not None and self.plc.is_alive():
self.plc.stop()
self.plc.join()
+ proginit.logger.debug("revpiplc thread successfully closed")
return self.plc.exitcode
else:
return -1
def xml_plcupload(self, filedata, filename):
- """Empfaengt Dateien fuer das PLC Programm.
+ """Empfaengt Dateien fuer das PLC Programm einzeln.
- @param filedata: GZIP Binary data der datei
- @param filename: Name inkl. Unterverzeichnis der Datei
- @returns: Ture, wenn Datei erfolgreich gespeichert wurde
+ @param filedata GZIP Binary data der datei
+ @param filename Name inkl. Unterverzeichnis der Datei
+ @return Ture, wenn Datei erfolgreich gespeichert wurde
"""
proginit.logger.debug("xmlrpc call plcupload")
@@ -1128,7 +1153,7 @@ class RevPiPyLoad():
def xml_plcuploadclean(self):
"""Loescht das gesamte plcworkdir Verzeichnis.
- @returns: True, wenn erfolgreich"""
+ @return True, wenn erfolgreich"""
proginit.logger.debug("xmlrpc call plcuploadclean")
try:
rmtree(".", ignore_errors=True)
@@ -1143,7 +1168,7 @@ class RevPiPyLoad():
def xml_setconfig(self, dc, loadnow=False):
"""Empfaengt die RevPiPyLoad Konfiguration.
- @returns: True, wenn erfolgreich angewendet"""
+ @return True, wenn erfolgreich angewendet"""
proginit.logger.debug("xmlrpc call setconfig")
keys = {
"autoreload": "[01]",
@@ -1186,13 +1211,15 @@ class RevPiPyLoad():
def xml_setpictoryrsc(self, filebytes, reset=False):
"""Schreibt die config.rsc Datei von piCotry.
- @param filebytes: xmlrpc.client.Binary()-Objekt
- @param reset: Reset piControl Device
- @returns: Statuscode:
- 0 Alles erfolgreich
+ @param filebytes xmlrpc.client.Binary()-Objekt
+ @param reset Reset piControl Device
+ @return Statuscode:
+ -0 Alles erfolgreich
-1 Kann JSON-Datei nicht laden
-2 piCtory Elemente in JSON-Datei nicht gefunden
-3 Konnte Konfiguraiton nicht schreiben
+ -4 Module in Konfiguration enthalten, die es nicht gibt
+ -5 Kein RAP Katalog zur Ueberpruefung gefunden
Positive Zahl ist exitcode von piControlReset
"""
@@ -1210,6 +1237,23 @@ class RevPiPyLoad():
if chk not in jconfigrsc:
return -2
+ # Prüfen ob Modulkatalog vorhanden ist
+ if rapcatalog is None:
+ return -5
+ else:
+
+ # piCtory Device in Katalog suchen
+ for picdev in jconfigrsc["Devices"]:
+ found = False
+ picdev = picdev["id"][7:-4]
+ for rapdev in rapcatalog:
+ if rapdev.find(picdev) >= 0:
+ found = True
+
+ # Device im Katalog nicht gefunden
+ if not found:
+ return -4
+
try:
with open(configrsc, "wb") as fh:
fh.write(filebytes.data)
@@ -1221,6 +1265,23 @@ class RevPiPyLoad():
else:
return 0
+ def xml_psstart(self):
+ """Startet den Prozessabbildserver.
+ @return True, wenn start erfolgreich"""
+ if self.xml_ps is not None:
+ return self.xml_ps.start()
+ else:
+ return False
+
+ def xml_psstop(self):
+ """Stoppt den Prozessabbildserver.
+ @return True, wenn stop erfolgreich"""
+ if self.xml_ps is not None:
+ self.xml_ps.stop()
+ return True
+ else:
+ return False
+
if __name__ == "__main__":
root = RevPiPyLoad()
diff --git a/setup.py b/setup.py
index ae17d66..64cce8f 100644
--- a/setup.py
+++ b/setup.py
@@ -27,10 +27,12 @@ setup(
license="LGPLv3",
name="revpipyload",
- version="0.3.0",
+ version="0.4.2",
scripts=["data/revpipyload"],
+ install_requires=["revpimodio"],
+
data_files=[
("/etc/default", ["data/etc/default/revpipyload"]),
("/etc/revpipyload", ["data/etc/revpipyload/revpipyload.conf"]),
@@ -40,12 +42,12 @@ setup(
description="PLC Loader für Python-Projekte auf den RevolutionPi",
long_description=""
- "Dieses Programm startet beim Systemstart ein angegebenes Python PLC\n"
- "Programm. Es überwacht das Programm und startet es im Fehlerfall neu.\n"
- "Bei Absturz kann das gesamte /dev/piControl0 auf 0x00 gesetzt werden.\n"
- "Außerdem stellt es einen XML-RPC Server bereit, über den die Software\n"
- "auf den RevPi geladen werden kann. Das Prozessabbild kann über ein Tool\n"
- "zur Laufzeit überwacht werden.",
+ "Dieses Programm startet beim Systemstart ein angegebenes Python PLC \n"
+ "Programm. Es überwacht das Programm und startet es im Fehlerfall neu. \n"
+ "Bei Absturz kann das gesamte /dev/piControl0 auf 0x00 gesetzt werden. \n"
+ "Außerdem stellt es einen XML-RPC Server bereit, über den die Software \n"
+ "auf den RevPi geladen werden kann. Das Prozessabbild kann über ein \n"
+ "Tool zur Laufzeit überwacht werden.",
classifiers=[
"License :: OSI Approved :: "
diff --git a/stdeb.cfg b/stdeb.cfg
index 2d9a383..b6c3754 100644
--- a/stdeb.cfg
+++ b/stdeb.cfg
@@ -1,4 +1,6 @@
[DEFAULT]
-X-Python3-Version: 3.2-
+Debian-Version: 1
+Depends3: python3-revpimodio (>= 0.11.0)
Package: revpipyload
-Suite: stable
\ No newline at end of file
+Suite: stable
+X-Python3-Version: >=3.2