mirror of
https://github.com/naruxde/revpipyload.git
synced 2025-11-08 15:13: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_set_uid = 0
|
||||
plcprogram = program.py
|
||||
plcprogram_watchdog = 0
|
||||
plcarguments =
|
||||
plcuid = 1000
|
||||
plcgid = 1000
|
||||
pythonversion = 3
|
||||
replace_ios = /etc/revpipyload/replace_ios.conf
|
||||
reset_driver_action = 2
|
||||
rtlevel = 0
|
||||
zeroonerror = 0
|
||||
zeroonexit = 0
|
||||
|
||||
@@ -29,32 +29,34 @@ __author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "0.8.5"
|
||||
|
||||
import gzip
|
||||
import logsystem
|
||||
import picontrolserver
|
||||
import plcsystem
|
||||
import proginit
|
||||
import os
|
||||
import signal
|
||||
import tarfile
|
||||
import zipfile
|
||||
from configparser import ConfigParser
|
||||
from hashlib import md5
|
||||
from helper import refullmatch
|
||||
from json import loads as jloads
|
||||
from shared.ipaclmanager import IpAclManager
|
||||
from shutil import rmtree
|
||||
from tempfile import mkstemp
|
||||
from threading import Event
|
||||
from time import asctime
|
||||
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
|
||||
|
||||
min_revpimodio = "2.4.5"
|
||||
|
||||
|
||||
class RevPiPyLoad():
|
||||
|
||||
"""Hauptklasse, die alle Funktionen zur Verfuegung stellt.
|
||||
|
||||
Hier wird die gesamte Konfiguraiton eingelesen und der ggf. aktivierte
|
||||
@@ -207,34 +209,38 @@ class RevPiPyLoad():
|
||||
restart_plcprogram = self._check_mustrestart_plcprogram()
|
||||
|
||||
# Konfiguration verarbeiten [DEFAULT]
|
||||
self.autoreload = \
|
||||
self.globalconfig["DEFAULT"].getboolean("autoreload", True)
|
||||
self.autoreloaddelay = \
|
||||
self.globalconfig["DEFAULT"].getint("autoreloaddelay", 5)
|
||||
self.autostart = \
|
||||
self.globalconfig["DEFAULT"].getboolean("autostart", False)
|
||||
self.plcworkdir = \
|
||||
self.globalconfig["DEFAULT"].get("plcworkdir", ".")
|
||||
self.plcprogram = \
|
||||
self.globalconfig["DEFAULT"].get("plcprogram", "none.py")
|
||||
self.plcarguments = \
|
||||
self.globalconfig["DEFAULT"].get("plcarguments", "")
|
||||
self.autoreload = self.globalconfig["DEFAULT"].getboolean(
|
||||
"autoreload", True)
|
||||
self.autoreloaddelay = self.globalconfig["DEFAULT"].getint(
|
||||
"autoreloaddelay", 5)
|
||||
self.autostart = self.globalconfig["DEFAULT"].getboolean(
|
||||
"autostart", False)
|
||||
self.plcworkdir = self.globalconfig["DEFAULT"].get(
|
||||
"plcworkdir", ".")
|
||||
self.plcprogram = self.globalconfig["DEFAULT"].get(
|
||||
"plcprogram", "none.py")
|
||||
self.plcprogram_watchdog = self.globalconfig["DEFAULT"].getboolean(
|
||||
"plcprogram_watchdog", False)
|
||||
self.plcarguments = self.globalconfig["DEFAULT"].get(
|
||||
"plcarguments", "")
|
||||
self.plcworkdir_set_uid = self.globalconfig["DEFAULT"].getboolean(
|
||||
"plcworkdir_set_uid", False)
|
||||
self.plcuid = \
|
||||
self.globalconfig["DEFAULT"].getint("plcuid", 65534)
|
||||
self.plcgid = \
|
||||
self.globalconfig["DEFAULT"].getint("plcgid", 65534)
|
||||
self.pythonversion = \
|
||||
self.globalconfig["DEFAULT"].getint("pythonversion", 3)
|
||||
self.replace_ios_config = \
|
||||
self.globalconfig["DEFAULT"].get("replace_ios", "")
|
||||
self.rtlevel = \
|
||||
self.globalconfig["DEFAULT"].getint("rtlevel", 0)
|
||||
self.zeroonerror = \
|
||||
self.globalconfig["DEFAULT"].getboolean("zeroonerror", True)
|
||||
self.zeroonexit = \
|
||||
self.globalconfig["DEFAULT"].getboolean("zeroonexit", True)
|
||||
self.plcuid = self.globalconfig["DEFAULT"].getint(
|
||||
"plcuid", 65534)
|
||||
self.plcgid = self.globalconfig["DEFAULT"].getint(
|
||||
"plcgid", 65534)
|
||||
self.pythonversion = self.globalconfig["DEFAULT"].getint(
|
||||
"pythonversion", 3)
|
||||
self.replace_ios_config = self.globalconfig["DEFAULT"].get(
|
||||
"replace_ios", "")
|
||||
self.rtlevel = self.globalconfig["DEFAULT"].getint(
|
||||
"rtlevel", 0)
|
||||
self.reset_driver_action = self.globalconfig["DEFAULT"].getint(
|
||||
"reset_driver_action", 2)
|
||||
self.zeroonerror = self.globalconfig["DEFAULT"].getboolean(
|
||||
"zeroonerror", True)
|
||||
self.zeroonexit = self.globalconfig["DEFAULT"].getboolean(
|
||||
"zeroonexit", True)
|
||||
|
||||
# Dateiveränderungen prüfen
|
||||
file_changed = False
|
||||
@@ -748,17 +754,21 @@ class RevPiPyLoad():
|
||||
if self.autostart and self.plc is not None:
|
||||
self.plc.start()
|
||||
|
||||
# Watchdog to detect the reset_driver event
|
||||
pictory_reset_driver = ResetDriverWatchdog()
|
||||
|
||||
# mainloop
|
||||
while not self._exit:
|
||||
file_changed = False
|
||||
|
||||
# Neue Konfiguration laden
|
||||
if self.evt_loadconfig.is_set():
|
||||
proginit.logger.info("got reqeust to reload config")
|
||||
self._loadconfig()
|
||||
|
||||
file_changed = False
|
||||
reset_driver_detected = pictory_reset_driver.triggered
|
||||
|
||||
# Dateiveränderungen prüfen mit beiden Funktionen!
|
||||
if self.check_pictory_changed():
|
||||
if reset_driver_detected and self.check_pictory_changed():
|
||||
file_changed = True
|
||||
|
||||
# Alle Verbindungen von ProcImgServer trennen
|
||||
@@ -789,6 +799,18 @@ class RevPiPyLoad():
|
||||
self.xml_ps.loadrevpimodio()
|
||||
# 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
|
||||
if self.mqtt and self.th_plcmqtt is not None \
|
||||
and not self.th_plcmqtt.is_alive():
|
||||
@@ -890,12 +912,14 @@ class RevPiPyLoad():
|
||||
dc["plcworkdir"] = self.plcworkdir
|
||||
dc["plcworkdir_set_uid"] = self.plcworkdir_set_uid
|
||||
dc["plcprogram"] = self.plcprogram
|
||||
dc["plcprogram_watchdog"] = self.plcprogram_watchdog
|
||||
dc["plcarguments"] = self.plcarguments
|
||||
dc["plcuid"] = self.plcuid
|
||||
dc["plcgid"] = self.plcgid
|
||||
dc["pythonversion"] = self.pythonversion
|
||||
dc["replace_ios"] = self.replace_ios_config.replace(
|
||||
self.plcworkdir + "/", "")
|
||||
dc["reset_driver_action"] = self.reset_driver_action
|
||||
dc["rtlevel"] = self.rtlevel
|
||||
dc["zeroonerror"] = int(self.zeroonerror)
|
||||
dc["zeroonexit"] = int(self.zeroonexit)
|
||||
@@ -1127,12 +1151,14 @@ class RevPiPyLoad():
|
||||
"autoreloaddelay": "[0-9]+",
|
||||
"autostart": "[01]",
|
||||
"plcprogram": ".+",
|
||||
"plcprogram_watchdog": "[01]",
|
||||
"plcarguments": ".*",
|
||||
"plcworkdir_set_uid": "[01]",
|
||||
# "plcuid": "[0-9]{,5}",
|
||||
# "plcgid": "[0-9]{,5}",
|
||||
"pythonversion": "[23]",
|
||||
"replace_ios": ".*",
|
||||
"reset_driver_action": "[0-2]",
|
||||
"rtlevel": "[0-1]",
|
||||
"zeroonerror": "[01]",
|
||||
"zeroonexit": "[01]",
|
||||
@@ -1351,6 +1377,7 @@ if __name__ == "__main__":
|
||||
|
||||
if proginit.pargs.test:
|
||||
from testsystem import TestSystem
|
||||
|
||||
root = TestSystem()
|
||||
else:
|
||||
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