diff --git a/doc/revpimodio2.netio.html b/doc/revpimodio2.netio.html
index 48b8224..f92c724 100644
--- a/doc/revpimodio2.netio.html
+++ b/doc/revpimodio2.netio.html
@@ -18,6 +18,9 @@ Global Attributes
Classes
+| AclException |
+Probleme mit Berechtigungen. |
+
| NetFH |
Netzwerk File Handler fuer das Prozessabbild. |
@@ -37,6 +40,37 @@ Functions
| None |
+
+AclException
+
+Probleme mit Berechtigungen.
+
+
+Derived from
+Exception
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+
NetFH
@@ -54,7 +88,7 @@ Thread
Class Attributes
-| __slots__ |
| closed |
| name |
| timeout |
+| __slots__ |
| closed |
| name |
| reconnecting |
| timeout |
Class Methods
@@ -98,6 +132,9 @@ Methods
get_name |
Verbindugnsnamen zurueckgeben. |
+| get_reconnecting |
+Interner reconnect aktiv wegen Netzwerkfehlern. |
+
| get_timeout |
Gibt aktuellen Timeout zurueck. |
@@ -241,6 +278,17 @@ Verbindugnsnamen zurueckgeben.
IP:PORT
+
+
+NetFH.get_reconnecting
+get_reconnecting()
+
+Interner reconnect aktiv wegen Netzwerkfehlern.
+
+- Returns:
+-
+True, wenn reconnect aktiv
+
NetFH.get_timeout
@@ -385,7 +433,7 @@ _RevPiModIO
Class Attributes
-| __slots__ |
+| __slots__ |
| reconnecting |
Class Methods
@@ -408,6 +456,9 @@ Methods
get_jconfigrsc |
Laedt die piCotry Konfiguration und erstellt ein . |
+| get_reconnecting |
+Interner reconnect aktiv wegen Netzwerkfehlern. |
+
| net_cleardefaultvalues |
Loescht Defaultwerte vom PLC Slave. |
@@ -476,6 +527,17 @@ Laedt die piCotry Konfiguration und erstellt ein .
der piCtory Konfiguration
+
+
+RevPiNetIO.get_reconnecting
+get_reconnecting()
+
+Interner reconnect aktiv wegen Netzwerkfehlern.
+
+- Returns:
+-
+True, wenn reconnect aktiv
+
RevPiNetIO.net_cleardefaultvalues
diff --git a/eric-revpimodio2.api b/eric-revpimodio2.api
index 6572765..6316765 100644
--- a/eric-revpimodio2.api
+++ b/eric-revpimodio2.api
@@ -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)
diff --git a/eric-revpimodio2.bas b/eric-revpimodio2.bas
index 3eda7c3..dea047d 100644
--- a/eric-revpimodio2.bas
+++ b/eric-revpimodio2.bas
@@ -1,3 +1,4 @@
+AclException Exception
Base Device
Connect Core
Core Base
diff --git a/revpimodio2/netio.py b/revpimodio2/netio.py
index c2da6a4..e0a9363 100644
--- a/revpimodio2/netio.py
+++ b/revpimodio2/netio.py
@@ -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 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 in Millisekunden"""
@@ -264,7 +292,7 @@ class NetFH(Thread):
"""IOCTL Befehle ueber das Netzwerk senden.
@param request Request as
@param arg Argument as """
- 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 """
- 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 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 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 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):