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