Insert watchdog for netcmd-loop, put unpack values directly to variables

The watchdog will force a disconnect, if the runtime of the netcmd-cylce is longer than __deadtime
Default value of watchdog is True
Split unpacked values of netcmd directly to cmd, position, length and blob
This commit is contained in:
2020-03-17 19:39:20 +01:00
parent 602daee3ea
commit 7f1cd2a58e

View File

@@ -6,7 +6,7 @@ __license__ = "GPLv3"
import socket import socket
from fcntl import ioctl from fcntl import ioctl
from struct import unpack, pack from struct import pack, unpack
from threading import Event, Thread from threading import Event, Thread
from timeit import default_timer from timeit import default_timer
@@ -30,12 +30,13 @@ class RevPiSlave(Thread):
""" """
def __init__(self, ipacl, port=55234, bindip=""): def __init__(self, ipacl, port=55234, bindip="", watchdog=True):
"""Instantiiert RevPiSlave-Klasse. """Instantiiert RevPiSlave-Klasse.
@param ipacl AclManager <class 'IpAclManager'> @param ipacl AclManager <class 'IpAclManager'>
@param port Listen Port fuer plc Slaveserver @param port Listen Port fuer plc Slaveserver
@param bindip IP-Adresse an die der Dienst gebunden wird (leer=alle) @param bindip IP-Adresse an die der Dienst gebunden wird (leer=alle)
@param watchdog Trennen, wenn Verarbeitungszeit zu lang
""" """
if not isinstance(ipacl, IpAclManager): if not isinstance(ipacl, IpAclManager):
@@ -55,6 +56,7 @@ class RevPiSlave(Thread):
self._port = port self._port = port
self.so = None self.so = None
self._th_dev = [] self._th_dev = []
self._watchdog = watchdog
self.zeroonerror = False self.zeroonerror = False
self.zeroonexit = False self.zeroonexit = False
@@ -138,7 +140,7 @@ class RevPiSlave(Thread):
) )
else: else:
# Thread starten # Thread starten
th = RevPiSlaveDev(tup_sock, aclstatus) th = RevPiSlaveDev(tup_sock, aclstatus, self._watchdog)
th.start() th.start()
self._th_dev.append(th) self._th_dev.append(th)
@@ -172,21 +174,30 @@ class RevPiSlave(Thread):
proginit.logger.debug("leave RevPiSlave.stop()") proginit.logger.debug("leave RevPiSlave.stop()")
@property
def watchdog(self) -> bool:
return self._watchdog
@watchdog.setter
def watchdog(self, value: bool):
self._watchdog = value
for th in self._th_dev: # type: RevPiSlaveDev
th.watchdog = value
class RevPiSlaveDev(Thread): class RevPiSlaveDev(Thread):
"""Klasse um eine RevPiModIO Verbindung zu verwalten. """Klasse um eine RevPiModIO Verbindung zu verwalten.
Diese Klasste stellt die Funktionen zur Verfuegung um Daten ueber das Diese Klasste stellt die Funktionen zur Verfuegung um Daten ueber das
Netzwerk mit dem Prozessabbild auszutauschen. Netzwerk mit dem Prozessabbild auszutauschen.
""" """
def __init__(self, devcon, acl): def __init__(self, devcon, acl, watchdog: bool):
"""Init RevPiSlaveDev-Class. """Init RevPiSlaveDev-Class.
@param devcon Tuple der Verbindung @param devcon Tuple der Verbindung
@param acl Berechtigungslevel @param acl Berechtigungslevel
@param watchdog Trennen, wenn Verarbeitungszeit zu lang
""" """
super().__init__() super().__init__()
self.__doerror = False self.__doerror = False
@@ -196,6 +207,7 @@ class RevPiSlaveDev(Thread):
self._devcon, self._addr = devcon self._devcon, self._addr = devcon
self._evt_exit = Event() self._evt_exit = Event()
self.got_replace_ios = False self.got_replace_ios = False
self.watchdog = watchdog
# Sicherheitsbytes # Sicherheitsbytes
self.ey_dict = {} self.ey_dict = {}
@@ -214,11 +226,14 @@ class RevPiSlaveDev(Thread):
try: try:
fh_proc = open(proginit.pargs.procimg, "r+b", 0) fh_proc = open(proginit.pargs.procimg, "r+b", 0)
except Exception: except Exception:
fh_proc = None
self._evt_exit.set() self._evt_exit.set()
proginit.logger.error( proginit.logger.error(
"can not open process image {0}".format(proginit.pargs.procimg) "can not open process image {0} for {1}"
"".format(proginit.pargs.procimg, self._addr)
) )
self._devcon.close()
self._devcon = None
return
buff_size = 2048 buff_size = 2048
dirty = True dirty = True
@@ -240,24 +255,24 @@ class RevPiSlaveDev(Thread):
recv_len -= count recv_len -= count
# Unpack ist schneller als Direktzugriff oder Umwandlung # Unpack ist schneller als Direktzugriff oder Umwandlung
netcmd = unpack("=c2sHH8sc", buff_recv) p_start, cmd, position, length, blob, p_stop = \
unpack("=c2sHH8sc", buff_recv)
except Exception as e: except Exception as e:
proginit.logger.error(e) proginit.logger.error(e)
break break
# Wenn Meldung ungültig ist aussteigen # Wenn Meldung ungültig ist aussteigen
if netcmd[0] != b'\x01' or netcmd[5] != b'\x17': if p_start != b'\x01' or p_stop != b'\x17':
proginit.logger.error("net cmd not valid {0}".format(netcmd)) proginit.logger.error(
"net cmd not valid {0}|{1}|..|{2}"
"".format(p_start, cmd, p_stop)
)
break break
cmd = netcmd[1]
if cmd == b'DA': if cmd == b'DA':
# Processabbild übertragen # Processabbild übertragen
# b CM ii ii 00000000 b = 16 # b CM ii ii 00000000 b = 16
position = netcmd[2]
length = netcmd[3]
fh_proc.seek(position) fh_proc.seek(position)
try: try:
self._devcon.sendall(fh_proc.read(length)) self._devcon.sendall(fh_proc.read(length))
@@ -274,9 +289,6 @@ class RevPiSlaveDev(Thread):
self._devcon.sendall(b'\x18') self._devcon.sendall(b'\x18')
break break
position = netcmd[2]
length = netcmd[3]
# Datenblock schreiben # Datenblock schreiben
buff_recv.clear() buff_recv.clear()
try: try:
@@ -297,7 +309,7 @@ class RevPiSlaveDev(Thread):
self._devcon.sendall(b'\x1e') self._devcon.sendall(b'\x1e')
elif cmd == b'FD': elif cmd == b'FD':
# Ausgänge gepuffert setzen, deutlich schneller als SD # Ausgänge gepuffert setzen, deutlich schneller als WD
# b CM ii ii 00000000 b = 16 # b CM ii ii 00000000 b = 16
# Berechtigung prüfen und ggf. trennen # Berechtigung prüfen und ggf. trennen
@@ -306,7 +318,7 @@ class RevPiSlaveDev(Thread):
break break
buff_recv.clear() buff_recv.clear()
counter = netcmd[2] counter = length
try: try:
while counter > 0: while counter > 0:
count = self._devcon.recv_into(buff_block, min(counter, buff_size)) count = self._devcon.recv_into(buff_block, min(counter, buff_size))
@@ -320,14 +332,14 @@ class RevPiSlaveDev(Thread):
# Header: ppllbuff # Header: ppllbuff
index = 0 index = 0
while index < netcmd[2]: while index < length:
position, length = unpack("=HH", buff_recv[index: index + 4]) r_position, r_length = unpack("=HH", buff_recv[index: index + 4])
index += 4 index += 4
fh_proc.seek(position) fh_proc.seek(r_position)
fh_proc.write(buff_recv[index:index + length]) fh_proc.write(buff_recv[index:index + r_length])
index += length index += r_length
# Record separator character # Record separator character
self._devcon.sendall(b'\x1e') self._devcon.sendall(b'\x1e')
@@ -340,10 +352,9 @@ class RevPiSlaveDev(Thread):
# Socket konfigurieren # Socket konfigurieren
# b CM ii xx 00000000 b = 16 # b CM ii xx 00000000 b = 16
timeoutms = netcmd[2] # position = timeoutms
if 0 < position <= 65535:
if 0 < timeoutms <= 65535: self._deadtime = position / 1000
self._deadtime = timeoutms / 1000
self._devcon.settimeout(self._deadtime) self._devcon.settimeout(self._deadtime)
proginit.logger.debug( proginit.logger.debug(
"set socket timeout to {0}".format(self._deadtime) "set socket timeout to {0}".format(self._deadtime)
@@ -365,9 +376,7 @@ class RevPiSlaveDev(Thread):
self._devcon.sendall(b'\x18') self._devcon.sendall(b'\x18')
break break
position = netcmd[2] control = blob[0:1]
length = netcmd[3]
control = netcmd[4][0]
ok_byte = b'\xff' if self.__doerror else b'\x1e' ok_byte = b'\xff' if self.__doerror else b'\x1e'
@@ -487,8 +496,7 @@ class RevPiSlaveDev(Thread):
# Net-IOCTL ausführen # Net-IOCTL ausführen
# b CM xx ii iiii0000 b = 16 # b CM xx ii iiii0000 b = 16
request = unpack("=I4x", netcmd[4])[0] request, = unpack("=I4x", blob)
length = netcmd[3]
buff_recv.clear() buff_recv.clear()
try: try:
@@ -535,7 +543,7 @@ class RevPiSlaveDev(Thread):
self._devcon.sendall(b'\x18') self._devcon.sendall(b'\x18')
break break
c = netcmd[4][0] c, d = unpack("=cc6x", blob)
if c == b'a': if c == b'a':
# CMD a = Switch ACL to 0 # CMD a = Switch ACL to 0
self._acl = 0 self._acl = 0
@@ -543,7 +551,7 @@ class RevPiSlaveDev(Thread):
self._devcon.sendall(b'\x1e') self._devcon.sendall(b'\x1e')
elif c == b'b': elif c == b'b':
# CMD b = Aktiviert/Deaktiviert den Fehlermodus # CMD b = Aktiviert/Deaktiviert den Fehlermodus
if netcmd[4][1] == b'\x01': if d == b'\x01':
self.__doerror = True self.__doerror = True
proginit.logger.warning("DV: set do_error") proginit.logger.warning("DV: set do_error")
else: else:
@@ -558,16 +566,23 @@ class RevPiSlaveDev(Thread):
) )
break break
# Verarbeitungszeit prüfen # Verarbeitungszeit nicht prüfen
if self._deadtime is not None: if self._deadtime is None:
comtime = default_timer() - ot continue
if comtime > self._deadtime:
proginit.logger.warning( comtime = default_timer() - ot
"runtime more than {0} ms: {1}!".format( if comtime > self._deadtime:
int(self._deadtime * 1000), comtime if self.watchdog:
) proginit.logger.error(
"runtime more than {0} ms: {1} - force disconnect"
"".format(int(self._deadtime * 1000), comtime)
)
break
else:
proginit.logger.warning(
"runtime more than {0} ms: {1}!"
"".format(int(self._deadtime * 1000), comtime)
) )
# TODO: Soll ein Fehler ausgelöst werden?
# Dirty verlassen # Dirty verlassen
if dirty: if dirty:
@@ -577,8 +592,7 @@ class RevPiSlaveDev(Thread):
proginit.logger.error("dirty shutdown of connection") proginit.logger.error("dirty shutdown of connection")
if fh_proc is not None: fh_proc.close()
fh_proc.close()
self._devcon.close() self._devcon.close()
self._devcon = None self._devcon = None