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