mirror of
https://github.com/naruxde/revpipyload.git
synced 2025-11-08 23:23:52 +01:00
zeroonexit und zeroonerror parameter
prüfen ob plc programm existiert docstrings und logmeldungen
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
autoreload=1
|
autoreload=1
|
||||||
autostart=1
|
autostart=1
|
||||||
|
plcworkdir=/var/lib/revpipyload
|
||||||
plcprogram=program.py
|
plcprogram=program.py
|
||||||
plcslave=0
|
plcslave=0
|
||||||
xmlrpc=1
|
xmlrpc=1
|
||||||
xmlrpcport=55123
|
xmlrpcport=55123
|
||||||
|
zeroonerror=1
|
||||||
zeroonexit=1
|
zeroonexit=1
|
||||||
|
|||||||
@@ -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-03-03, 11:10:33 -->
|
<!-- Saved: 2017-03-05, 13:46:54 -->
|
||||||
<!-- 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,7 +9,7 @@
|
|||||||
<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.2.1</Version>
|
<Version>0.2.2</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"/>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ logplc = "revpipyload.log"
|
|||||||
logger = None
|
logger = None
|
||||||
pargs = None
|
pargs = None
|
||||||
|
|
||||||
|
|
||||||
class ProgInit():
|
class ProgInit():
|
||||||
|
|
||||||
"""Programmfunktionen fuer Parameter und Logger."""
|
"""Programmfunktionen fuer Parameter und Logger."""
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
#
|
#
|
||||||
# RevPiPyLoad
|
# RevPiPyLoad
|
||||||
# Version: 0.2.1
|
# Version: 0.2.2
|
||||||
#
|
#
|
||||||
# Webpage: https://revpimodio.org/
|
# Webpage: https://revpimodio.org/revpipyplc/
|
||||||
# (c) Sven Sager, License: LGPLv3
|
# (c) Sven Sager, License: LGPLv3
|
||||||
#
|
#
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
@@ -20,14 +20,29 @@ from xmlrpc.server import SimpleXMLRPCServer
|
|||||||
|
|
||||||
class LogReader():
|
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.
|
||||||
|
Ausserdem koennen nur neue Zeilen abgerufen werden, um eine dynamische
|
||||||
|
Logansicht zu ermoeglichen.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Instantiiert LogReader-Klasse."""
|
||||||
self.fhapp = None
|
self.fhapp = None
|
||||||
self.posapp = 0
|
self.posapp = 0
|
||||||
self.fhplc = None
|
self.fhplc = None
|
||||||
self.posplc = 0
|
self.posplc = 0
|
||||||
|
|
||||||
def get_applines(self):
|
def get_applines(self):
|
||||||
|
"""Gibt neue Zeilen ab letzen Aufruf zurueck.
|
||||||
|
@returns: list() mit neuen Zeilen"""
|
||||||
if not os.access(proginit.logapp, os.R_OK):
|
if not os.access(proginit.logapp, os.R_OK):
|
||||||
|
proginit.logger.error(
|
||||||
|
"can not access logfile {}".format(proginit.logapp)
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if self.fhapp is None or self.fhapp.closed:
|
if self.fhapp is None or self.fhapp.closed:
|
||||||
@@ -40,12 +55,19 @@ class LogReader():
|
|||||||
if line:
|
if line:
|
||||||
lst_new.append(line)
|
lst_new.append(line)
|
||||||
else:
|
else:
|
||||||
|
self.fhapp.seek(self.posapp)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
proginit.debug("got {} new log lines".format(len(lst_new)))
|
||||||
return lst_new
|
return lst_new
|
||||||
|
|
||||||
def get_applog(self):
|
def get_applog(self):
|
||||||
|
"""Gibt die gesamte Logdatei zurueck.
|
||||||
|
@returns: str() mit Logdaten"""
|
||||||
if not os.access(proginit.logapp, os.R_OK):
|
if not os.access(proginit.logapp, os.R_OK):
|
||||||
|
proginit.logger.error(
|
||||||
|
"can not access logfile {}".format(proginit.logapp)
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if self.fhapp is None or self.fhapp.closed:
|
if self.fhapp is None or self.fhapp.closed:
|
||||||
@@ -54,7 +76,12 @@ class LogReader():
|
|||||||
return self.fhapp.read()
|
return self.fhapp.read()
|
||||||
|
|
||||||
def get_plclines(self):
|
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):
|
if not os.access(proginit.logplc, os.R_OK):
|
||||||
|
proginit.logger.error(
|
||||||
|
"can not access logfile {}".format(proginit.logplc)
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if self.fhplc is None or self.fhplc.closed:
|
if self.fhplc is None or self.fhplc.closed:
|
||||||
@@ -67,12 +94,19 @@ class LogReader():
|
|||||||
if line:
|
if line:
|
||||||
lst_new.append(line)
|
lst_new.append(line)
|
||||||
else:
|
else:
|
||||||
|
self.fhplc.seek(self.posplc)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
proginit.debug("got {} new log lines".format(len(lst_new)))
|
||||||
return lst_new
|
return lst_new
|
||||||
|
|
||||||
def get_plclog(self):
|
def get_plclog(self):
|
||||||
|
"""Gibt die gesamte Logdatei zurueck.
|
||||||
|
@returns: str() mit Logdaten"""
|
||||||
if not os.access(proginit.logplc, os.R_OK):
|
if not os.access(proginit.logplc, os.R_OK):
|
||||||
|
proginit.logger.error(
|
||||||
|
"can not access logfile {}".format(proginit.logplc)
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if self.fhplc is None or self.fhplc.closed:
|
if self.fhplc is None or self.fhplc.closed:
|
||||||
@@ -84,17 +118,28 @@ class LogReader():
|
|||||||
class RevPiPlc(Thread):
|
class RevPiPlc(Thread):
|
||||||
|
|
||||||
def __init__(self, program):
|
def __init__(self, program):
|
||||||
|
"""Instantiiert RevPiPlc-Klasse."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.autoreload = False
|
self.autoreload = False
|
||||||
self._evt_exit = Event()
|
self._evt_exit = Event()
|
||||||
self.exitcode = 0
|
self.exitcode = None
|
||||||
self._lst_proc = shlex.split("/usr/bin/env python3 -u " + program)
|
self._program = program
|
||||||
self._procplc = None
|
self._procplc = None
|
||||||
|
self.zeroonerror = False
|
||||||
self.zeroonexit = False
|
self.zeroonexit = False
|
||||||
|
|
||||||
|
def _zeroprocimg(self):
|
||||||
|
"""Setzt Prozessabbild auf NULL."""
|
||||||
|
if os.exists("/dev/piControl0"):
|
||||||
|
f = open("/dev/piControl0", "w+b", 0)
|
||||||
|
f.write(bytes(4096))
|
||||||
|
proginit.logger.warning("set piControl0 to ZERO")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# Prozess starten
|
"""Fuehrt PLC-Programm aus und ueberwacht es."""
|
||||||
proginit.logger.info("start plc program")
|
lst_proc = shlex.split("/usr/bin/env python3 -u " + self.program)
|
||||||
|
|
||||||
|
# Ausgaben konfigurieren und ggf. umleiten
|
||||||
fh = None
|
fh = None
|
||||||
if proginit.pargs.daemon:
|
if proginit.pargs.daemon:
|
||||||
if os.access(os.path.dirname(proginit.logapp), os.R_OK | os.W_OK):
|
if os.access(os.path.dirname(proginit.logapp), os.R_OK | os.W_OK):
|
||||||
@@ -109,8 +154,9 @@ class RevPiPlc(Thread):
|
|||||||
fh.flush()
|
fh.flush()
|
||||||
|
|
||||||
# Prozess erstellen
|
# Prozess erstellen
|
||||||
|
proginit.logger.info("start plc program {}".format(self.program))
|
||||||
self._procplc = subprocess.Popen(
|
self._procplc = subprocess.Popen(
|
||||||
self._lst_proc, bufsize=1, stdout=fh, stderr=subprocess.STDOUT
|
lst_proc, bufsize=1, stdout=fh, stderr=subprocess.STDOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
while not self._evt_exit.is_set():
|
while not self._evt_exit.is_set():
|
||||||
@@ -127,21 +173,19 @@ class RevPiPlc(Thread):
|
|||||||
self.exitcode
|
self.exitcode
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if self.zeroonerror:
|
||||||
if self.zeroonexit and os.exists("/dev/piControl0"):
|
self._zeroprocimg()
|
||||||
# piControl0 auf NULL setzen
|
|
||||||
f = open("/dev/piControl0", "w+b", 0)
|
|
||||||
f.write(bytes(4096))
|
|
||||||
proginit.logger.warning("set piControl0 to ZERO")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# PLC Python Programm sauber beendet
|
# PLC Python Programm sauber beendet
|
||||||
proginit.logger.info("plc program did a clean exit")
|
proginit.logger.info("plc program did a clean exit")
|
||||||
|
if self.zeroonexit:
|
||||||
|
self._zeroprocimg()
|
||||||
|
|
||||||
if not self._evt_exit.is_set() and self.autoreload:
|
if not self._evt_exit.is_set() and self.autoreload:
|
||||||
# Prozess neu starten
|
# Prozess neu starten
|
||||||
self._procplc = subprocess.Popen(
|
self._procplc = subprocess.Popen(
|
||||||
self._lst_proc, bufsize=1, stdout=fh,
|
lst_proc, bufsize=1, stdout=fh,
|
||||||
stderr=subprocess.STDOUT
|
stderr=subprocess.STDOUT
|
||||||
)
|
)
|
||||||
if self.exitcode == 0:
|
if self.exitcode == 0:
|
||||||
@@ -149,7 +193,9 @@ class RevPiPlc(Thread):
|
|||||||
"restart plc program after clean exit"
|
"restart plc program after clean exit"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
proginit.logger.warning("restart plc program after crash")
|
proginit.logger.warning(
|
||||||
|
"restart plc program after crash"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -157,39 +203,43 @@ class RevPiPlc(Thread):
|
|||||||
|
|
||||||
# Prozess beenden
|
# Prozess beenden
|
||||||
count = 0
|
count = 0
|
||||||
proginit.logger.info("term plc program")
|
proginit.logger.info("term plc program {}".format(self.program))
|
||||||
self._procplc.terminate()
|
self._procplc.terminate()
|
||||||
while self._procplc.poll() is None and count < 10:
|
while self._procplc.poll() is None and count < 10:
|
||||||
count += 1
|
count += 1
|
||||||
proginit.logger.debug(
|
proginit.logger.info(
|
||||||
"wait term plc program {} seconds".format(count * 0.5)
|
"wait term plc program {} seconds".format(count * 0.5)
|
||||||
)
|
)
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
if self._procplc.poll() is None:
|
if self._procplc.poll() is None:
|
||||||
proginit.logger.warning("can not term plc program")
|
proginit.logger.warning(
|
||||||
|
"can not term plc program {}".format(self.program)
|
||||||
|
)
|
||||||
self._procplc.kill()
|
self._procplc.kill()
|
||||||
proginit.logger.warning("killed plc program")
|
proginit.logger.warning("killed plc program")
|
||||||
|
|
||||||
|
# Exitcode auswerten
|
||||||
self.exitcode = self._procplc.poll()
|
self.exitcode = self._procplc.poll()
|
||||||
|
if self.zeroonexit and self.exitcode == 0 \
|
||||||
|
or self.zeroonerror and self.exitcode > 0:
|
||||||
|
self._zeroprocimg()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
"""Beendet PLC-Programm."""
|
||||||
self._evt_exit.set()
|
self._evt_exit.set()
|
||||||
|
|
||||||
|
|
||||||
class RevPiPyLoad(proginit.ProgInit):
|
class RevPiPyLoad(proginit.ProgInit):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Instantiiert RevPiPyLoad-Klasse."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._exit = True
|
self._exit = True
|
||||||
self.evt_loadconfig = Event()
|
self.evt_loadconfig = Event()
|
||||||
|
|
||||||
self.autoreload = None
|
|
||||||
self.logr = LogReader()
|
self.logr = LogReader()
|
||||||
self.plc = None
|
self.plc = None
|
||||||
self.plcprog = None
|
|
||||||
self.plcslave = None
|
|
||||||
self.tpe = None
|
self.tpe = None
|
||||||
self.xmlrpc = None
|
|
||||||
self.xsrv = None
|
self.xsrv = None
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
@@ -204,9 +254,10 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
"""Load configuration file and setup modul."""
|
"""Load configuration file and setup modul."""
|
||||||
self.evt_loadconfig.clear()
|
self.evt_loadconfig.clear()
|
||||||
pauseproc = False
|
pauseproc = False
|
||||||
|
|
||||||
if not self._exit:
|
if not self._exit:
|
||||||
proginit.logger.info(
|
proginit.logger.info(
|
||||||
"shutdown python plc program while getting new config"
|
"shutdown revpipyload while getting new config"
|
||||||
)
|
)
|
||||||
self.stop()
|
self.stop()
|
||||||
pauseproc = True
|
pauseproc = True
|
||||||
@@ -221,9 +272,17 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
self.autoreload = int(self.globalconfig["DEFAULT"].get("autoreload", 1))
|
self.autoreload = int(self.globalconfig["DEFAULT"].get("autoreload", 1))
|
||||||
self.autostart = int(self.globalconfig["DEFAULT"].get("autostart", 0))
|
self.autostart = int(self.globalconfig["DEFAULT"].get("autostart", 0))
|
||||||
self.plcprog = self.globalconfig["DEFAULT"].get("plcprogram", None)
|
self.plcprog = self.globalconfig["DEFAULT"].get("plcprogram", None)
|
||||||
|
self.plcworkdir = self.globalconfig["DEFAULT"].get(
|
||||||
|
"plcworkdir", "/var/lib/revpipyload"
|
||||||
|
)
|
||||||
self.plcslave = int(self.globalconfig["DEFAULT"].get("plcslave", 0))
|
self.plcslave = int(self.globalconfig["DEFAULT"].get("plcslave", 0))
|
||||||
self.xmlrpc = int(self.globalconfig["DEFAULT"].get("xmlrpc", 1))
|
self.xmlrpc = int(self.globalconfig["DEFAULT"].get("xmlrpc", 1))
|
||||||
self.zeroonexit = int(self.globalconfig["DEFAULT"].get("zeroonexit", 1))
|
self.zerooneerror = int(
|
||||||
|
self.globalconfig["DEFAULT"].get("zeroonerror", 1)
|
||||||
|
)
|
||||||
|
self.zeroonexit = int(
|
||||||
|
self.globalconfig["DEFAULT"].get("zeroonexit", 1)
|
||||||
|
)
|
||||||
|
|
||||||
# PLC Thread konfigurieren
|
# PLC Thread konfigurieren
|
||||||
self.plc = self._plcthread()
|
self.plc = self._plcthread()
|
||||||
@@ -256,16 +315,23 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
|
|
||||||
if pauseproc:
|
if pauseproc:
|
||||||
proginit.logger.info(
|
proginit.logger.info(
|
||||||
"start python plc program after getting new config"
|
"start revpipyload after getting new config"
|
||||||
)
|
)
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def _plcthread(self):
|
def _plcthread(self):
|
||||||
"""Konfiguriert den PLC-Thread fuer die Ausfuehrung.
|
"""Konfiguriert den PLC-Thread fuer die Ausfuehrung.
|
||||||
@returns: PLC-Thread Object"""
|
@returns: PLC-Thread Object or None"""
|
||||||
|
|
||||||
|
# Prüfen ob Programm existiert
|
||||||
|
if not os.exists(os.path.join(self.plcworkdir, self.plcprog)):
|
||||||
|
proginit.logger.error("plc file does not exists {}")
|
||||||
|
return
|
||||||
|
|
||||||
proginit.logger.debug("create PLC watcher")
|
proginit.logger.debug("create PLC watcher")
|
||||||
th_plc = RevPiPlc(self.plcprog)
|
th_plc = RevPiPlc(os.path.join(self.plcworkdir, self.plcprog))
|
||||||
th_plc.autoreload = self.autoreload
|
th_plc.autoreload = self.autoreload
|
||||||
|
th_plc.zeroonerror = self.zerooneerror
|
||||||
th_plc.zeroonexit = self.zeroonexit
|
th_plc.zeroonexit = self.zeroonexit
|
||||||
proginit.logger.debug("created PLC watcher")
|
proginit.logger.debug("created PLC watcher")
|
||||||
return th_plc
|
return th_plc
|
||||||
@@ -291,8 +357,9 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
self.tpe.submit(self.xsrv.serve_forever)
|
self.tpe.submit(self.xsrv.serve_forever)
|
||||||
|
|
||||||
if self.autostart:
|
if self.autostart:
|
||||||
proginit.logger.info("starting plc program {}".format(self.plcprog))
|
proginit.logger.debug("starting revpiplc-thread")
|
||||||
self.plc.start()
|
if self.plc is not None:
|
||||||
|
self.plc.start()
|
||||||
|
|
||||||
while not self._exit \
|
while not self._exit \
|
||||||
and not self.evt_loadconfig.is_set():
|
and not self.evt_loadconfig.is_set():
|
||||||
@@ -307,7 +374,7 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
proginit.logger.info("stopping revpipyload")
|
proginit.logger.info("stopping revpipyload")
|
||||||
self._exit = True
|
self._exit = True
|
||||||
|
|
||||||
proginit.logger.info("stopping plc program {}".format(self.plcprog))
|
proginit.logger.debug("stopping revpiplc-thread")
|
||||||
self.plc.stop()
|
self.plc.stop()
|
||||||
self.plc.join()
|
self.plc.join()
|
||||||
|
|
||||||
@@ -334,8 +401,11 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
return -1
|
return -1
|
||||||
else:
|
else:
|
||||||
self.plc = self._plcthread()
|
self.plc = self._plcthread()
|
||||||
self.plc.start()
|
if self.plc is None:
|
||||||
return self.plc.exitcode
|
return 100
|
||||||
|
else:
|
||||||
|
self.plc.start()
|
||||||
|
return 0
|
||||||
|
|
||||||
def xml_plcstop(self):
|
def xml_plcstop(self):
|
||||||
proginit.logger.debug("xmlrpc call plcstop")
|
proginit.logger.debug("xmlrpc call plcstop")
|
||||||
@@ -343,7 +413,7 @@ class RevPiPyLoad(proginit.ProgInit):
|
|||||||
self.plc.join()
|
self.plc.join()
|
||||||
return self.plc.exitcode
|
return self.plc.exitcode
|
||||||
|
|
||||||
def xml_plcupload(self, file):
|
def xml_plcupload(self, path=None, file=None, clear=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def xml_reload(self):
|
def xml_reload(self):
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -21,13 +21,13 @@ class MyEggInfo(distutils.command.install_egg_info.install_egg_info):
|
|||||||
setup(
|
setup(
|
||||||
author="Sven Sager",
|
author="Sven Sager",
|
||||||
author_email="akira@narux.de",
|
author_email="akira@narux.de",
|
||||||
url="https://revpimodio.org",
|
url="https://revpimodio.org/revpipyplc/",
|
||||||
maintainer="Sven Sager",
|
maintainer="Sven Sager",
|
||||||
maintainer_email="akira@revpimodio.org",
|
maintainer_email="akira@revpimodio.org",
|
||||||
|
|
||||||
license="LGPLv3",
|
license="LGPLv3",
|
||||||
name="revpipyload",
|
name="revpipyload",
|
||||||
version="0.2.1",
|
version="0.2.2",
|
||||||
|
|
||||||
scripts=["data/revpipyload"],
|
scripts=["data/revpipyload"],
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user