diff --git a/data/etc/revpipyload/revpipyload.conf b/data/etc/revpipyload/revpipyload.conf index 3f346fe..603af70 100644 --- a/data/etc/revpipyload/revpipyload.conf +++ b/data/etc/revpipyload/revpipyload.conf @@ -3,6 +3,7 @@ autoreload = 1 autoreloaddelay = 5 autostart = 1 plcworkdir = /var/lib/revpipyload +plcworkdir_set_uid = 0 plcprogram = program.py plcarguments = plcuid = 1000 diff --git a/doc/helper.html b/doc/helper.html index e023644..a58d308 100644 --- a/doc/helper.html +++ b/doc/helper.html @@ -31,9 +31,6 @@ Functions refullmatch re.fullmatch wegen alter python version aus wheezy nachgebaut. - -revpimodio_replaceio -Importiert und ersetzt IOs in RevPiModIO.

@@ -83,26 +80,5 @@ True, wenn komplett passt sonst False
Up
-

- -

revpimodio_replaceio

-revpimodio_replaceio(revpi, filename) -

-Importiert und ersetzt IOs in RevPiModIO. -

-
revpi
-
-RevPiModIO Instanz -
filename
-
-Dateiname der Ersetzungsdatei -
-
-
Returns:
-
-True, wenn alle IOs ersetzt werden konnten -
-
-
Up

\ No newline at end of file diff --git a/doc/picontrolserver.html b/doc/picontrolserver.html index f00c03f..3a6243e 100644 --- a/doc/picontrolserver.html +++ b/doc/picontrolserver.html @@ -12,7 +12,7 @@ Modul fuer die Verwaltung der PLC-Slave Funktionen.

Global Attributes

- +
__author__
__copyright__
__license__
HASH_FAIL
HASH_NULL
HASH_PICT
HASH_RPIO
__author__
__copyright__
__license__

Classes

@@ -66,6 +66,12 @@ Methods check_connectedacl Prueft bei neuen ACLs bestehende Verbindungen. +disconnect_all +Close all device connection. + +disconnect_replace_ios +Close all device with loaded replace_ios file. + newlogfile Konfiguriert die FileHandler auf neue Logdatei. @@ -104,6 +110,18 @@ RevPiSlave.check_connectedacl check_connectedacl()

Prueft bei neuen ACLs bestehende Verbindungen. +

+

+RevPiSlave.disconnect_all

+disconnect_all() +

+Close all device connection. +

+

+RevPiSlave.disconnect_replace_ios

+disconnect_replace_ios() +

+Close all device with loaded replace_ios file.

RevPiSlave.newlogfile

diff --git a/doc/procimgserver.html b/doc/procimgserver.html index c4b3138..850e8b5 100644 --- a/doc/procimgserver.html +++ b/doc/procimgserver.html @@ -63,6 +63,9 @@ Methods ProcimgServer Instantiiert RevPiCheckServer()-Klasse. +__del__ +Clean up RevPiModIO. + devices Generiert Deviceliste mit Position und Namen. @@ -104,7 +107,13 @@ XML-RPC Server
Replace IOs of RevPiModIO
- + +

+ProcimgServer.__del__

+__del__() +

+Clean up RevPiModIO. +

ProcimgServer.devices

devices() diff --git a/doc/proginit.html b/doc/proginit.html index 61d58df..c1901e6 100644 --- a/doc/proginit.html +++ b/doc/proginit.html @@ -12,7 +12,7 @@ Main functions of our program.

Global Attributes

- +
__author__
__copyright__
__license__
forked
globalconffile
logapp
logger
logplc
pargs
picontrolreset
rapcatalog
startdir
__author__
__copyright__
__license__
conf
forked
globalconffile
logapp
logger
logplc
pargs
picontrolreset
rapcatalog
startdir

Classes

diff --git a/doc/revpipyload.html b/doc/revpipyload.html index 4ec59aa..e64899e 100644 --- a/doc/revpipyload.html +++ b/doc/revpipyload.html @@ -110,6 +110,12 @@ Methods _signewlogfile Signal handler to start new logfile. +check_pictory_changed +Prueft ob sich die piCtory Datei veraendert hat. + +check_replace_ios_changed +Prueft ob sich die replace_ios.conf Datei veraendert hat (oder del). + packapp Erzeugt aus dem PLC-Programm ein TAR/Zip-File. @@ -299,7 +305,29 @@ RevPiPyLoad._signewlogfile _signewlogfile(signum, frame)

Signal handler to start new logfile. -

+

+

+RevPiPyLoad.check_pictory_changed

+check_pictory_changed() +

+Prueft ob sich die piCtory Datei veraendert hat. +

+
Returns:
+
+True, wenn veraendert wurde +
+
+

+RevPiPyLoad.check_replace_ios_changed

+check_replace_ios_changed() +

+Prueft ob sich die replace_ios.conf Datei veraendert hat (oder del). +

+
Returns:
+
+True, wenn veraendert wurde +
+

RevPiPyLoad.packapp

packapp(mode="tar", pictory=False) diff --git a/eric-revpipyload.api b/eric-revpipyload.api index 813f47c..b9fa29e 100644 --- a/eric-revpipyload.api +++ b/eric-revpipyload.api @@ -4,7 +4,6 @@ helper.__license__?9 helper._setuprt?5(pid, evt_exit) helper._zeroprocimg?5() helper.refullmatch?4(regex, string) -helper.revpimodio_replaceio?4(revpi, filename) logsystem.LogReader.closeall?4() logsystem.LogReader.load_applog?4(start, count) logsystem.LogReader.load_plclog?4(start, count) @@ -33,7 +32,13 @@ mqttserver.MqttServer?1(basetopic, sendinterval, broker_address, port=1883, tls_ mqttserver.__author__?9 mqttserver.__copyright__?9 mqttserver.__license__?9 +picontrolserver.HASH_FAIL?7 +picontrolserver.HASH_NULL?7 +picontrolserver.HASH_PICT?7 +picontrolserver.HASH_RPIO?7 picontrolserver.RevPiSlave.check_connectedacl?4() +picontrolserver.RevPiSlave.disconnect_all?4() +picontrolserver.RevPiSlave.disconnect_replace_ios?4() picontrolserver.RevPiSlave.newlogfile?4() picontrolserver.RevPiSlave.run?4() picontrolserver.RevPiSlave.stop?4() @@ -57,6 +62,7 @@ plcsystem.RevPiPlc?1(program, arguments, pversion) plcsystem.__author__?9 plcsystem.__copyright__?9 plcsystem.__license__?9 +procimgserver.ProcimgServer.__del__?6() procimgserver.ProcimgServer.devices?4() procimgserver.ProcimgServer.ios?4(iotype) procimgserver.ProcimgServer.loadrevpimodio?4() @@ -72,6 +78,7 @@ proginit.__author__?9 proginit.__copyright__?9 proginit.__license__?9 proginit.cleanup?4() +proginit.conf?7 proginit.configure?4() proginit.forked?7 proginit.globalconffile?7 @@ -92,6 +99,8 @@ revpipyload.RevPiPyLoad._plcthread?5() revpipyload.RevPiPyLoad._sigexit?5(signum, frame) revpipyload.RevPiPyLoad._sigloadconfig?5(signum, frame) revpipyload.RevPiPyLoad._signewlogfile?5(signum, frame) +revpipyload.RevPiPyLoad.check_pictory_changed?4() +revpipyload.RevPiPyLoad.check_replace_ios_changed?4() revpipyload.RevPiPyLoad.packapp?4(mode="tar", pictory=False) revpipyload.RevPiPyLoad.start?4() revpipyload.RevPiPyLoad.stop?4() diff --git a/revpipyload.e4p b/revpipyload.e4p index c4f6847..6ed2e35 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.7.6 + 0.8.0 Sven Sager akira@narux.de diff --git a/revpipyload/helper.py b/revpipyload/helper.py index 34c8f81..915f546 100644 --- a/revpipyload/helper.py +++ b/revpipyload/helper.py @@ -5,7 +5,6 @@ __copyright__ = "Copyright (C) 2018 Sven Sager" __license__ = "GPLv3" import os import proginit -from configparser import ConfigParser from re import match as rematch from subprocess import Popen, PIPE @@ -118,115 +117,6 @@ def _zeroprocimg(): ) -def revpimodio_replaceio(revpi, filename): - """Importiert und ersetzt IOs in RevPiModIO. - - @param revpi RevPiModIO Instanz - @param filename Dateiname der Ersetzungsdatei - @return True, wenn alle IOs ersetzt werden konnten - - """ - cp = ConfigParser() - try: - with open(filename, "r") as fh: - cp.read_file(fh) - except Exception as e: - proginit.logger.error( - "could not read replace_io file '{0}' | {1}".format(filename, e) - ) - return False - - # Pre-check - lst_replace = [] - rc = True - for io in cp: - if io == "DEFAULT": - continue - - dict_replace = { - "replace": cp[io].get("replace", ""), - "frm": cp[io].get("frm"), - "bmk": cp[io].get("bmk", ""), - "byteorder": cp[io].get("byteorder", "little"), - } - - if dict_replace["replace"] in revpi.io: - - # Byteorder prüfen - if not (dict_replace["byteorder"] == "little" or - dict_replace["byteorder"] == "big"): - proginit.logger.error( - "byteorder of '{0}' must be 'little' or 'big'".format(io) - ) - rc = False - continue - - if dict_replace["frm"] == "?": - - # Convert defaultvalue from config file - try: - dict_replace["default"] = cp[io].getboolean("defaultvalue") - except Exception: - proginit.logger.error( - "could not convert '{0}' defaultvalue '{1}' to boolean" - "".format(io, cp[io].get("defaultvalue")) - ) - rc = False - continue - - # Get bitaddress - try: - dict_replace["bitaddress"] = cp[io].getint("bitaddress", 0) - except Exception: - proginit.logger.error( - "could not convert '{0}' bitaddress '{1}' to integer" - "".format(io, cp[io].get("bitaddress")) - ) - rc = False - continue - - else: - # Convert defaultvalue from config file - try: - dict_replace["default"] = cp[io].getint("defaultvalue") - except Exception: - proginit.logger.error( - "could not convert '{0}' defaultvalue '{1}' to integer" - "".format(io, cp[io].get("defaultvalue")) - ) - rc = False - continue - - else: - proginit.logger.error( - "can not find io '{0}' to replace with '{1}'" - "".format(dict_replace["replace"], io) - ) - rc = False - continue - - # Replace_IO übernehmen - lst_replace.append(dict_replace) - - if not rc: - # Abbrechen, wenn IO-Verarbeitung einen Fehler hatte - return False - - # Replace IOs - for dict_replace in lst_replace: - - # FIXME: Hier können Fehler auftreten !!! - - revpi.io[dict_replace["replace"]].replace_io( - io, - frm=dict_replace["frm"], - bmk=dict_replace["bmk"], - bit=dict_replace["bitaddress"], - byteorder=dict_replace["byteorder"], - defaultvalue=dict_replace["default"] - ) - - def refullmatch(regex, string): """re.fullmatch wegen alter python version aus wheezy nachgebaut. diff --git a/revpipyload/mqttserver.py b/revpipyload/mqttserver.py index f4aa9dc..2a0cd12 100644 --- a/revpipyload/mqttserver.py +++ b/revpipyload/mqttserver.py @@ -74,7 +74,6 @@ class MqttServer(Thread): self._reloadmodio = False self._replace_ios = replace_ios self._rpi = None - self._rpi_write = None self._send_events = send_events self._sendinterval = sendinterval self._write_outputs = write_outputs @@ -130,26 +129,18 @@ class MqttServer(Thread): # RevPiModIO-Modul Instantiieren if self._rpi is not None: self._rpi.cleanup() - if self._rpi_write is not None: - self._rpi_write.cleanup() proginit.logger.debug("create revpimodio2 object for MQTT") try: - # Lesend und Eventüberwachung + # Vollzugriff und Eventüberwachung self._rpi = revpimodio2.RevPiModIO( autorefresh=self._send_events, - monitoring=True, + monitoring=not self._write_outputs, configrsc=proginit.pargs.configrsc, procimg=proginit.pargs.procimg, - replace_io_file=self._replace_ios + replace_io_file=self._replace_ios, + direct_output=True, ) - # Schreibenen Zugriff - if self._write_outputs: - self._rpi_write = revpimodio2.RevPiModIO( - configrsc=proginit.pargs.configrsc, - procimg=proginit.pargs.procimg, - replace_io_file=self._replace_ios - ) if self._replace_ios: proginit.logger.info("loaded replace_ios to MQTT") @@ -159,16 +150,11 @@ class MqttServer(Thread): # Lesend und Eventüberwachung self._rpi = revpimodio2.RevPiModIO( autorefresh=self._send_events, - monitoring=True, + monitoring=not self._write_outputs, configrsc=proginit.pargs.configrsc, - procimg=proginit.pargs.procimg + procimg=proginit.pargs.procimg, + direct_output=True, ) - # Schreibenen Zugriff - if self._write_outputs: - self._rpi_write = revpimodio2.RevPiModIO( - configrsc=proginit.pargs.configrsc, - procimg=proginit.pargs.procimg - ) proginit.logger.warning( "replace_ios_file not loadable for MQTT - using " "defaults now | {0}".format(e) @@ -176,7 +162,6 @@ class MqttServer(Thread): except Exception: self._rpi = None - self._rpi_write = None proginit.logger.error( "piCtory configuration not loadable for MQTT" ) @@ -280,11 +265,11 @@ class MqttServer(Thread): # IO holen if coreio: coreio = ioname.split(".")[-1] - io = getattr(self._rpi_write.core, coreio) + io = getattr(self._rpi.core, coreio) if not isinstance(io, revpimodio2.io.IOBase): raise RuntimeError() else: - io = self._rpi_write.io[ioname] + io = self._rpi.io[ioname] io_needbytes = type(io.value) == bytes except Exception: proginit.logger.error( @@ -300,10 +285,8 @@ class MqttServer(Thread): ) elif ioget: - # Daten je nach IO Type aus Prozessabbild laden - if io.type == revpimodio2.OUT: - io._parentdevice.syncoutputs() - else: + # Werte laden, wenn nicht autorefresh + if not self._send_events: io._parentdevice.readprocimg() # Publish Wert von IO @@ -344,7 +327,6 @@ class MqttServer(Thread): return # Write Value to RevPi - io._parentdevice.syncoutputs() try: io.value = value except Exception: @@ -352,8 +334,6 @@ class MqttServer(Thread): "could not write '{0}' to Output '{1}'" "".format(value, ioname) ) - else: - io._parentdevice.writeprocimg() elif ioreset: # Counter zurücksetzen diff --git a/revpipyload/picontrolserver.py b/revpipyload/picontrolserver.py index 8105ced..4283b2b 100644 --- a/revpipyload/picontrolserver.py +++ b/revpipyload/picontrolserver.py @@ -10,8 +10,13 @@ from shared.ipaclmanager import IpAclManager from threading import Event, Thread from timeit import default_timer +# Hashvalues +HASH_NULL = b'\x00' * 16 +HASH_FAIL = b'\xff' * 16 +HASH_PICT = HASH_FAIL +HASH_RPIO = HASH_NULL + -# NOTE: Sollte dies als Process ausgeführt werden? class RevPiSlave(Thread): """RevPi PLC-Server. @@ -70,6 +75,19 @@ class RevPiSlave(Thread): ) dev._acl = level + def disconnect_all(self): + """Close all device connection.""" + # Alle Threads beenden + for th in self._th_dev: + th.stop() + + def disconnect_replace_ios(self): + """Close all device with loaded replace_ios file.""" + # Alle Threads beenden die Replace_IOs emfpangen haben + for th in self._th_dev: + if th.got_replace_ios: + th.stop() + def newlogfile(self): """Konfiguriert die FileHandler auf neue Logdatei.""" pass @@ -80,6 +98,7 @@ class RevPiSlave(Thread): # Socket öffnen und konfigurieren bis Erfolg oder Ende self.so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.so.settimeout(2) while not self._evt_exit.is_set(): try: self.so.bind((self._bindip, self._port)) @@ -97,9 +116,11 @@ class RevPiSlave(Thread): self.exitcode = -1 # Verbindung annehmen - proginit.logger.info("accept new connection for revpinetio") try: tup_sock = self.so.accept() + proginit.logger.info("accepted new connection for revpinetio") + except socket.timeout: + continue except Exception: if not self._evt_exit.is_set(): proginit.logger.exception("accept exception") @@ -174,6 +195,7 @@ class RevPiSlaveDev(Thread): self._deadtime = None self._devcon, self._addr = devcon self._evt_exit = Event() + self.got_replace_ios = False self._writeerror = False # Sicherheitsbytes @@ -381,10 +403,47 @@ class RevPiSlaveDev(Thread): ) break else: - continue - finally: # End-of-Transmission character immer senden self._devcon.send(b'\x04') + continue + + elif cmd == b'PH': + # piCtory md5 Hashwert senden (16 Byte) + proginit.logger.debug( + "send pictory hashvalue: {0}".format(HASH_PICT) + ) + self._devcon.sendall(HASH_PICT) + + elif cmd == b'RP': + # Replace_IOs Konfiguration senden, wenn hash existiert + proginit.logger.debug( + "transfair replace_io configuration: {0}" + "".format(proginit.pargs.configrsc) + ) + replace_ios = proginit.conf["DEFAULT"].get("replace_ios", "") + try: + if HASH_RPIO != HASH_NULL and replace_ios: + with open(replace_ios, "rb") as fh: + # Komplette replace_io Datei senden + self._devcon.sendall(fh.read()) + except Exception as e: + proginit.logger.error( + "error on replace_io transfair: {0}".format(e) + ) + break + else: + # End-of-Transmission character immer senden + self._devcon.send(b'\x04') + continue + + elif cmd == b'RH': + # Replace_IOs md5 Hashwert senden (16 Byte) + self.got_replace_ios = True + + proginit.logger.debug( + "send replace_ios hashvalue: {0}".format(HASH_RPIO) + ) + self._devcon.sendall(HASH_RPIO) elif cmd == b'EX': # Sauber Verbindung verlassen @@ -416,6 +475,7 @@ class RevPiSlaveDev(Thread): ) else: # Simulation + # TODO: IOCTL für Dateien implementieren proginit.logger.warning( "ioctl {0} with {1} simulated".format(request, arg) ) diff --git a/revpipyload/procimgserver.py b/revpipyload/procimgserver.py index b6fbc45..ed1be3a 100644 --- a/revpipyload/procimgserver.py +++ b/revpipyload/procimgserver.py @@ -54,6 +54,11 @@ class ProcimgServer(): proginit.logger.debug("leave ProcimgServer.__init__()") + def __del__(self): + """Clean up RevPiModIO.""" + if self.rpi is not None: + self.rpi.cleanup() + def devices(self): """Generiert Deviceliste mit Position und Namen. @return list() mit Tuple (pos, name)""" @@ -99,7 +104,8 @@ class ProcimgServer(): self.rpi = revpimodio2.RevPiModIO( configrsc=proginit.pargs.configrsc, procimg=proginit.pargs.procimg, - replace_io_file=self.replace_ios + replace_io_file=self.replace_ios, + direct_output=True, ) if self.replace_ios: @@ -110,6 +116,7 @@ class ProcimgServer(): self.rpi = revpimodio2.RevPiModIO( configrsc=proginit.pargs.configrsc, procimg=proginit.pargs.procimg, + direct_output=True, ) proginit.logger.warning( "replace_ios_file not loadable for ProcimgServer - using " @@ -122,9 +129,6 @@ class ProcimgServer(): ) return e - # NOTE: Warum das? - self.rpi.syncoutputs(device=0) - proginit.logger.debug("created revpimodio2 object") def setvalue(self, device, io, value): @@ -140,8 +144,6 @@ class ProcimgServer(): if type(value) == Binary: value = value.data - self.rpi.syncoutputs(device=device) - try: # Neuen Wert übernehmen if type(value) == bytes or type(value) == bool: @@ -156,13 +158,12 @@ class ProcimgServer(): except Exception as e: return [device, io, False, str(e)] - self.rpi.writeprocimg(device=device) return [device, io, True, ""] def values(self): """Liefert Prozessabbild an Client. @return Binary() bytes or None""" - if self.rpi.readprocimg() and self.rpi.syncoutputs(): + if self.rpi.readprocimg(): bytebuff = bytearray() for dev in self.rpi.device: bytebuff += bytes(dev) diff --git a/revpipyload/proginit.py b/revpipyload/proginit.py index a0bf0fd..63dddf3 100644 --- a/revpipyload/proginit.py +++ b/revpipyload/proginit.py @@ -7,7 +7,9 @@ import logging import os import sys from argparse import ArgumentParser +from configparser import ConfigParser +conf = ConfigParser() forked = False globalconffile = None logapp = "revpipyloadapp.log" diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py index d0504aa..c2ee5fc 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -28,7 +28,7 @@ begrenzt werden! __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2018 Sven Sager" __license__ = "GPLv3" -__version__ = "0.7.6" +__version__ = "0.8.0" import gzip import logsystem import picontrolserver @@ -39,6 +39,7 @@ 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 @@ -49,7 +50,7 @@ from time import asctime from xmlrpc.client import Binary from xrpcserver import SaveXMLRPCServer -min_revpimodio = "2.3.3" +min_revpimodio = "2.4.1" class RevPiPyLoad(): @@ -67,14 +68,18 @@ class RevPiPyLoad(): # Klassenattribute self._exit = True - self.pictorymtime = os.path.getmtime(proginit.pargs.configrsc) - self.replaceiosmtime = 0 self.evt_loadconfig = Event() self.globalconfig = ConfigParser() + proginit.conf = self.globalconfig self.logr = logsystem.LogReader() self.xsrv = None self.xml_ps = None + # Dateimerker + self.pictorymtime = 0 + self.replaceiosmtime = 0 + self.replaceiofail = False + # Berechtigungsmanger if proginit.pargs.developermode: self.plcslaveacl = IpAclManager(minlevel=0, maxlevel=9) @@ -194,6 +199,7 @@ class RevPiPyLoad(): "loading config file: {0}".format(proginit.globalconffile) ) self.globalconfig.read(proginit.globalconffile) + proginit.conf = self.globalconfig # Merker für Subsystem-Neustart nach laden, vor setzen restart_plcmqtt = self._check_mustrestart_mqtt() @@ -213,6 +219,8 @@ class RevPiPyLoad(): self.globalconfig["DEFAULT"].get("plcprogram", "none.py") 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 = \ @@ -228,23 +236,17 @@ class RevPiPyLoad(): self.zeroonexit = \ self.globalconfig["DEFAULT"].getboolean("zeroonexit", True) - # MTime für replace io übernehmen - mtime = 0 - if self.replace_ios_config: - if os.access(self.replace_ios_config, os.R_OK | os.W_OK): - mtime = os.path.getmtime(self.replace_ios_config) - else: - proginit.logger.error( - "can not access (r/w) the replace_ios file '{0}' " - "using defaults".format(self.replace_ios_config) - ) - self.replace_ios_config = "" - - if self.replaceiosmtime != mtime: - # MQTT reload erforderlich + # Dateiveränderungen prüfen + file_changed = False + # Beide Funktionen müssen einmal aufgerufen werden + if self.check_pictory_changed(): + file_changed = True + if self.check_replace_ios_changed(): + file_changed = True + if file_changed: restart_plcmqtt = True - - self.replaceiosmtime = mtime + restart_plcslave = True + restart_plcprogram = True # Konfiguration verarbeiten [MQTT] self.mqtt = 0 @@ -328,6 +330,10 @@ class RevPiPyLoad(): ) os.chdir(self.plcworkdir) + # Workdirectory owner setzen + if self.plcworkdir_set_uid: + os.chown(self.plcworkdir, self.plcuid, -1) + # MQTT konfigurieren if restart_plcmqtt: self.stop_plcmqtt() @@ -419,17 +425,18 @@ class RevPiPyLoad(): "revpimodio2: 'apt-get install python3-revpimodio2'" "".format(min_revpimodio) ) - try: - self.xml_ps = procimgserver.ProcimgServer( - self.xsrv, - None if not self.replace_ios_config - else self.replace_ios_config, - ) - self.xsrv.register_function(1, self.xml_psstart, "psstart") - self.xsrv.register_function(1, self.xml_psstop, "psstop") - except Exception as e: - self.xml_ps = None - proginit.logger.error(e) + else: + try: + self.xml_ps = procimgserver.ProcimgServer( + self.xsrv, + None if not self.replace_ios_config + else self.replace_ios_config, + ) + self.xsrv.register_function(1, self.xml_psstart, "psstart") + self.xsrv.register_function(1, self.xml_psstop, "psstop") + except Exception as e: + self.xml_ps = None + proginit.logger.error(e) # XML Modus 2 Einstellungen lesen und Programm herunterladen self.xsrv.register_function( @@ -592,6 +599,67 @@ class RevPiPyLoad(): proginit.logger.debug("leave RevPiPyLoad._signewlogfile()") + def check_pictory_changed(self): + """Prueft ob sich die piCtory Datei veraendert hat. + @return True, wenn veraendert wurde""" + mtime = os.path.getmtime(proginit.pargs.configrsc) + if self.pictorymtime == mtime: + return False + self.pictorymtime = mtime + + # TODO: Nur "Devices" list vergleich + + with open(proginit.pargs.configrsc, "rb") as fh: + file_hash = md5(fh.read()).digest() + if picontrolserver.HASH_PICT == file_hash: + return False + picontrolserver.HASH_PICT = file_hash + + return True + + def check_replace_ios_changed(self): + """Prueft ob sich die replace_ios.conf Datei veraendert hat (oder del). + @return True, wenn veraendert wurde""" + + # Zugriffsrechte prüfen (pre-check für unten) + if self.replace_ios_config \ + and not os.access(self.replace_ios_config, os.R_OK): + + if not self.replaceiofail: + proginit.logger.error( + "can not access (r/w) the replace_ios file '{0}' " + "using defaults".format(self.replace_ios_config) + ) + self.replaceiofail = True + else: + self.replaceiofail = False + + if not self.replace_ios_config or self.replaceiofail: + # Dateipfad leer, prüfen ob es vorher einen gab + if self.replaceiosmtime > 0 \ + or picontrolserver.HASH_RPIO != picontrolserver.HASH_NULL: + self.replaceiosmtime = 0 + picontrolserver.HASH_RPIO = picontrolserver.HASH_NULL + return True + + else: + mtime = os.path.getmtime(self.replace_ios_config) + if self.replaceiosmtime == mtime: + return False + self.replaceiosmtime = mtime + + # TODO: Instanz von ConfigParser vergleichen + + with open(self.replace_ios_config, "rb") as fh: + file_hash = md5(fh.read()).digest() + if picontrolserver.HASH_RPIO == file_hash: + return False + picontrolserver.HASH_RPIO = file_hash + + return True + + return False + def packapp(self, mode="tar", pictory=False): """Erzeugt aus dem PLC-Programm ein TAR/Zip-File. @@ -668,12 +736,43 @@ class RevPiPyLoad(): # 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() + # Dateiveränderungen prüfen mit beiden Funktionen! + if self.check_pictory_changed(): + file_changed = True + + # Alle Verbindungen von ProcImgServer trennen + self.th_plcslave.disconnect_all() + + proginit.logger.warning("piCtory configuration was changed") + + if self.check_replace_ios_changed(): + if not file_changed: + # Verbindungen von ProcImgServer trennen mit replace_ios + self.th_plcslave.disconnect_replace_ios() + + file_changed = True + proginit.logger.warning("replace ios file was changed") + + if file_changed: + # Auf Dateiveränderung reagieren + + # MQTT Publisher neu laden + if self.mqtt and self.th_plcmqtt is not None: + self.th_plcmqtt.reload_revpimodio() + + # XML Prozessabbildserver neu laden + if self.xml_ps is not None: + self.xml_psstop() + self.xml_ps.loadrevpimodio() + # Kein psstart um Reload im Client zu erzeugen + # MQTT Publisher Thread prüfen if self.mqtt and self.th_plcmqtt is not None \ and not self.th_plcmqtt.is_alive(): @@ -687,27 +786,14 @@ class RevPiPyLoad(): # PLC Server Thread prüfen if self.plcslave and self.th_plcslave is not None \ and not self.th_plcslave.is_alive(): - proginit.logger.warning( - "restart plc slave after thread was not running" - ) + if not file_changed: + proginit.logger.warning( + "restart plc slave after thread was not running" + ) self.th_plcslave = self._plcslave() if self.th_plcslave is not None: self.th_plcslave.start() - # piCtory auf Veränderung prüfen - if self.pictorymtime != os.path.getmtime(proginit.pargs.configrsc): - proginit.logger.warning("piCtory configuration was changed") - self.pictorymtime = os.path.getmtime(proginit.pargs.configrsc) - - # MQTT Publisher neu laden - if self.mqtt and self.th_plcmqtt is not None: - self.th_plcmqtt.reload_revpimodio() - - # XML Prozessabbildserver neu laden - if self.xml_ps is not None: - self.xml_psstop() - self.xml_ps.loadrevpimodio() - self.evt_loadconfig.wait(1) proginit.logger.info("stopping revpipyload") @@ -1082,6 +1168,7 @@ class RevPiPyLoad(): # conf-Datei schreiben with open(proginit.globalconffile, "w") as fh: self.globalconfig.write(fh) + proginit.conf = self.globalconfig proginit.logger.info( "got new config and wrote it to {0}" "".format(proginit.globalconffile) diff --git a/revpipyload/testsystem.py b/revpipyload/testsystem.py index 5ef5a1f..abb23f2 100644 --- a/revpipyload/testsystem.py +++ b/revpipyload/testsystem.py @@ -41,7 +41,7 @@ class TestSystem: rpi = revpimodio2.RevPiModIO( configrsc=proginit.pargs.configrsc, procimg=proginit.pargs.procimg, - monitoring=False, + monitoring=True, debug=True, replace_io_file=file, ) diff --git a/setup.py b/setup.py index 64dd099..304d814 100644 --- a/setup.py +++ b/setup.py @@ -27,11 +27,11 @@ setup( license="LGPLv3", name="revpipyload", - version="0.7.6", + version="0.8.0", scripts=["data/revpipyload"], - install_requires=["revpimodio2 >= 2.3.3"], + install_requires=["revpimodio2 >= 2.4.1"], python_requires=">=3.2", data_files=[ diff --git a/stdeb.cfg b/stdeb.cfg index 5cbcf63..1cb55bd 100644 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -1,5 +1,5 @@ [DEFAULT] Debian-Version: 1 -Depends3: python3-revpimodio2 (>= 2.2.5) +Depends3: python3-revpimodio2 (>= 2.4.1) Package: revpipyload Suite: stable