Reconnect sicherer gestaltet

Abfrage von .reconnecting eingebaut
Dirtybytes und Timeout lösen keine Exception mehr aus sondern einen Reconnect
This commit is contained in:
2019-08-16 22:32:27 +02:00
parent da8f944486
commit 2009ed9ce5
4 changed files with 168 additions and 45 deletions

View File

@@ -18,6 +18,9 @@ Global Attributes</h3>
Classes</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#AclException">AclException</a></td>
<td>Probleme mit Berechtigungen.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH">NetFH</a></td>
<td>Netzwerk File Handler fuer das Prozessabbild.</td>
</tr><tr>
@@ -37,6 +40,37 @@ Functions</h3>
<tr><td>None</td></tr>
</table>
<hr /><hr />
<a NAME="AclException" ID="AclException"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">AclException</h2>
<p>
Probleme mit Berechtigungen.
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
Exception
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="NetFH" ID="NetFH"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">NetFH</h2>
<p>
@@ -54,7 +88,7 @@ Thread
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>__slots__</td></tr><tr><td>closed</td></tr><tr><td>name</td></tr><tr><td>timeout</td></tr>
<tr><td>__slots__</td></tr><tr><td>closed</td></tr><tr><td>name</td></tr><tr><td>reconnecting</td></tr><tr><td>timeout</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
@@ -98,6 +132,9 @@ Methods</h3>
<td><a style="color:#0000FF" href="#NetFH.get_name">get_name</a></td>
<td>Verbindugnsnamen zurueckgeben.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.get_reconnecting">get_reconnecting</a></td>
<td>Interner reconnect aktiv wegen Netzwerkfehlern.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.get_timeout">get_timeout</a></td>
<td>Gibt aktuellen Timeout zurueck.</td>
</tr><tr>
@@ -241,6 +278,17 @@ Verbindugnsnamen zurueckgeben.
<dd>
<class 'str'> IP:PORT
</dd>
</dl><a NAME="NetFH.get_reconnecting" ID="NetFH.get_reconnecting"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.get_reconnecting</h3>
<b>get_reconnecting</b>(<i></i>)
<p>
Interner reconnect aktiv wegen Netzwerkfehlern.
</p><dl>
<dt>Returns:</dt>
<dd>
True, wenn reconnect aktiv
</dd>
</dl><a NAME="NetFH.get_timeout" ID="NetFH.get_timeout"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.get_timeout</h3>
@@ -385,7 +433,7 @@ _RevPiModIO
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>__slots__</td></tr>
<tr><td>__slots__</td></tr><tr><td>reconnecting</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
@@ -408,6 +456,9 @@ Methods</h3>
<td><a style="color:#0000FF" href="#RevPiNetIO.get_jconfigrsc">get_jconfigrsc</a></td>
<td>Laedt die piCotry Konfiguration und erstellt ein <class 'dict'>.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIO.get_reconnecting">get_reconnecting</a></td>
<td>Interner reconnect aktiv wegen Netzwerkfehlern.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIO.net_cleardefaultvalues">net_cleardefaultvalues</a></td>
<td>Loescht Defaultwerte vom PLC Slave.</td>
</tr><tr>
@@ -476,6 +527,17 @@ Laedt die piCotry Konfiguration und erstellt ein <class 'dict'>.
<dd>
<class 'dict'> der piCtory Konfiguration
</dd>
</dl><a NAME="RevPiNetIO.get_reconnecting" ID="RevPiNetIO.get_reconnecting"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO.get_reconnecting</h3>
<b>get_reconnecting</b>(<i></i>)
<p>
Interner reconnect aktiv wegen Netzwerkfehlern.
</p><dl>
<dt>Returns:</dt>
<dd>
True, wenn reconnect aktiv
</dd>
</dl><a NAME="RevPiNetIO.net_cleardefaultvalues" ID="RevPiNetIO.net_cleardefaultvalues"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO.net_cleardefaultvalues</h3>

View File

@@ -192,11 +192,13 @@ revpimodio2.netio.NetFH.closed?7
revpimodio2.netio.NetFH.flush?4()
revpimodio2.netio.NetFH.get_closed?4()
revpimodio2.netio.NetFH.get_name?4()
revpimodio2.netio.NetFH.get_reconnecting?4()
revpimodio2.netio.NetFH.get_timeout?4()
revpimodio2.netio.NetFH.ioctl?4(request, arg=b'')
revpimodio2.netio.NetFH.name?7
revpimodio2.netio.NetFH.read?4(length)
revpimodio2.netio.NetFH.readpictory?4()
revpimodio2.netio.NetFH.reconnecting?7
revpimodio2.netio.NetFH.run?4()
revpimodio2.netio.NetFH.seek?4(position)
revpimodio2.netio.NetFH.set_dirtybytes?4(position, dirtybytes)
@@ -208,8 +210,10 @@ revpimodio2.netio.NetFH?1(address, timeout=500)
revpimodio2.netio.RevPiNetIO._create_myfh?5()
revpimodio2.netio.RevPiNetIO.disconnect?4()
revpimodio2.netio.RevPiNetIO.get_jconfigrsc?4()
revpimodio2.netio.RevPiNetIO.get_reconnecting?4()
revpimodio2.netio.RevPiNetIO.net_cleardefaultvalues?4(device=None)
revpimodio2.netio.RevPiNetIO.net_setdefaultvalues?4(device=None)
revpimodio2.netio.RevPiNetIO.reconnecting?7
revpimodio2.netio.RevPiNetIO?1(address, autorefresh=False, monitoring=False, syncoutputs=True, simulator=False, debug=False, replace_io_file=None, direct_output=False)
revpimodio2.netio.RevPiNetIODriver?1(address, virtdev, autorefresh=False, monitoring=False, syncoutputs=True, debug=False, replace_io_file=None, direct_output=False)
revpimodio2.netio.RevPiNetIOSelected?1(address, deviceselection, autorefresh=False, monitoring=False, syncoutputs=True, simulator=False, debug=False, replace_io_file=None, direct_output=False)

View File

@@ -1,3 +1,4 @@
AclException Exception
Base Device
Connect Core
Core Base

View File

@@ -25,6 +25,13 @@ _syspictory = b'\x01PI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
_sysflush = b'\x01SD\x00\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x17'
class AclException(Exception):
"""Probleme mit Berechtigungen."""
pass
class NetFH(Thread):
"""Netzwerk File Handler fuer das Prozessabbild.
@@ -54,7 +61,7 @@ class NetFH(Thread):
self.__flusherr = False
self.__sockact = False
self.__sockerr = Event()
self.__sockend = False
self.__sockend = Event()
self.__socklock = Lock()
self.__timeout = None
self.__trigger = False
@@ -90,10 +97,10 @@ class NetFH(Thread):
if bytecode == b'\x18':
# Alles beenden, wenn nicht erlaubt
self.__sockend = True
self.__sockend.set()
self.__sockerr.set()
self._slavesock.close()
raise RuntimeError(
raise AclException(
"write access to the process image is not permitted - use "
"monitoring=True or check aclplcslave.conf on RevPi and "
"reload revpipyload!"
@@ -151,11 +158,12 @@ class NetFH(Thread):
@returns Empfangende Bytes
"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("I/O operation on closed file")
with self.__socklock:
self._slavesock.sendall(send_bytes)
# FIXME: Schleife bis Daten empfangen sind einbauen
recv = self._slavesock.recv(recv_count)
self.__trigger = True
return recv
@@ -163,10 +171,13 @@ class NetFH(Thread):
def clear_dirtybytes(self, position=None):
"""Entfernt die konfigurierten Dirtybytes vom RevPi Slave.
@param position Startposition der Dirtybytes"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("I/O operation on closed file")
with self.__socklock:
error = False
try:
self.__socklock.acquire()
if position is None:
# Alle Dirtybytes löschen
self._slavesock.sendall(_sysdeldirty)
@@ -184,40 +195,52 @@ class NetFH(Thread):
# ACL prüfen und ggf Fehler werfen
self.__check_acl(check)
self.__sockerr.set()
raise IOError("clear dirtybytes error on network")
except AclException:
raise
except Exception:
error = True
finally:
self.__socklock.release()
# Daten bei Erfolg übernehmen
# Daten immer übernehmen
if position is None:
self.__dictdirty = {}
elif position in self.__dictdirty:
del self.__dictdirty[position]
if error:
# Fehler nach übernahme der Daten auslösen um diese zu setzen
self.__sockerr.set()
self.__trigger = True
def close(self):
"""Verbindung trennen."""
if self.__sockend:
if self.__sockend.is_set():
return
self.__sockend = True
self.__sockend.set()
self.__sockerr.set()
# Vom Socket sauber trennen
if self._slavesock is not None:
with self.__socklock:
try:
if self.__sockend:
self._slavesock.send(_sysexit)
else:
self._slavesock.shutdown(socket.SHUT_RDWR)
except Exception:
pass
try:
self.__socklock.acquire()
self._slavesock.send(_sysexit)
# NOTE: Wird das benötigt?
self._slavesock.shutdown(socket.SHUT_RDWR)
except Exception:
pass
finally:
self.__socklock.release()
self._slavesock.close()
def flush(self):
"""Schreibpuffer senden."""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("flush of closed file")
with self.__socklock:
@@ -248,13 +271,18 @@ class NetFH(Thread):
def get_closed(self):
"""Pruefen ob Verbindung geschlossen ist.
@return True, wenn Verbindung geschlossen ist"""
return self.__sockend
return self.__sockend.is_set()
def get_name(self):
"""Verbindugnsnamen zurueckgeben.
@return <class 'str'> IP:PORT"""
return "{0}:{1}".format(*self._address)
def get_reconnecting(self):
"""Interner reconnect aktiv wegen Netzwerkfehlern.
@return True, wenn reconnect aktiv"""
return self.__sockerr.is_set()
def get_timeout(self):
"""Gibt aktuellen Timeout zurueck.
@return <class 'int'> in Millisekunden"""
@@ -264,7 +292,7 @@ class NetFH(Thread):
"""IOCTL Befehle ueber das Netzwerk senden.
@param request Request as <class 'int'>
@param arg Argument as <class 'byte'>"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("read of closed file")
if not (isinstance(arg, bytes) and len(arg) <= 1024):
@@ -295,7 +323,7 @@ class NetFH(Thread):
"""Daten ueber das Netzwerk lesen.
@param length Anzahl der Bytes
@return Gelesene <class 'bytes'>"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("read of closed file")
with self.__socklock:
@@ -307,8 +335,8 @@ class NetFH(Thread):
)
bytesbuff = bytearray()
while not self.__sockend and len(bytesbuff) < length:
rbytes = self._slavesock.recv(1024)
while not self.__sockend.is_set() and len(bytesbuff) < length:
rbytes = self._slavesock.recv(256)
if rbytes == b'':
self.__sockerr.set()
@@ -323,33 +351,36 @@ class NetFH(Thread):
def readpictory(self):
"""Ruft die piCtory Konfiguration ab.
@return <class 'bytes'> piCtory Datei"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("read of closed file")
with self.__socklock:
self._slavesock.send(_syspictory)
byte_buff = bytearray()
while not self.__sockend:
data = self._slavesock.recv(1024)
while not self.__sockend.is_set():
data = self._slavesock.recv(256)
byte_buff += data
if data.find(b'\x04') >= 0:
self.__trigger = True
# NOTE: Nur suchen oder Ende prüfen?
return byte_buff[:-1]
self.__sockerr.set()
raise IOError("readpictory error on network")
self.__trigger = True
def run(self):
"""Handler fuer Synchronisierung."""
while not self.__sockend:
while not self.__sockend.is_set():
# Bei Fehlermeldung neu verbinden
if self.__sockerr.is_set():
self._connect()
if self.__sockerr.is_set():
# Verhindert bei Scheitern 100% CPU last
self.__sockend.wait(self.__waitsync)
else:
# Kein Fehler aufgetreten, sync durchführen wenn socket frei
@@ -380,7 +411,7 @@ class NetFH(Thread):
def seek(self, position):
"""Springt an angegebene Position.
@param position An diese Position springen"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("seek of closed file")
self.__position = int(position)
@@ -388,10 +419,13 @@ class NetFH(Thread):
"""Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.
@param positon Startposition zum Schreiben
@param dirtybytes <class 'bytes'> die geschrieben werden sollen"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("I/O operation on closed file")
with self.__socklock:
error = False
try:
self.__socklock.acquire()
self._slavesock.sendall(
b'\x01EY' +
position.to_bytes(length=2, byteorder="little") +
@@ -406,24 +440,35 @@ class NetFH(Thread):
# ACL prüfen und ggf Fehler werfen
self.__check_acl(check)
self.__sockerr.set()
raise IOError("set dirtybytes error on network")
except AclException:
raise
except Exception:
error = True
finally:
self.__socklock.release()
# Daten erfolgreich übernehmen
self.__dictdirty[position] = dirtybytes
# Daten immer übernehmen
self.__dictdirty[position] = dirtybytes
self.__trigger = True
if error:
# Fehler nach übernahme der Daten auslösen um diese zu setzen
self.__sockerr.set()
self.__trigger = True
def set_timeout(self, value):
"""Setzt Timeoutwert fuer Verbindung.
@param value Timeout in Millisekunden"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("I/O operation on closed file")
# Timeoutwert verarbeiten (könnte Exception auslösen)
self.__set_systimeout(value)
with self.__socklock:
try:
self.__socklock.acquire()
self._slavesock.send(
b'\x01CF' +
value.to_bytes(length=2, byteorder="little") +
@@ -431,15 +476,18 @@ class NetFH(Thread):
)
check = self._slavesock.recv(1)
if check != b'\x1e':
self.__sockerr.set()
raise IOError("set timeout error on network")
except Exception:
self.__sockerr.set()
finally:
self.__socklock.release()
self.__trigger = True
self.__trigger = True
def tell(self):
"""Gibt aktuelle Position zurueck.
@return int aktuelle Position"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("I/O operation on closed file")
return self.__position
@@ -447,7 +495,7 @@ class NetFH(Thread):
"""Daten ueber das Netzwerk schreiben.
@param bytebuff Bytes zum schreiben
@return <class 'int'> Anzahl geschriebener bytes"""
if self.__sockend:
if self.__sockend.is_set():
raise ValueError("write to closed file")
if self.__flusherr:
@@ -469,6 +517,7 @@ class NetFH(Thread):
closed = property(get_closed)
name = property(get_name)
reconnecting = property(get_reconnecting)
timeout = property(get_timeout, set_timeout)
@@ -579,6 +628,11 @@ class RevPiNetIO(_RevPiModIO):
mynh.close()
return jloads(byte_buff.decode("utf-8"))
def get_reconnecting(self):
"""Interner reconnect aktiv wegen Netzwerkfehlern.
@return True, wenn reconnect aktiv"""
return self._myfh.reconnecting
def net_cleardefaultvalues(self, device=None):
"""Loescht Defaultwerte vom PLC Slave.
@param device nur auf einzelnes Device anwenden, sonst auf Alle"""
@@ -646,6 +700,8 @@ class RevPiNetIO(_RevPiModIO):
dev._offset + dev._slc_out.start, dirtybytes
)
reconnecting = property(get_reconnecting)
class RevPiNetIOSelected(RevPiNetIO):