mirror of
https://github.com/naruxde/revpipyload.git
synced 2025-11-08 23:23:52 +01:00
"Reset Driver" of piCtory can now restart your plc program
You can set reset_driver_action in revpipyload.conf to 0, 1, 2 0=do nothing 1=Restart PLC program only if piCtory config was changed 2=Always restart PLC program after pressing "Reset Driver" (default) (will not work on old wheezy) close #1
This commit is contained in:
6
.idea/codeStyles/Project.xml
generated
Normal file
6
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
|
<option name="RIGHT_MARGIN" value="80" />
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
7
.idea/dictionaries/akira.xml
generated
Normal file
7
.idea/dictionaries/akira.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="akira">
|
||||||
|
<words>
|
||||||
|
<w>ctory</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
@@ -5,11 +5,13 @@ autostart = 1
|
|||||||
plcworkdir = /var/lib/revpipyload
|
plcworkdir = /var/lib/revpipyload
|
||||||
plcworkdir_set_uid = 0
|
plcworkdir_set_uid = 0
|
||||||
plcprogram = program.py
|
plcprogram = program.py
|
||||||
|
plcprogram_watchdog = 0
|
||||||
plcarguments =
|
plcarguments =
|
||||||
plcuid = 1000
|
plcuid = 1000
|
||||||
plcgid = 1000
|
plcgid = 1000
|
||||||
pythonversion = 3
|
pythonversion = 3
|
||||||
replace_ios = /etc/revpipyload/replace_ios.conf
|
replace_ios = /etc/revpipyload/replace_ios.conf
|
||||||
|
reset_driver_action = 2
|
||||||
rtlevel = 0
|
rtlevel = 0
|
||||||
zeroonerror = 0
|
zeroonerror = 0
|
||||||
zeroonexit = 0
|
zeroonexit = 0
|
||||||
|
|||||||
@@ -29,32 +29,34 @@ __author__ = "Sven Sager"
|
|||||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
__version__ = "0.8.5"
|
__version__ = "0.8.5"
|
||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
import logsystem
|
|
||||||
import picontrolserver
|
|
||||||
import plcsystem
|
|
||||||
import proginit
|
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import tarfile
|
import tarfile
|
||||||
import zipfile
|
import zipfile
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from helper import refullmatch
|
|
||||||
from json import loads as jloads
|
from json import loads as jloads
|
||||||
from shared.ipaclmanager import IpAclManager
|
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from tempfile import mkstemp
|
from tempfile import mkstemp
|
||||||
from threading import Event
|
from threading import Event
|
||||||
from time import asctime
|
from time import asctime
|
||||||
from xmlrpc.client import Binary
|
from xmlrpc.client import Binary
|
||||||
|
|
||||||
|
import logsystem
|
||||||
|
import picontrolserver
|
||||||
|
import plcsystem
|
||||||
|
import proginit
|
||||||
|
from helper import refullmatch
|
||||||
|
from shared.ipaclmanager import IpAclManager
|
||||||
|
from watchdogs import ResetDriverWatchdog
|
||||||
from xrpcserver import SaveXMLRPCServer
|
from xrpcserver import SaveXMLRPCServer
|
||||||
|
|
||||||
min_revpimodio = "2.4.5"
|
min_revpimodio = "2.4.5"
|
||||||
|
|
||||||
|
|
||||||
class RevPiPyLoad():
|
class RevPiPyLoad():
|
||||||
|
|
||||||
"""Hauptklasse, die alle Funktionen zur Verfuegung stellt.
|
"""Hauptklasse, die alle Funktionen zur Verfuegung stellt.
|
||||||
|
|
||||||
Hier wird die gesamte Konfiguraiton eingelesen und der ggf. aktivierte
|
Hier wird die gesamte Konfiguraiton eingelesen und der ggf. aktivierte
|
||||||
@@ -207,34 +209,38 @@ class RevPiPyLoad():
|
|||||||
restart_plcprogram = self._check_mustrestart_plcprogram()
|
restart_plcprogram = self._check_mustrestart_plcprogram()
|
||||||
|
|
||||||
# Konfiguration verarbeiten [DEFAULT]
|
# Konfiguration verarbeiten [DEFAULT]
|
||||||
self.autoreload = \
|
self.autoreload = self.globalconfig["DEFAULT"].getboolean(
|
||||||
self.globalconfig["DEFAULT"].getboolean("autoreload", True)
|
"autoreload", True)
|
||||||
self.autoreloaddelay = \
|
self.autoreloaddelay = self.globalconfig["DEFAULT"].getint(
|
||||||
self.globalconfig["DEFAULT"].getint("autoreloaddelay", 5)
|
"autoreloaddelay", 5)
|
||||||
self.autostart = \
|
self.autostart = self.globalconfig["DEFAULT"].getboolean(
|
||||||
self.globalconfig["DEFAULT"].getboolean("autostart", False)
|
"autostart", False)
|
||||||
self.plcworkdir = \
|
self.plcworkdir = self.globalconfig["DEFAULT"].get(
|
||||||
self.globalconfig["DEFAULT"].get("plcworkdir", ".")
|
"plcworkdir", ".")
|
||||||
self.plcprogram = \
|
self.plcprogram = self.globalconfig["DEFAULT"].get(
|
||||||
self.globalconfig["DEFAULT"].get("plcprogram", "none.py")
|
"plcprogram", "none.py")
|
||||||
self.plcarguments = \
|
self.plcprogram_watchdog = self.globalconfig["DEFAULT"].getboolean(
|
||||||
self.globalconfig["DEFAULT"].get("plcarguments", "")
|
"plcprogram_watchdog", False)
|
||||||
|
self.plcarguments = self.globalconfig["DEFAULT"].get(
|
||||||
|
"plcarguments", "")
|
||||||
self.plcworkdir_set_uid = self.globalconfig["DEFAULT"].getboolean(
|
self.plcworkdir_set_uid = self.globalconfig["DEFAULT"].getboolean(
|
||||||
"plcworkdir_set_uid", False)
|
"plcworkdir_set_uid", False)
|
||||||
self.plcuid = \
|
self.plcuid = self.globalconfig["DEFAULT"].getint(
|
||||||
self.globalconfig["DEFAULT"].getint("plcuid", 65534)
|
"plcuid", 65534)
|
||||||
self.plcgid = \
|
self.plcgid = self.globalconfig["DEFAULT"].getint(
|
||||||
self.globalconfig["DEFAULT"].getint("plcgid", 65534)
|
"plcgid", 65534)
|
||||||
self.pythonversion = \
|
self.pythonversion = self.globalconfig["DEFAULT"].getint(
|
||||||
self.globalconfig["DEFAULT"].getint("pythonversion", 3)
|
"pythonversion", 3)
|
||||||
self.replace_ios_config = \
|
self.replace_ios_config = self.globalconfig["DEFAULT"].get(
|
||||||
self.globalconfig["DEFAULT"].get("replace_ios", "")
|
"replace_ios", "")
|
||||||
self.rtlevel = \
|
self.rtlevel = self.globalconfig["DEFAULT"].getint(
|
||||||
self.globalconfig["DEFAULT"].getint("rtlevel", 0)
|
"rtlevel", 0)
|
||||||
self.zeroonerror = \
|
self.reset_driver_action = self.globalconfig["DEFAULT"].getint(
|
||||||
self.globalconfig["DEFAULT"].getboolean("zeroonerror", True)
|
"reset_driver_action", 2)
|
||||||
self.zeroonexit = \
|
self.zeroonerror = self.globalconfig["DEFAULT"].getboolean(
|
||||||
self.globalconfig["DEFAULT"].getboolean("zeroonexit", True)
|
"zeroonerror", True)
|
||||||
|
self.zeroonexit = self.globalconfig["DEFAULT"].getboolean(
|
||||||
|
"zeroonexit", True)
|
||||||
|
|
||||||
# Dateiveränderungen prüfen
|
# Dateiveränderungen prüfen
|
||||||
file_changed = False
|
file_changed = False
|
||||||
@@ -336,9 +342,9 @@ class RevPiPyLoad():
|
|||||||
# Workdirectory owner setzen
|
# Workdirectory owner setzen
|
||||||
try:
|
try:
|
||||||
if self.plcworkdir_set_uid:
|
if self.plcworkdir_set_uid:
|
||||||
os.chown(self.plcworkdir, self.plcuid, -1)
|
os.chown(self.plcworkdir, self.plcuid, -1)
|
||||||
else:
|
else:
|
||||||
os.chown(self.plcworkdir, 0, -1)
|
os.chown(self.plcworkdir, 0, -1)
|
||||||
except Exception:
|
except Exception:
|
||||||
proginit.logger.warning(
|
proginit.logger.warning(
|
||||||
"could not set user id on working directory"
|
"could not set user id on working directory"
|
||||||
@@ -748,17 +754,21 @@ class RevPiPyLoad():
|
|||||||
if self.autostart and self.plc is not None:
|
if self.autostart and self.plc is not None:
|
||||||
self.plc.start()
|
self.plc.start()
|
||||||
|
|
||||||
|
# Watchdog to detect the reset_driver event
|
||||||
|
pictory_reset_driver = ResetDriverWatchdog()
|
||||||
|
|
||||||
# mainloop
|
# mainloop
|
||||||
while not self._exit:
|
while not self._exit:
|
||||||
file_changed = False
|
|
||||||
|
|
||||||
# Neue Konfiguration laden
|
# Neue Konfiguration laden
|
||||||
if self.evt_loadconfig.is_set():
|
if self.evt_loadconfig.is_set():
|
||||||
proginit.logger.info("got reqeust to reload config")
|
proginit.logger.info("got reqeust to reload config")
|
||||||
self._loadconfig()
|
self._loadconfig()
|
||||||
|
|
||||||
|
file_changed = False
|
||||||
|
reset_driver_detected = pictory_reset_driver.triggered
|
||||||
|
|
||||||
# Dateiveränderungen prüfen mit beiden Funktionen!
|
# Dateiveränderungen prüfen mit beiden Funktionen!
|
||||||
if self.check_pictory_changed():
|
if reset_driver_detected and self.check_pictory_changed():
|
||||||
file_changed = True
|
file_changed = True
|
||||||
|
|
||||||
# Alle Verbindungen von ProcImgServer trennen
|
# Alle Verbindungen von ProcImgServer trennen
|
||||||
@@ -789,6 +799,18 @@ class RevPiPyLoad():
|
|||||||
self.xml_ps.loadrevpimodio()
|
self.xml_ps.loadrevpimodio()
|
||||||
# Kein psstart um Reload im Client zu erzeugen
|
# Kein psstart um Reload im Client zu erzeugen
|
||||||
|
|
||||||
|
# Restart plc program after piCtory change
|
||||||
|
if self.plc is not None and self.plc.is_alive() and (
|
||||||
|
self.reset_driver_action == 1 and file_changed or
|
||||||
|
self.reset_driver_action == 2 and reset_driver_detected):
|
||||||
|
# Plc program is running and we have to restart it
|
||||||
|
proginit.logger.warning(
|
||||||
|
"restart plc program after 'reset driver' was requested"
|
||||||
|
)
|
||||||
|
self.stop_plcprogram()
|
||||||
|
self.plc = self._plcthread()
|
||||||
|
self.plc.start()
|
||||||
|
|
||||||
# MQTT Publisher Thread prüfen
|
# MQTT Publisher Thread prüfen
|
||||||
if self.mqtt and self.th_plcmqtt is not None \
|
if self.mqtt and self.th_plcmqtt is not None \
|
||||||
and not self.th_plcmqtt.is_alive():
|
and not self.th_plcmqtt.is_alive():
|
||||||
@@ -890,12 +912,14 @@ class RevPiPyLoad():
|
|||||||
dc["plcworkdir"] = self.plcworkdir
|
dc["plcworkdir"] = self.plcworkdir
|
||||||
dc["plcworkdir_set_uid"] = self.plcworkdir_set_uid
|
dc["plcworkdir_set_uid"] = self.plcworkdir_set_uid
|
||||||
dc["plcprogram"] = self.plcprogram
|
dc["plcprogram"] = self.plcprogram
|
||||||
|
dc["plcprogram_watchdog"] = self.plcprogram_watchdog
|
||||||
dc["plcarguments"] = self.plcarguments
|
dc["plcarguments"] = self.plcarguments
|
||||||
dc["plcuid"] = self.plcuid
|
dc["plcuid"] = self.plcuid
|
||||||
dc["plcgid"] = self.plcgid
|
dc["plcgid"] = self.plcgid
|
||||||
dc["pythonversion"] = self.pythonversion
|
dc["pythonversion"] = self.pythonversion
|
||||||
dc["replace_ios"] = self.replace_ios_config.replace(
|
dc["replace_ios"] = self.replace_ios_config.replace(
|
||||||
self.plcworkdir + "/", "")
|
self.plcworkdir + "/", "")
|
||||||
|
dc["reset_driver_action"] = self.reset_driver_action
|
||||||
dc["rtlevel"] = self.rtlevel
|
dc["rtlevel"] = self.rtlevel
|
||||||
dc["zeroonerror"] = int(self.zeroonerror)
|
dc["zeroonerror"] = int(self.zeroonerror)
|
||||||
dc["zeroonexit"] = int(self.zeroonexit)
|
dc["zeroonexit"] = int(self.zeroonexit)
|
||||||
@@ -1127,12 +1151,14 @@ class RevPiPyLoad():
|
|||||||
"autoreloaddelay": "[0-9]+",
|
"autoreloaddelay": "[0-9]+",
|
||||||
"autostart": "[01]",
|
"autostart": "[01]",
|
||||||
"plcprogram": ".+",
|
"plcprogram": ".+",
|
||||||
|
"plcprogram_watchdog": "[01]",
|
||||||
"plcarguments": ".*",
|
"plcarguments": ".*",
|
||||||
"plcworkdir_set_uid": "[01]",
|
"plcworkdir_set_uid": "[01]",
|
||||||
# "plcuid": "[0-9]{,5}",
|
# "plcuid": "[0-9]{,5}",
|
||||||
# "plcgid": "[0-9]{,5}",
|
# "plcgid": "[0-9]{,5}",
|
||||||
"pythonversion": "[23]",
|
"pythonversion": "[23]",
|
||||||
"replace_ios": ".*",
|
"replace_ios": ".*",
|
||||||
|
"reset_driver_action": "[0-2]",
|
||||||
"rtlevel": "[0-1]",
|
"rtlevel": "[0-1]",
|
||||||
"zeroonerror": "[01]",
|
"zeroonerror": "[01]",
|
||||||
"zeroonexit": "[01]",
|
"zeroonexit": "[01]",
|
||||||
@@ -1351,6 +1377,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
if proginit.pargs.test:
|
if proginit.pargs.test:
|
||||||
from testsystem import TestSystem
|
from testsystem import TestSystem
|
||||||
|
|
||||||
root = TestSystem()
|
root = TestSystem()
|
||||||
else:
|
else:
|
||||||
root = RevPiPyLoad()
|
root = RevPiPyLoad()
|
||||||
|
|||||||
75
revpipyload/watchdogs.py
Normal file
75
revpipyload/watchdogs.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Watchdog systems to monitor plc program and reset_driver of piCtory."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2020 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
import os
|
||||||
|
from fcntl import ioctl
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
import proginit as pi
|
||||||
|
|
||||||
|
|
||||||
|
class ResetDriverWatchdog(Thread):
|
||||||
|
"""Watchdog to catch a piCtory reset_driver action."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(ResetDriverWatchdog, self).__init__()
|
||||||
|
self.daemon = True
|
||||||
|
self._exit = False
|
||||||
|
self._fh = None
|
||||||
|
self._triggered = False
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
"""
|
||||||
|
Mainloop of watchdog for reset_driver.
|
||||||
|
|
||||||
|
If the thread can not open the process image or the IOCTL is not
|
||||||
|
implemented (wheezy), the thread function will stop. The trigger
|
||||||
|
property will always return True.
|
||||||
|
"""
|
||||||
|
pi.logger.debug("enter ResetDriverWatchdog.run()")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._fh = os.open(pi.pargs.procimg, os.O_RDONLY)
|
||||||
|
except Exception:
|
||||||
|
pi.logger.error(
|
||||||
|
"can not open process image at '{0}' for piCtory "
|
||||||
|
"reset_driver watchdog".format(pi.pargs.procimg)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# The ioctl will return 2 byte (c-type int)
|
||||||
|
byte_buff = bytearray(2)
|
||||||
|
while not self._exit:
|
||||||
|
try:
|
||||||
|
rc = ioctl(self._fh, 19250, byte_buff)
|
||||||
|
if rc == 0 and byte_buff[0] == 1:
|
||||||
|
self._triggered = True
|
||||||
|
pi.logger.debug("piCtory reset_driver detected")
|
||||||
|
except Exception:
|
||||||
|
os.close(self._fh)
|
||||||
|
self._fh = None
|
||||||
|
pi.logger.warning("IOCTL KB_WAIT_FOR_EVENT is not implemented")
|
||||||
|
return
|
||||||
|
|
||||||
|
pi.logger.debug("leave ResetDriverWatchdog.run()")
|
||||||
|
|
||||||
|
def stop(self) -> None:
|
||||||
|
"""Stop watchdog for piCtory reset_driver."""
|
||||||
|
pi.logger.debug("enter ResetDriverWatchdog.stop()")
|
||||||
|
|
||||||
|
self._exit = True
|
||||||
|
if self._fh is not None:
|
||||||
|
os.close(self._fh)
|
||||||
|
self._fh = None
|
||||||
|
|
||||||
|
pi.logger.debug("leave ResetDriverWatchdog.stop()")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def triggered(self) -> bool:
|
||||||
|
rc = self._triggered or not self.is_alive()
|
||||||
|
self._triggered = False
|
||||||
|
return rc
|
||||||
Reference in New Issue
Block a user