Use struct module for net commands, send files with length, replace SD with WD

The struct module is more efficient than int.from_bytes.
piCtory and ReplaceIO file length in the first 4 bytes of transmission
Replaced SD with WD cmd, which will write one request, no buffer
This commit is contained in:
2020-03-08 20:57:56 +01:00
parent b5dd561aa3
commit 602daee3ea

View File

@@ -3,22 +3,24 @@
__author__ = "Sven Sager" __author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager" __copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "GPLv3" __license__ = "GPLv3"
import proginit
import socket import socket
from fcntl import ioctl from fcntl import ioctl
from shared.ipaclmanager import IpAclManager from struct import unpack, pack
from threading import Event, Thread from threading import Event, Thread
from timeit import default_timer from timeit import default_timer
import proginit
from shared.ipaclmanager import IpAclManager
# Hashvalues # Hashvalues
HASH_NULL = b'\x00' * 16 HASH_NULL = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
HASH_FAIL = b'\xff' * 16 HASH_FAIL = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
HASH_PICT = HASH_FAIL HASH_PICT = HASH_FAIL
HASH_RPIO = HASH_NULL HASH_RPIO = HASH_NULL
class RevPiSlave(Thread): class RevPiSlave(Thread):
"""RevPi PLC-Server. """RevPi PLC-Server.
Diese Klasste stellt den RevPi PLC-Server zur verfuegung und akzeptiert Diese Klasste stellt den RevPi PLC-Server zur verfuegung und akzeptiert
@@ -59,7 +61,7 @@ class RevPiSlave(Thread):
def check_connectedacl(self): def check_connectedacl(self):
"""Prueft bei neuen ACLs bestehende Verbindungen.""" """Prueft bei neuen ACLs bestehende Verbindungen."""
for dev in self._th_dev: for dev in self._th_dev:
ip, port = dev._addr ip, port = dev._addr
level = self.__ipacl.get_acllevel(ip) level = self.__ipacl.get_acllevel(ip)
if level < 0: if level < 0:
# Verbindung killen # Verbindung killen
@@ -172,7 +174,6 @@ class RevPiSlave(Thread):
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
@@ -195,7 +196,6 @@ 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._writeerror = False
# Sicherheitsbytes # Sicherheitsbytes
self.ey_dict = {} self.ey_dict = {}
@@ -222,13 +222,12 @@ class RevPiSlaveDev(Thread):
buff_size = 2048 buff_size = 2048
dirty = True dirty = True
netcmd = bytearray()
buff_block = bytearray(buff_size) buff_block = bytearray(buff_size)
buff_recv = bytearray() buff_recv = bytearray()
while not self._evt_exit.is_set(): while not self._evt_exit.is_set():
# Laufzeitberechnung starten # Laufzeitberechnung starten
ot = default_timer() ot = default_timer()
netcmd.clear() buff_recv.clear()
# Meldung erhalten # Meldung erhalten
try: try:
@@ -237,27 +236,27 @@ class RevPiSlaveDev(Thread):
count = self._devcon.recv_into(buff_block, recv_len) count = self._devcon.recv_into(buff_block, recv_len)
if count == 0: if count == 0:
raise IOError("lost network connection") raise IOError("lost network connection")
netcmd += buff_block[:count] buff_recv += buff_block[:count]
recv_len -= count recv_len -= count
# Unpack ist schneller als Direktzugriff oder Umwandlung
netcmd = unpack("=c2sHH8sc", buff_recv)
except Exception as e: except Exception as e:
proginit.logger.exception(e) proginit.logger.error(e)
break break
# Wenn Meldung ungültig ist aussteigen # Wenn Meldung ungültig ist aussteigen
if netcmd[0:1] != b'\x01' or netcmd[-1:] != b'\x17': if netcmd[0] != b'\x01' or netcmd[5] != b'\x17':
if netcmd != b'': proginit.logger.error("net cmd not valid {0}".format(netcmd))
proginit.logger.error(
"net cmd not valid {0}".format(netcmd)
)
break break
cmd = netcmd[1:3] cmd = netcmd[1]
if cmd == b'DA': if cmd == b'DA':
# Processabbild übertragen # Processabbild übertragen
# bCMiiii00000000b = 16 # b CM ii ii 00000000 b = 16
position = int.from_bytes(netcmd[3:5], byteorder="little") position = netcmd[2]
length = int.from_bytes(netcmd[5:7], byteorder="little") length = netcmd[3]
fh_proc.seek(position) fh_proc.seek(position)
try: try:
@@ -266,44 +265,72 @@ class RevPiSlaveDev(Thread):
proginit.logger.error("error while send read data") proginit.logger.error("error while send read data")
break break
elif cmd == b'SD': elif cmd == b'WD':
# Ausgänge setzen, wenn acl es erlaubt # Ausgänge setzen, wenn acl es erlaubt
# bCMiiiic0000000b = 16 # b CM ii ii c0000000 b = 16
# Berechtigung prüfen und ggf. trennen # Berechtigung prüfen und ggf. trennen
if self._acl < 1: if self._acl < 1:
self._devcon.sendall(b'\x18') self._devcon.sendall(b'\x18')
break break
position = int.from_bytes(netcmd[3:5], byteorder="little") position = netcmd[2]
length = int.from_bytes(netcmd[5:7], byteorder="little") length = netcmd[3]
control = netcmd[7:8]
# Datenblock schreiben
buff_recv.clear()
try:
while length > 0:
count = self._devcon.recv_into(buff_block, min(length, buff_size))
if count == 0:
raise IOError("lost network connection")
buff_recv += buff_block[:count]
length -= count
except Exception:
proginit.logger.error("error while recv data for wd write")
break
fh_proc.seek(position)
fh_proc.write(buff_recv)
# Record separator character
self._devcon.sendall(b'\x1e')
elif cmd == b'FD':
# Ausgänge gepuffert setzen, deutlich schneller als SD
# b CM ii ii 00000000 b = 16
# Berechtigung prüfen und ggf. trennen
if self._acl < 1:
self._devcon.sendall(b'\x18')
break
buff_recv.clear()
counter = netcmd[2]
try:
while counter > 0:
count = self._devcon.recv_into(buff_block, min(counter, buff_size))
if count == 0:
raise IOError("lost network connection")
buff_recv += buff_block[:count]
counter -= count
except Exception:
proginit.logger.error("error while recv data for fd write")
break
# Header: ppllbuff
index = 0
while index < netcmd[2]:
position, length = unpack("=HH", buff_recv[index: index + 4])
index += 4
if control == b'\x1d' and length > 0:
# Empfange Datenblock zu schreiben nach Meldung
buff_recv.clear()
try:
while length > 0:
count = self._devcon.recv_into(buff_block, min(length, buff_size))
if count == 0:
raise IOError("lost network connection")
buff_recv += buff_block[:count]
length -= count
except Exception:
proginit.logger.error("error while recv data to write")
self._writeerror = True
break
fh_proc.seek(position) fh_proc.seek(position)
fh_proc.write(buff_recv) fh_proc.write(buff_recv[index:index + length])
# Record seperator character index += length
if control == b'\x1c':
# Bestätige Schreibvorgang aller Datenblöcke # Record separator character
if self._writeerror: self._devcon.sendall(b'\x1e')
self._devcon.sendall(b'\xff')
else:
self._devcon.sendall(b'\x1e')
self._writeerror = False
elif cmd == b'\x06\x16': elif cmd == b'\x06\x16':
# Just sync # Just sync
@@ -311,13 +338,9 @@ class RevPiSlaveDev(Thread):
elif cmd == b'CF': elif cmd == b'CF':
# Socket konfigurieren # Socket konfigurieren
# bCMii0000000000b = 16 # b CM ii xx 00000000 b = 16
try: timeoutms = netcmd[2]
timeoutms = int.from_bytes(netcmd[3:5], byteorder="little")
except Exception:
proginit.logger.error("can not convert timeout value")
break
if 0 < timeoutms <= 65535: if 0 < timeoutms <= 65535:
self._deadtime = timeoutms / 1000 self._deadtime = timeoutms / 1000
@@ -326,7 +349,7 @@ class RevPiSlaveDev(Thread):
"set socket timeout to {0}".format(self._deadtime) "set socket timeout to {0}".format(self._deadtime)
) )
# Record seperator character # Record separator character
self._devcon.sendall(b'\x1e') self._devcon.sendall(b'\x1e')
else: else:
proginit.logger.error("timeout value must be 0 to 65535") proginit.logger.error("timeout value must be 0 to 65535")
@@ -335,16 +358,16 @@ class RevPiSlaveDev(Thread):
elif cmd == b'EY': elif cmd == b'EY':
# Bytes bei Verbindungsabbruch schreiben # Bytes bei Verbindungsabbruch schreiben
# bCMiiiix0000000b = 16 # b CM ii ii x0000000 b = 16
# Berechtigung prüfen und ggf. trennen # Berechtigung prüfen und ggf. trennen
if self._acl < 1: if self._acl < 1:
self._devcon.sendall(b'\x18') self._devcon.sendall(b'\x18')
break break
position = int.from_bytes(netcmd[3:5], byteorder="little") position = netcmd[2]
length = int.from_bytes(netcmd[5:7], byteorder="little") length = netcmd[3]
control = netcmd[7:8] control = netcmd[4][0]
ok_byte = b'\xff' if self.__doerror else b'\x1e' ok_byte = b'\xff' if self.__doerror else b'\x1e'
@@ -352,7 +375,6 @@ class RevPiSlaveDev(Thread):
# Alle Dirtybytes löschen # Alle Dirtybytes löschen
self.ey_dict = {} self.ey_dict = {}
# Record seperator character
self._devcon.sendall(ok_byte) self._devcon.sendall(ok_byte)
proginit.logger.info("cleared all dirty bytes") proginit.logger.info("cleared all dirty bytes")
@@ -362,7 +384,6 @@ class RevPiSlaveDev(Thread):
if position in self.ey_dict: if position in self.ey_dict:
del self.ey_dict[position] del self.ey_dict[position]
# Record seperator character
self._devcon.sendall(ok_byte) self._devcon.sendall(ok_byte)
proginit.logger.info( proginit.logger.info(
"cleared dirty bytes on position {0}" "cleared dirty bytes on position {0}"
@@ -386,7 +407,6 @@ class RevPiSlaveDev(Thread):
self.ey_dict[position] = bytes(buff_recv) self.ey_dict[position] = bytes(buff_recv)
# Record seperator character
self._devcon.sendall(ok_byte) self._devcon.sendall(ok_byte)
proginit.logger.info( proginit.logger.info(
"got dirty bytes to write on error on position {0}" "got dirty bytes to write on error on position {0}"
@@ -401,17 +421,18 @@ class RevPiSlaveDev(Thread):
) )
try: try:
with open(proginit.pargs.configrsc, "rb") as fh_pic: with open(proginit.pargs.configrsc, "rb") as fh_pic:
# Komplette piCtory Datei senden # Komplette piCtory Datei lesen
self._devcon.sendall(fh_pic.read()) buff = fh_pic.read()
self._devcon.sendall(pack("=I", len(buff)) + buff)
except Exception as e: except Exception as e:
proginit.logger.error( proginit.logger.error(
"error on pictory transfair: {0}".format(e) "error on pictory transfair: {0}".format(e)
) )
break break
else:
# End-of-Transmission character immer senden # Laufzeitberechnung überspringen
self._devcon.sendall(b'\x04') continue
continue
elif cmd == b'PH': elif cmd == b'PH':
# piCtory md5 Hashwert senden (16 Byte) # piCtory md5 Hashwert senden (16 Byte)
@@ -427,20 +448,25 @@ class RevPiSlaveDev(Thread):
"".format(proginit.pargs.configrsc) "".format(proginit.pargs.configrsc)
) )
replace_ios = proginit.conf["DEFAULT"].get("replace_ios", "") replace_ios = proginit.conf["DEFAULT"].get("replace_ios", "")
try: if HASH_RPIO != HASH_NULL and replace_ios:
if HASH_RPIO != HASH_NULL and replace_ios: try:
with open(replace_ios, "rb") as fh: with open(replace_ios, "rb") as fh:
# Komplette replace_io Datei senden # Komplette replace_io Datei lesen
self._devcon.sendall(fh.read()) buff = fh.read()
except Exception as e:
proginit.logger.error( self._devcon.sendall(pack("=I", len(buff)) + buff)
"error on replace_io transfair: {0}".format(e) except Exception as e:
) proginit.logger.error(
break "error on replace_io transfair: {0}".format(e)
)
break
else: else:
# End-of-Transmission character immer senden # Nulllänge senden, damit client weiter machen kann
self._devcon.sendall(b'\x04') self._devcon.sendall(b'\x00\x00\x00\x00')
continue
# Laufzeitberechnung überspringen
continue
elif cmd == b'RH': elif cmd == b'RH':
# Replace_IOs md5 Hashwert senden (16 Byte) # Replace_IOs md5 Hashwert senden (16 Byte)
@@ -459,10 +485,10 @@ class RevPiSlaveDev(Thread):
elif cmd == b'IC': elif cmd == b'IC':
# Net-IOCTL ausführen # Net-IOCTL ausführen
# bCMiiiiii000000b = 16 # b CM xx ii iiii0000 b = 16
request = int.from_bytes(netcmd[3:7], byteorder="little") request = unpack("=I4x", netcmd[4])[0]
length = int.from_bytes(netcmd[7:9], byteorder="little") length = netcmd[3]
buff_recv.clear() buff_recv.clear()
try: try:
@@ -503,13 +529,13 @@ class RevPiSlaveDev(Thread):
elif proginit.pargs.developermode and cmd == b'DV': elif proginit.pargs.developermode and cmd == b'DV':
# Development options # Development options
# bCMc00000000000b = 16 # b CM ii ii c0000000 b = 16
if self._acl < 9: if self._acl < 9:
# Spezieller ACL-Wert für Entwicklung # Spezieller ACL-Wert für Entwicklung
self._devcon.sendall(b'\x18') self._devcon.sendall(b'\x18')
break break
c = netcmd[3:4] c = netcmd[4][0]
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
@@ -517,7 +543,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:5] == b'\x01': if netcmd[4][1] == b'\x01':
self.__doerror = True self.__doerror = True
proginit.logger.warning("DV: set do_error") proginit.logger.warning("DV: set do_error")
else: else: