bindip für PlcSlave wird jetzt verarbeitet

Fehlerabfang bei piCtory-Übertragung
IOCTL über das Netzwerk implementiert
Parameter --developermode implementiert
This commit is contained in:
2018-12-12 10:03:50 +01:00
parent 2fac129232
commit 637348111b
3 changed files with 116 additions and 32 deletions

View File

@@ -5,6 +5,7 @@ __copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "GPLv3"
import proginit
import socket
from fcntl import ioctl
from shared.ipaclmanager import IpAclManager
from threading import Event, Thread
from timeit import default_timer
@@ -16,23 +17,32 @@ class RevPiSlave(Thread):
"""RevPi PLC-Server.
Diese Klasste stellt den RevPi PLC-Server zur verfuegung und akzeptiert
neue Verbindungen. Dieser werden dann als RevPiSlaveDev abgebildet.
neue Verbindungen. Diese werden dann als RevPiSlaveDev abgebildet.
Ueber die angegebenen ACLs koennen Zugriffsbeschraenkungen vergeben werden.
"""
def __init__(self, ipacl, port=55234):
def __init__(self, ipacl, port=55234, bindip=""):
"""Instantiiert RevPiSlave-Klasse.
@param ipacl AclManager <class 'IpAclManager'>
@param port Listen Port fuer plc Slaveserver"""
if not type(ipacl) == IpAclManager:
@param port Listen Port fuer plc Slaveserver
@param bindip IP-Adresse an die der Dienst gebunden wird (leer=alle)
"""
if not isinstance(ipacl, IpAclManager):
raise ValueError("parameter ipacl must be <class 'IpAclManager'>")
if not type(port) == int:
raise ValueError("parameter port must be <class 'int'>")
if not (isinstance(port, int) and 0 < port <= 65535):
raise ValueError(
"parameter port must be <class 'int'> and in range 1 - 65535"
)
if not isinstance(bindip, str):
raise ValueError("parameter bindip must be <class 'str'>")
super().__init__()
self.__ipacl = ipacl
self._bindip = bindip
self._evt_exit = Event()
self.exitcode = None
self._port = port
@@ -68,17 +78,19 @@ class RevPiSlave(Thread):
"""Startet Serverkomponente fuer die Annahme neuer Verbindungen."""
proginit.logger.debug("enter RevPiSlave.run()")
# Socket öffnen und konfigurieren
# Socket öffnen und konfigurieren bis Erfolg oder Ende
self.so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while not self._evt_exit.is_set():
try:
self.so.bind(("", self._port))
except Exception:
proginit.logger.warning("can not bind socket - retry")
self.so.bind((self._bindip, self._port))
except Exception as e:
proginit.logger.warning(
"can not bind socket: {0} - retry".format(e)
)
self._evt_exit.wait(1)
else:
self.so.listen(15)
break
self.so.listen(15)
# Mit Socket arbeiten
while not self._evt_exit.is_set():
@@ -156,6 +168,7 @@ class RevPiSlaveDev(Thread):
"""
super().__init__()
self.__doerror = False
self._acl = acl
self.daemon = True
self._deadtime = None
@@ -273,7 +286,7 @@ class RevPiSlaveDev(Thread):
proginit.logger.error("can not convert timeout value")
break
if 0 < timeoutms < 65535:
if 0 < timeoutms <= 65535:
self._deadtime = timeoutms / 1000
self._devcon.settimeout(self._deadtime)
proginit.logger.debug(
@@ -300,12 +313,14 @@ class RevPiSlaveDev(Thread):
length = int.from_bytes(netcmd[5:7], byteorder="little")
control = netcmd[7:8]
ok_byte = b'\xff' if self.__doerror else b'\x1e'
if control == b'\xff':
# Alle Dirtybytes löschen
self.ey_dict = {}
# Record seperator character
self._devcon.send(b'\x1e')
self._devcon.send(ok_byte)
proginit.logger.info("cleared all dirty bytes")
elif control == b'\xfe':
@@ -315,7 +330,7 @@ class RevPiSlaveDev(Thread):
del self.ey_dict[position]
# Record seperator character
self._devcon.send(b'\x1e')
self._devcon.send(ok_byte)
proginit.logger.info(
"cleared dirty bytes on position {0}"
"".format(position)
@@ -344,7 +359,7 @@ class RevPiSlaveDev(Thread):
break
# Record seperator character
self._devcon.send(b'\x1e')
self._devcon.send(ok_byte)
proginit.logger.info(
"got dirty bytes to write on error on position {0}"
"".format(position)
@@ -356,19 +371,20 @@ class RevPiSlaveDev(Thread):
"transfair pictory configuration: {0}"
"".format(proginit.pargs.configrsc)
)
fh_pic = open(proginit.pargs.configrsc, "rb")
while True:
data = fh_pic.read(1024)
if data:
# FIXME: Fehler fangen
self._devcon.send(data)
else:
fh_pic.close()
break
# End-of-Transmission character
self._devcon.send(b'\x04')
continue
try:
with open(proginit.pargs.configrsc, "rb") as fh_pic:
# Komplette piCtory Datei senden
self._devcon.sendall(fh_pic.read())
except Exception as e:
proginit.logger.error(
"error on pictory transfair: {0}".format(e)
)
break
else:
continue
finally:
# End-of-Transmission character immer senden
self._devcon.send(b'\x04')
elif cmd == b'EX':
# Sauber Verbindung verlassen
@@ -376,8 +392,68 @@ class RevPiSlaveDev(Thread):
self._evt_exit.set()
continue
elif cmd == b'IC':
# Net-IOCTL ausführen
# bCMiiiiii000000b = 16
request = int.from_bytes(netcmd[3:7], byteorder="little")
length = int.from_bytes(netcmd[7:9], byteorder="little")
arg = self._devcon.recv(length)
# Berechtigung prüfen und ggf. trennen
if self._acl < 1:
self._devcon.send(b'\x18')
break
try:
if proginit.pargs.procimg == "/dev/piControl0":
# Läuft auf RevPi
ioctl(fh_proc, request, arg)
proginit.logger.debug(
"ioctl {0} with {1} successful"
"".format(request, arg)
)
else:
# Simulation
proginit.logger.warning(
"ioctl {0} with {1} simulated".format(request, arg)
)
except Exception as ex:
proginit.logger.error(ex)
self._devcon.send(b'\xff')
else:
self._devcon.send(b'\x1e')
elif proginit.pargs.developermode and cmd == b'DV':
# Development options
# bCMc00000000000b = 16
if self._acl < 9:
# Spezieller ACL-Wert für Entwicklung
self._devcon.send(b'\x18')
break
c = netcmd[3:4]
if c == b'a':
# CMD a = Switch ACL to 0
self._acl = 0
proginit.logger.warning("DV: set acl to 0")
self._devcon.send(b'\x1e')
elif c == b'b':
# CMD b = Aktiviert/Deaktiviert den Fehlermodus
if netcmd[4:5] == b'\x01':
self.__doerror = True
proginit.logger.warning("DV: set do_error")
else:
self.__doerror = False
proginit.logger.warning("DV: reset do_error")
self._devcon.send(b'\x1e')
else:
# Kein gültiges CMD gefunden, abbruch!
proginit.logger.error(
"found unknown net cmd: {0}".format(cmd)
)
break
# Verarbeitungszeit prüfen

View File

@@ -65,7 +65,11 @@ def configure():
)
parser.add_argument(
"-v", "--verbose", action="count", dest="verbose",
help="Switch on verbose logging"
help="Switch on verbose logging: info -v debug -vv"
)
parser.add_argument(
"--developermode", action="store_true", dest="developermode",
default=False
)
global pargs
pargs = parser.parse_args()

View File

@@ -73,8 +73,12 @@ class RevPiPyLoad():
self.xml_ps = None
# Berechtigungsmanger
self.plcslaveacl = IpAclManager(minlevel=0, maxlevel=1)
self.xmlrpcacl = IpAclManager(minlevel=0, maxlevel=4)
if proginit.pargs.developermode:
self.plcslaveacl = IpAclManager(minlevel=0, maxlevel=9)
self.xmlrpcacl = IpAclManager(minlevel=0, maxlevel=9)
else:
self.plcslaveacl = IpAclManager(minlevel=0, maxlevel=1)
self.xmlrpcacl = IpAclManager(minlevel=0, maxlevel=4)
# Threads/Prozesse
self.th_plcmqtt = None
@@ -519,7 +523,7 @@ class RevPiPyLoad():
if self.plcslave:
th_plc = picontrolserver.RevPiSlave(
self.plcslaveacl, self.plcslaveport
self.plcslaveacl, self.plcslaveport, self.plcslavebindip
)
proginit.logger.debug("leave RevPiPyLoad._plcslave()")