mirror of
https://github.com/naruxde/revpimodio2.git
synced 2025-11-08 22:03:53 +01:00
Reconnect sicherer gestaltet
Abfrage von .reconnecting eingebaut Dirtybytes und Timeout lösen keine Exception mehr aus sondern einen Reconnect
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
AclException Exception
|
||||
Base Device
|
||||
Connect Core
|
||||
Core Base
|
||||
|
||||
@@ -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.__socklock.acquire()
|
||||
self._slavesock.send(_sysexit)
|
||||
else:
|
||||
|
||||
# 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
|
||||
# Daten immer übernehmen
|
||||
self.__dictdirty[position] = dirtybytes
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user