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/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 fa7112a..a3f9d72 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) @@ -93,6 +92,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/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/picontrolserver.py b/revpipyload/picontrolserver.py index cb3501d..9bb4ac2 100644 --- a/revpipyload/picontrolserver.py +++ b/revpipyload/picontrolserver.py @@ -6,6 +6,7 @@ __license__ = "GPLv3" import proginit import socket from fcntl import ioctl +from hashlib import md5 from shared.ipaclmanager import IpAclManager from threading import Event, Thread from timeit import default_timer @@ -385,6 +386,25 @@ class RevPiSlaveDev(Thread): # End-of-Transmission character immer senden self._devcon.send(b'\x04') + elif cmd == b'PH': + # piCtory md5 Hashwert senden (32 Byte) + try: + with open(proginit.pargs.configrsc, "rb") as fh_pic: + # Hashwert erzeugen und senden + file_hash = md5(fh_pic.read()) + proginit.logger.debug( + "send pictory hashvalue: {0}" + "".format(file_hash) + ) + self._devcon.sendall(file_hash) + except Exception as e: + proginit.logger.error( + "error on pictory hash value transfair: {0}".format(e) + ) + break + else: + continue + elif cmd == b'RP': # piCtory Konfiguration senden proginit.logger.debug( @@ -408,6 +428,27 @@ class RevPiSlaveDev(Thread): # End-of-Transmission character immer senden self._devcon.send(b'\x04') + elif cmd == b'RH': + # Replace_IOs md5 Hashwert senden (32 Byte) + replace_ios = proginit.conf["DEFAULT"].get("replace_ios", None) + try: + with open(replace_ios, "rb") as fh: + # Hashwert erzeugen und senden + file_hash = md5(fh.read()) + proginit.logger.debug( + "send replace_ios hashvalue: {0}" + "".format(file_hash) + ) + self._devcon.sendall(file_hash) + except Exception as e: + proginit.logger.error( + "error on replace_ios hash value transfair: {0}" + "".format(e) + ) + break + else: + continue + elif cmd == b'EX': # Sauber Verbindung verlassen dirty = False @@ -429,7 +470,6 @@ class RevPiSlaveDev(Thread): break try: - # FIXME: IOCTL für Dateien implementieren if proginit.pargs.procimg == "/dev/piControl0": # Läuft auf RevPi ioctl(fh_proc, request, arg) @@ -439,6 +479,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/revpipyload.py b/revpipyload/revpipyload.py index ea6b286..4f165c8 100755 --- a/revpipyload/revpipyload.py +++ b/revpipyload/revpipyload.py @@ -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,8 +68,6 @@ 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 @@ -76,6 +75,12 @@ class RevPiPyLoad(): self.xsrv = None self.xml_ps = None + # Dateimerker + self.pictorymtime = 0 + self.pictoryhash = b'' + self.replaceiosmtime = 0 + self.replaceiohash = b'' + # Berechtigungsmanger if proginit.pargs.developermode: self.plcslaveacl = IpAclManager(minlevel=0, maxlevel=9) @@ -233,23 +238,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 @@ -597,6 +596,56 @@ 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 + + with open(proginit.pargs.configrsc, "rb") as fh: + file_hash = md5(fh.read()) + if self.pictoryhash == file_hash: + return False + self.pictoryhash = 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 | os.W_OK): + 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 not self.replace_ios_config: + # Dateipfad leer, prüfen ob es vorher einen gab + if self.replaceiosmtime > 0 or self.replaceiohash: + self.replaceiosmtime = 0 + self.replaceiohash = b'' + return True + + else: + mtime = os.path.getmtime(self.replace_ios_config) + if self.replaceiosmtime == mtime: + return False + self.replaceiosmtime = mtime + + with open(self.replace_ios_config, "rb") as fh: + file_hash = md5(fh.read()) + if self.replaceiohash == file_hash: + return False + self.replaceiohash = file_hash + + return True + def packapp(self, mode="tar", pictory=False): """Erzeugt aus dem PLC-Programm ein TAR/Zip-File. @@ -672,6 +721,7 @@ class RevPiPyLoad(): self.plc.start() # mainloop + file_changed = False while not self._exit: # Neue Konfiguration laden @@ -699,21 +749,30 @@ class RevPiPyLoad(): 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): + # Dateiveränderungen prüfen mit beiden Funktionen! + if self.check_pictory_changed(): + file_changed = True proginit.logger.warning("piCtory configuration was changed") - self.pictorymtime = os.path.getmtime(proginit.pargs.configrsc) + if self.check_replace_ios_changed(): + file_changed = True + proginit.logger.warning("replace ios file was changed") + if file_changed: + file_changed = False + + # Auf Dateiveränderung reagieren # MQTT Publisher neu laden if self.mqtt and self.th_plcmqtt is not None: self.th_plcmqtt.reload_revpimodio() # FIXME: ProcImgServer muss alle Verbindungen vernichten + # NOTE: RevPiNetIO müsste bei Neuverbindung Hashs abfragen # 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 self.evt_loadconfig.wait(1) 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, )