Modul netio eingefügt

This commit is contained in:
2017-09-11 16:29:44 +02:00
parent 974fe26fd4
commit 3f85a09382
11 changed files with 1200 additions and 15 deletions

View File

@@ -39,7 +39,10 @@ Modules</h3>
<td>RevPiModIO Modul fuer die Verwaltung der IOs.</td>
</tr><tr>
<td><a style="color:#0000FF" href="revpimodio2.modio.html">modio</a></td>
<td>RevPiModIO Hauptklasse.</td>
<td>RevPiModIO Hauptklasse fuer piControl0 Zugriff.</td>
</tr><tr>
<td><a style="color:#0000FF" href="revpimodio2.netio.html">netio</a></td>
<td>RevPiModIO Hauptklasse fuer Netzwerkzugriff.</td>
</tr><tr>
<td><a style="color:#0000FF" href="revpimodio2.summary.html">summary</a></td>
<td>Bildet die Summary-Sektion von piCtory ab.</td>

View File

@@ -7,7 +7,7 @@
<h1 style="background-color:#FFFFFF;color:#0000FF">
revpimodio2.modio</h1>
<p>
RevPiModIO Hauptklasse.
RevPiModIO Hauptklasse fuer piControl0 Zugriff.
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Global Attributes</h3>
@@ -19,7 +19,7 @@ Classes</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#RevPiModIO">RevPiModIO</a></td>
<td>Klasse fuer die Verwaltung aller piCtory Informationen.</td>
<td>Klasse fuer die Verwaltung der piCtory Konfiguration.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiModIODriver">RevPiModIODriver</a></td>
<td>Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen.</td>
@@ -37,7 +37,7 @@ Functions</h3>
<a NAME="RevPiModIO" ID="RevPiModIO"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiModIO</h2>
<p>
Klasse fuer die Verwaltung aller piCtory Informationen.
Klasse fuer die Verwaltung der piCtory Konfiguration.
</p><p>
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des

543
doc/revpimodio2.netio.html Normal file
View File

@@ -0,0 +1,543 @@
<!DOCTYPE html>
<html><head>
<title>revpimodio2.netio</title>
<meta charset="UTF-8">
</head>
<body style="background-color:#FFFFFF;color:#000000"><a NAME="top" ID="top"></a>
<h1 style="background-color:#FFFFFF;color:#0000FF">
revpimodio2.netio</h1>
<p>
RevPiModIO Hauptklasse fuer Netzwerkzugriff.
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Global Attributes</h3>
<table>
<tr><td>_sysdeldirty</td></tr><tr><td>_sysexit</td></tr><tr><td>_sysflush</td></tr><tr><td>_syspictory</td></tr><tr><td>_syssync</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Classes</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#NetFH">NetFH</a></td>
<td>Netzwerk File Handler fuer das Prozessabbild.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIO">RevPiNetIO</a></td>
<td>Klasse fuer die Verwaltung der piCtory Konfiguration ueber das Netzwerk.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIODriver">RevPiNetIODriver</a></td>
<td>Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIOSelected">RevPiNetIOSelected</a></td>
<td>Klasse fuer die Verwaltung einzelner Devices aus piCtory.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Functions</h3>
<table>
<tr><td>None</td></tr>
</table>
<hr /><hr />
<a NAME="NetFH" ID="NetFH"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">NetFH</h2>
<p>
Netzwerk File Handler fuer das Prozessabbild.
</p><p>
Dieses FileObject-like Object verwaltet das Lesen und Schriben des
Prozessabbilds ueber das Netzwerk. Ein entfernter Revolution Pi kann
so gesteuert werden.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
Thread
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>closed</td></tr><tr><td>name</td></tr><tr><td>timeout</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><a style="color:#0000FF" href="#NetFH.__init__">NetFH</a></td>
<td>Init NetFH-class.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.__del__">__del__</a></td>
<td>NetworkFileHandler beenden.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH._connect">_connect</a></td>
<td>Stellt die Verbindung zu einem RevPiSlave her.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.clear_dirtybytes">clear_dirtybytes</a></td>
<td>Entfernt die konfigurierten Dirtybytes vom RevPi Slave.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.close">close</a></td>
<td>Verbindung trennen.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.flush">flush</a></td>
<td>Schreibpuffer senden.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.get_closed">get_closed</a></td>
<td>Pruefen ob Verbindung geschlossen ist.</td>
</tr><tr>
<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_timeout">get_timeout</a></td>
<td>Gibt aktuellen Timeout zurueck.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.read">read</a></td>
<td>Daten ueber das Netzwerk lesen.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.readpictory">readpictory</a></td>
<td>Ruft die piCtory Konfiguration ab.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.run">run</a></td>
<td>Handler fuer Synchronisierung.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.seek">seek</a></td>
<td>Springt an angegebene Position.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.set_dirtybytes">set_dirtybytes</a></td>
<td>Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.set_timeout">set_timeout</a></td>
<td>Setzt Timeoutwert fuer Verbindung.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.tell">tell</a></td>
<td>Gibt aktuelle Position zurueck.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#NetFH.write">write</a></td>
<td>Daten ueber das Netzwerk schreiben.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="NetFH.__init__" ID="NetFH.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH (Constructor)</h3>
<b>NetFH</b>(<i>address, timeout=500</i>)
<p>
Init NetFH-class.
</p><a NAME="NetFH.__del__" ID="NetFH.__del__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.__del__</h3>
<b>__del__</b>(<i></i>)
<p>
NetworkFileHandler beenden.
</p><a NAME="NetFH._connect" ID="NetFH._connect"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH._connect</h3>
<b>_connect</b>(<i></i>)
<p>
Stellt die Verbindung zu einem RevPiSlave her.
</p><a NAME="NetFH.clear_dirtybytes" ID="NetFH.clear_dirtybytes"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.clear_dirtybytes</h3>
<b>clear_dirtybytes</b>(<i>position=None</i>)
<p>
Entfernt die konfigurierten Dirtybytes vom RevPi Slave.
</p><dl>
<dt><i>position</i></dt>
<dd>
Startposition der Dirtybytes
</dd>
</dl><a NAME="NetFH.close" ID="NetFH.close"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.close</h3>
<b>close</b>(<i></i>)
<p>
Verbindung trennen.
</p><a NAME="NetFH.flush" ID="NetFH.flush"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.flush</h3>
<b>flush</b>(<i></i>)
<p>
Schreibpuffer senden.
</p><a NAME="NetFH.get_closed" ID="NetFH.get_closed"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.get_closed</h3>
<b>get_closed</b>(<i></i>)
<p>
Pruefen ob Verbindung geschlossen ist.
</p><dl>
<dt>Returns:</dt>
<dd>
True, wenn Verbindung geschlossen ist
</dd>
</dl><a NAME="NetFH.get_name" ID="NetFH.get_name"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.get_name</h3>
<b>get_name</b>(<i></i>)
<p>
Verbindugnsnamen zurueckgeben.
</p><dl>
<dt>Returns:</dt>
<dd>
<class 'str'> IP:PORT
</dd>
</dl><a NAME="NetFH.get_timeout" ID="NetFH.get_timeout"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.get_timeout</h3>
<b>get_timeout</b>(<i></i>)
<p>
Gibt aktuellen Timeout zurueck.
</p><dl>
<dt>Returns:</dt>
<dd>
<class 'int'> in Millisekunden
</dd>
</dl><a NAME="NetFH.read" ID="NetFH.read"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.read</h3>
<b>read</b>(<i>length</i>)
<p>
Daten ueber das Netzwerk lesen.
</p><dl>
<dt><i>length</i></dt>
<dd>
Anzahl der Bytes
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
Gelesene <class 'bytes'>
</dd>
</dl><a NAME="NetFH.readpictory" ID="NetFH.readpictory"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.readpictory</h3>
<b>readpictory</b>(<i></i>)
<p>
Ruft die piCtory Konfiguration ab.
</p><dl>
<dt>Returns:</dt>
<dd>
<class 'bytes'> piCtory Datei
</dd>
</dl><a NAME="NetFH.run" ID="NetFH.run"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.run</h3>
<b>run</b>(<i></i>)
<p>
Handler fuer Synchronisierung.
</p><a NAME="NetFH.seek" ID="NetFH.seek"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.seek</h3>
<b>seek</b>(<i>position</i>)
<p>
Springt an angegebene Position.
</p><dl>
<dt><i>position</i></dt>
<dd>
An diese Position springen
</dd>
</dl><a NAME="NetFH.set_dirtybytes" ID="NetFH.set_dirtybytes"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.set_dirtybytes</h3>
<b>set_dirtybytes</b>(<i>position, dirtybytes</i>)
<p>
Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.
</p><dl>
<dt><i>positon</i></dt>
<dd>
Startposition zum Schreiben
</dd><dt><i>dirtybytes</i></dt>
<dd>
<class 'bytes'> die geschrieben werden sollen
</dd>
</dl><a NAME="NetFH.set_timeout" ID="NetFH.set_timeout"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.set_timeout</h3>
<b>set_timeout</b>(<i>value</i>)
<p>
Setzt Timeoutwert fuer Verbindung.
</p><dl>
<dt><i>value</i></dt>
<dd>
Timeout in Millisekunden
</dd>
</dl><a NAME="NetFH.tell" ID="NetFH.tell"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.tell</h3>
<b>tell</b>(<i></i>)
<p>
Gibt aktuelle Position zurueck.
</p><dl>
<dt>Returns:</dt>
<dd>
int aktuelle Position
</dd>
</dl><a NAME="NetFH.write" ID="NetFH.write"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
NetFH.write</h3>
<b>write</b>(<i>bytebuff</i>)
<p>
Daten ueber das Netzwerk schreiben.
</p><dl>
<dt><i>bytebuff</i></dt>
<dd>
Bytes zum schreiben
</dd>
</dl><dl>
<dt>Returns:</dt>
<dd>
<class 'int'> Anzahl geschriebener bytes
</dd>
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="RevPiNetIO" ID="RevPiNetIO"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiNetIO</h2>
<p>
Klasse fuer die Verwaltung der piCtory Konfiguration ueber das Netzwerk.
</p><p>
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des
Prozessabbilds und stellt sicher, dass die Daten synchron sind.
Sollten nur einzelne Devices gesteuert werden, verwendet man
RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit
Device Positionen oder Device Namen.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
_RevPiModIO
<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><a style="color:#0000FF" href="#RevPiNetIO.__init__">RevPiNetIO</a></td>
<td>Instantiiert die Grundfunktionen.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIO._create_myfh">_create_myfh</a></td>
<td>Erstellt NetworkFileObject.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#RevPiNetIO.get_jconfigrsc">get_jconfigrsc</a></td>
<td>Laed die piCotry Konfiguration und erstellt ein <class 'dict'>.</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>
<td><a style="color:#0000FF" href="#RevPiNetIO.net_setdefaultvalues">net_setdefaultvalues</a></td>
<td>Konfiguriert den PLC Slave mit den piCtory Defaultwerten.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="RevPiNetIO.__init__" ID="RevPiNetIO.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO (Constructor)</h3>
<b>RevPiNetIO</b>(<i>address, autorefresh=False, monitoring=False, syncoutputs=True, simulator=False</i>)
<p>
Instantiiert die Grundfunktionen.
</p><dl>
<dt><i>address:</i></dt>
<dd>
IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
</dd><dt><i>autorefresh</i></dt>
<dd>
Wenn True, alle Devices zu autorefresh hinzufuegen
</dd><dt><i>monitoring</i></dt>
<dd>
In- und Outputs werden gelesen, niemals geschrieben
</dd><dt><i>syncoutputs</i></dt>
<dd>
Aktuell gesetzte Outputs vom Prozessabbild einlesen
</dd><dt><i>simulator</i></dt>
<dd>
Laed das Modul als Simulator und vertauscht IOs
</dd>
</dl><a NAME="RevPiNetIO._create_myfh" ID="RevPiNetIO._create_myfh"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO._create_myfh</h3>
<b>_create_myfh</b>(<i></i>)
<p>
Erstellt NetworkFileObject.
return FileObject
</p><a NAME="RevPiNetIO.get_jconfigrsc" ID="RevPiNetIO.get_jconfigrsc"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO.get_jconfigrsc</h3>
<b>get_jconfigrsc</b>(<i></i>)
<p>
Laed die piCotry Konfiguration und erstellt ein <class 'dict'>.
</p><dl>
<dt>Returns:</dt>
<dd>
<class 'dict'> der piCtory Konfiguration
</dd>
</dl><a NAME="RevPiNetIO.net_cleardefaultvalues" ID="RevPiNetIO.net_cleardefaultvalues"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO.net_cleardefaultvalues</h3>
<b>net_cleardefaultvalues</b>(<i>device=None</i>)
<p>
Loescht Defaultwerte vom PLC Slave.
</p><dl>
<dt><i>device</i></dt>
<dd>
nur auf einzelnes Device anwenden, sonst auf Alle
</dd>
</dl><a NAME="RevPiNetIO.net_setdefaultvalues" ID="RevPiNetIO.net_setdefaultvalues"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIO.net_setdefaultvalues</h3>
<b>net_setdefaultvalues</b>(<i>device=None</i>)
<p>
Konfiguriert den PLC Slave mit den piCtory Defaultwerten.
</p><dl>
<dt><i>device</i></dt>
<dd>
nur auf einzelnes Device anwenden, sonst auf Alle
</dd>
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="RevPiNetIODriver" ID="RevPiNetIODriver"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiNetIODriver</h2>
<p>
Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen.
</p><p>
Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO
verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs
verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen
dann ueber logiCAD an den Devices abgerufen werden.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
RevPiNetIOSelected
<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><a style="color:#0000FF" href="#RevPiNetIODriver.__init__">RevPiNetIODriver</a></td>
<td>Instantiiert die Grundfunktionen.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="RevPiNetIODriver.__init__" ID="RevPiNetIODriver.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIODriver (Constructor)</h3>
<b>RevPiNetIODriver</b>(<i>address, virtdev, autorefresh=False, monitoring=False, syncoutputs=True</i>)
<p>
Instantiiert die Grundfunktionen.
</p><p>
Parameter 'monitoring' und 'simulator' stehen hier nicht zur
Verfuegung, da diese automatisch gesetzt werden.
</p><dl>
<dt><i>address:</i></dt>
<dd>
IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
</dd><dt><i>virtdev</i></dt>
<dd>
Virtuelles Device oder mehrere als <class 'list'>
</dd>
</dl><dl>
<dt><b>See Also:</b></dt>
<dd>
<a style="color:#0000FF" href="#RevPiModIO.__init__">RevPiModIO.__init__(...)</a>
</dd>
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="RevPiNetIOSelected" ID="RevPiNetIOSelected"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">RevPiNetIOSelected</h2>
<p>
Klasse fuer die Verwaltung einzelner Devices aus piCtory.
</p><p>
Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration
und bilded sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des
Adressbereichs im Prozessabbild an dem sich die angegebenen Devices
befinden und stellt sicher, dass die Daten synchron sind.
</p><p>
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
RevPiNetIO
<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><a style="color:#0000FF" href="#RevPiNetIOSelected.__init__">RevPiNetIOSelected</a></td>
<td>Instantiiert nur fuer angegebene Devices die Grundfunktionen.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="RevPiNetIOSelected.__init__" ID="RevPiNetIOSelected.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiNetIOSelected (Constructor)</h3>
<b>RevPiNetIOSelected</b>(<i>address, deviceselection, autorefresh=False, monitoring=False, syncoutputs=True, simulator=False</i>)
<p>
Instantiiert nur fuer angegebene Devices die Grundfunktionen.
</p><p>
Der Parameter deviceselection kann eine einzelne
Device Position / einzelner Device Name sein oder eine Liste mit
mehreren Positionen / Namen
</p><dl>
<dt><i>address:</i></dt>
<dd>
IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
</dd><dt><i>deviceselection</i></dt>
<dd>
Positionsnummer oder Devicename
</dd>
</dl><dl>
<dt><b>See Also:</b></dt>
<dd>
<a style="color:#0000FF" href="#RevPiNetIO.__init__">RevPiNetIO.__init__(...)</a>
</dd>
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr />
</body></html>

View File

@@ -161,4 +161,35 @@ revpimodio2.modio.RevPiModIO.writeprocimg?4(device=None)
revpimodio2.modio.RevPiModIO?1(autorefresh=False, monitoring=False, syncoutputs=True, procimg=None, configrsc=None, simulator=False)
revpimodio2.modio.RevPiModIODriver?1(virtdev, autorefresh=False, monitoring=False, syncoutputs=True, procimg=None, configrsc=None)
revpimodio2.modio.RevPiModIOSelected?1(deviceselection, autorefresh=False, monitoring=False, syncoutputs=True, procimg=None, configrsc=None, simulator=False)
revpimodio2.netio.NetFH._connect?5()
revpimodio2.netio.NetFH.clear_dirtybytes?4(position=None)
revpimodio2.netio.NetFH.close?4()
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_timeout?4()
revpimodio2.netio.NetFH.name?7
revpimodio2.netio.NetFH.read?4(length)
revpimodio2.netio.NetFH.readpictory?4()
revpimodio2.netio.NetFH.run?4()
revpimodio2.netio.NetFH.seek?4(position)
revpimodio2.netio.NetFH.set_dirtybytes?4(position, dirtybytes)
revpimodio2.netio.NetFH.set_timeout?4(value)
revpimodio2.netio.NetFH.tell?4()
revpimodio2.netio.NetFH.timeout?7
revpimodio2.netio.NetFH.write?4(bytebuff)
revpimodio2.netio.NetFH?1(address, timeout=500)
revpimodio2.netio.RevPiNetIO._create_myfh?5()
revpimodio2.netio.RevPiNetIO.get_jconfigrsc?4()
revpimodio2.netio.RevPiNetIO.net_cleardefaultvalues?4(device=None)
revpimodio2.netio.RevPiNetIO.net_setdefaultvalues?4(device=None)
revpimodio2.netio.RevPiNetIO?1(address, autorefresh=False, monitoring=False, syncoutputs=True, simulator=False)
revpimodio2.netio.RevPiNetIODriver?1(address, virtdev, autorefresh=False, monitoring=False, syncoutputs=True)
revpimodio2.netio.RevPiNetIOSelected?1(address, deviceselection, autorefresh=False, monitoring=False, syncoutputs=True, simulator=False)
revpimodio2.netio._sysdeldirty?8
revpimodio2.netio._sysexit?8
revpimodio2.netio._sysflush?8
revpimodio2.netio._syspictory?8
revpimodio2.netio._syssync?8
revpimodio2.summary.Summary?1(summary)

View File

@@ -2,8 +2,12 @@ Core Device
EventCallback Thread
Gateway Device
IntIO IOBase
NetFH Thread
ProcimgWriter Thread
RevPiModIODriver RevPiModIOSelected
RevPiModIOSelected RevPiModIO
RevPiNetIO _RevPiModIO
RevPiNetIODriver RevPiNetIOSelected
RevPiNetIOSelected RevPiNetIO
StructIO IOBase
Virtual Gateway

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-5.1.dtd">
<!-- eric project file for project revpimodio2 -->
<!-- Saved: 2017-09-11, 10:48:24 -->
<!-- Saved: 2017-09-11, 13:27:05 -->
<!-- Copyright (C) 2017 Sven Sager, akira@narux.de -->
<Project version="5.1">
<Language>en_US</Language>
@@ -9,7 +9,7 @@
<ProgLanguage mixed="0">Python3</ProgLanguage>
<ProjectType>Console</ProjectType>
<Description>Das Modul stellt alle Devices und IOs aus der piCtory Konfiguration in Python3 zur Verfügung. Es ermöglicht den direkten Zugriff auf die Werte über deren vergebenen Namen. Lese- und Schreibaktionen mit dem Prozessabbild werden von dem Modul selbst verwaltet, ohne dass sich der Programmierer um Offsets und Adressen kümmern muss. Für die Gatewaymodule wie ModbusTCP oder Profinet sind eigene 'Inputs' und 'Outputs' über einen bestimmten Adressbereich definierbar. Auf diese IOs kann mit Python3 über den Namen direkt auf die Werte zugegriffen werden.</Description>
<Version>2.0.4</Version>
<Version>2.1.0</Version>
<Author>Sven Sager</Author>
<Email>akira@narux.de</Email>
<Eol index="1"/>
@@ -31,6 +31,7 @@
<Source>test/web_virtdevdriver.py</Source>
<Source>test/web_benniesrun.py</Source>
<Source>test/web_benniesrunxxl.py</Source>
<Source>revpimodio2/netio.py</Source>
</Sources>
<Forms/>
<Translations/>

View File

@@ -18,13 +18,14 @@ fuehrt das Modul bei Datenaenderung aus.
"""
import warnings
from .modio import *
__all__ = ["RevPiModIO", "RevPiModIOSelected", "RevPiModIODriver"]
__all__ = [
"RevPiModIO", "RevPiModIOSelected", "RevPiModIODriver",
"RevPiNetIO", "RevPiNetIOSelected", "RevPiNetIODriver"
]
__author__ = "Sven Sager <akira@revpimodio.org>"
__name__ = "revpimodio2"
__package__ = "revpimodio2"
__version__ = "2.0.4"
__version__ = "2.1.0"
# Global package values
OFF = 0
@@ -60,3 +61,7 @@ def consttostr(value):
return "BOTH"
else:
return ""
# Benötigte Klassen importieren
from .modio import RevPiModIO, RevPiModIOSelected, RevPiModIODriver
from .netio import RevPiNetIO, RevPiNetIOSelected, RevPiNetIODriver

View File

@@ -8,7 +8,7 @@
"""RevPiModIO Modul fuer die Verwaltung der IOs."""
import struct
from threading import Event
from .__init__ import RISING, FALLING, BOTH, consttostr
from revpimodio2 import RISING, FALLING, BOTH, consttostr
class Type(object):

View File

@@ -5,7 +5,7 @@
# Webpage: https://revpimodio.org/
# (c) Sven Sager, License: LGPLv3
#
"""RevPiModIO Hauptklasse."""
"""RevPiModIO Hauptklasse fuer piControl0 Zugriff."""
import warnings
from json import load as jload
from math import ceil
@@ -18,12 +18,12 @@ from . import device as devicemodule
from . import helper as helpermodule
from . import summary as summarymodule
from .io import IOList
from .__init__ import RISING, FALLING, BOTH
from revpimodio2 import RISING, FALLING, BOTH
class RevPiModIO(object):
"""Klasse fuer die Verwaltung aller piCtory Informationen.
"""Klasse fuer die Verwaltung der piCtory Konfiguration.
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des

598
revpimodio2/netio.py Normal file
View File

@@ -0,0 +1,598 @@
# -*- coding: utf-8 -*-
#
# python3-RevPiModIO
#
# Webpage: https://revpimodio.org/
# (c) Sven Sager, License: LGPLv3
#
"""RevPiModIO Hauptklasse fuer Netzwerkzugriff."""
import socket
from json import loads as jloads
from threading import Thread, Event, Lock
from .device import Device
from .modio import RevPiModIO as _RevPiModIO
# Synchronisierungsbefehl
_syssync = b'\x01\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
# Disconnectbefehl
_sysexit = b'\x01EX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
# DirtyBytes von Server entfernen
_sysdeldirty = b'\x01EY\x00\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x17'
# piCtory Konfiguration laden
_syspictory = b'\x01PI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
# Übertragene Bytes schreiben
_sysflush = b'\x01SD\x00\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x17'
class NetFH(Thread):
"""Netzwerk File Handler fuer das Prozessabbild.
Dieses FileObject-like Object verwaltet das Lesen und Schriben des
Prozessabbilds ueber das Netzwerk. Ein entfernter Revolution Pi kann
so gesteuert werden.
"""
def __init__(self, address, timeout=500):
"""Init NetFH-class."""
super().__init__()
self.daemon = True
self.__by_buff = b''
self.__int_buff = 0
self.__dictdirty = {}
self.__flusherr = False
self.__sockact = False
self.__sockerr = Event()
self.__sockend = False
self.__socklock = Lock()
self.__timeout = timeout / 1000
self.__trigger = False
self.__waitsync = self.__timeout / 2
socket.setdefaulttimeout(self.__timeout)
# Verbindung herstellen
self._address = address
self._slavesock = None
self._connect()
if self._slavesock is None:
raise FileNotFoundError("can not connect to revpi slave")
# NetFH konfigurieren
self.__position = 0
self.start()
def __del__(self):
"""NetworkFileHandler beenden."""
self.close()
def _connect(self):
"""Stellt die Verbindung zu einem RevPiSlave her."""
# Neuen Socket aufbauen
so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
so.connect(self._address)
except:
pass
else:
# Alten Socket trennen
with self.__socklock:
if self._slavesock is not None:
self._slavesock.close()
self._slavesock = so
self.__sockerr.clear()
# Timeout setzen
self.set_timeout(int(self.__timeout * 1000))
# DirtyBytes übertragen
for pos in self.__dictdirty:
self.set_dirtybytes(pos, self.__dictdirty[pos])
def clear_dirtybytes(self, position=None):
"""Entfernt die konfigurierten Dirtybytes vom RevPi Slave.
@param position Startposition der Dirtybytes"""
if self.__sockend:
raise ValueError("I/O operation on closed file")
with self.__socklock:
if position is None:
self._slavesock.sendall(_sysdeldirty)
else:
self._slavesock.sendall(
b'\x01EY' +
position.to_bytes(length=2, byteorder="little") +
b'\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x17'
)
check = self._slavesock.recv(1)
if check != b'\x1e':
self.__sockerr.set()
raise IOError("clear dirtybytes error on network")
# Daten bei Erfolg übernehmen
if position is None:
self.__dictdirty = {}
else:
del self.__dictdirty[position]
self.__trigger = True
def close(self):
"""Verbindung trennen."""
self.__sockend = True
self.__sockerr.set()
def flush(self):
"""Schreibpuffer senden."""
if self.__sockend:
raise ValueError("flush of closed file")
with self.__socklock:
self._slavesock.sendall(
self.__by_buff + _sysflush
)
# Rückmeldebyte auswerten
blockok = self._slavesock.recv(1)
if blockok != b'\x1e':
self.__sockerr.set()
self.__flusherr = True
raise IOError("flush error on network")
else:
self.__flusherr = False
# Puffer leeren
self.__int_buff = 0
self.__by_buff = b''
self.__trigger = True
def get_closed(self):
"""Pruefen ob Verbindung geschlossen ist.
@return True, wenn Verbindung geschlossen ist"""
return self.__sockend
def get_name(self):
"""Verbindugnsnamen zurueckgeben.
@return <class 'str'> IP:PORT"""
return "{}:{}".format(*self._address)
def get_timeout(self):
"""Gibt aktuellen Timeout zurueck.
@return <class 'int'> in Millisekunden"""
return int(self.__timeout * 1000)
def read(self, length):
"""Daten ueber das Netzwerk lesen.
@param length Anzahl der Bytes
@return Gelesene <class 'bytes'>"""
if self.__sockend:
raise ValueError("read of closed file")
with self.__socklock:
self._slavesock.send(
b'\x01DA' +
self.__position.to_bytes(length=2, byteorder="little") +
length.to_bytes(length=2, byteorder="little") +
b'\x00\x00\x00\x00\x00\x00\x00\x00\x17'
)
bytesbuff = bytearray()
while not self.__sockend and len(bytesbuff) < length:
rbytes = self._slavesock.recv(1024)
if rbytes == b'':
self.__sockerr.set()
raise IOError("read error on network")
bytesbuff += rbytes
self.__position += length
self.__trigger = True
return bytes(bytesbuff)
def readpictory(self):
"""Ruft die piCtory Konfiguration ab.
@return <class 'bytes'> piCtory Datei"""
if self.__sockend:
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)
byte_buff += data
if data.find(b'\x04') >= 0:
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:
# Auf Fehlermeldung warten
if self.__sockerr.wait(self.__waitsync):
if not self.__sockend:
# Im Fehlerfall neu verbinden
self._connect()
elif not self.__sockend:
# Kein Fehler aufgetreten, sync durchführen wenn socket frei
if not self.__trigger and \
self.__socklock.acquire(blocking=False):
try:
self._slavesock.send(_syssync)
data = self._slavesock.recv(2)
except IOError:
print("ioerror in run()")
self.__sockerr.set()
else:
if data != b'\x06\x16':
print("data error in run():", str(data))
self.__sockerr.set()
self.__socklock.release()
self.__trigger = False
# Vom Socket trennen
with self.__socklock:
try:
if self.__sockend:
self._slavesock.send(_sysexit)
else:
self._slavesock.shutdown(socket.SHUT_RDWR)
except:
pass
self._slavesock.close()
def seek(self, position):
"""Springt an angegebene Position.
@param position An diese Position springen"""
if self.__sockend:
raise ValueError("seek of closed file")
self.__position = int(position)
def set_dirtybytes(self, position, dirtybytes):
"""Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.
@param positon Startposition zum Schreiben
@param dirtybytes <class 'bytes'> die geschrieben werden sollen"""
if self.__sockend:
raise ValueError("I/O operation on closed file")
with self.__socklock:
self._slavesock.sendall(
b'\x01EY' +
position.to_bytes(length=2, byteorder="little") +
len(dirtybytes).to_bytes(length=2, byteorder="little") +
b'\x00\x00\x00\x00\x00\x00\x00\x00\x17' +
dirtybytes
)
check = self._slavesock.recv(1)
if check != b'\x1e':
self.__sockerr.set()
raise IOError("set dirtybytes error on network")
# Daten erfolgreich übernehmen
self.__dictdirty[position] = dirtybytes
self.__trigger = True
def set_timeout(self, value):
"""Setzt Timeoutwert fuer Verbindung.
@param value Timeout in Millisekunden"""
if self.__sockend:
raise ValueError("I/O operation on closed file")
if type(value) == int and (0 < value <= 65535):
self.__timeout = value / 1000
self.__waitsync = self.__timeout / 2 - 0.05
# Timeouts in Sockets übernehmen
socket.setdefaulttimeout(self.__timeout)
self._slavesock.settimeout(self.__timeout)
with self.__socklock:
self._slavesock.send(
b'\x01CF' +
value.to_bytes(length=2, byteorder="little") +
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
)
check = self._slavesock.recv(1)
if check != b'\x1e':
self.__sockerr.set()
raise IOError("set timeout error on network")
self.__trigger = True
else:
raise ValueError("value must between 0 and 65535 milliseconds")
def tell(self):
"""Gibt aktuelle Position zurueck.
@return int aktuelle Position"""
if self.__sockend:
raise ValueError("I/O operation on closed file")
return self.__position
def write(self, bytebuff):
"""Daten ueber das Netzwerk schreiben.
@param bytebuff Bytes zum schreiben
@return <class 'int'> Anzahl geschriebener bytes"""
if self.__sockend:
raise ValueError("write to closed file")
if self.__flusherr:
raise IOError("I/O error since last flush")
with self.__socklock:
self.__int_buff += 1
# Datenblöcke mit Group Seperator in Puffer ablegen
self.__by_buff += b'\x01SD' + \
self.__position.to_bytes(length=2, byteorder="little") + \
len(bytebuff).to_bytes(length=2, byteorder="little") + \
b'\x1d\x00\x00\x00\x00\x00\x00\x00\x17' + \
bytebuff
# TODO: Bufferlänge und dann flushen?
return len(bytebuff)
closed = property(get_closed)
name = property(get_name)
timeout = property(get_timeout, set_timeout)
class RevPiNetIO(_RevPiModIO):
"""Klasse fuer die Verwaltung der piCtory Konfiguration ueber das Netzwerk.
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des
Prozessabbilds und stellt sicher, dass die Daten synchron sind.
Sollten nur einzelne Devices gesteuert werden, verwendet man
RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit
Device Positionen oder Device Namen.
"""
def __init__(
self, address, autorefresh=False, monitoring=False,
syncoutputs=True, simulator=False):
"""Instantiiert die Grundfunktionen.
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
@param autorefresh Wenn True, alle Devices zu autorefresh hinzufuegen
@param monitoring In- und Outputs werden gelesen, niemals geschrieben
@param syncoutputs Aktuell gesetzte Outputs vom Prozessabbild einlesen
@param simulator Laed das Modul als Simulator und vertauscht IOs
"""
# Adresse verarbeiten
if type(address) == str:
# TODO: IP-Adresse prüfen
self._address = (address, 55234)
elif type(address) == tuple:
if len(address) == 2 \
and type(address[0]) == str \
and type(address[1]) == int:
# Werte prüfen
# TODO: IP-Adresse prüfen
if 0 < address[1] <= 65535:
raise ValueError("port number out of range 1 - 65535")
self._address = address
else:
raise ValueError(
"address tuple must be (<class 'str'>, <class 'int'>)"
)
# Vererben
super().__init__(
autorefresh,
monitoring,
syncoutputs,
"{}:{}".format(*self._address),
None,
simulator
)
# Netzwerkfilehandler anlegen
self._myfh = self._create_myfh()
# Modul konfigurieren
self._configure(self.get_jconfigrsc())
def _create_myfh(self):
"""Erstellt NetworkFileObject.
return FileObject"""
self._buffedwrite = True
return NetFH(self._address)
def get_jconfigrsc(self):
"""Laed die piCotry Konfiguration und erstellt ein <class 'dict'>.
@return <class 'dict'> der piCtory Konfiguration"""
mynh = NetFH(self._address)
byte_buff = mynh.readpictory()
mynh.close()
return jloads(byte_buff.decode("utf-8"))
def net_cleardefaultvalues(self, device=None):
"""Loescht Defaultwerte vom PLC Slave.
@param device nur auf einzelnes Device anwenden, sonst auf Alle"""
if self.monitoring:
raise RuntimeError(
"can not send default values, while system is in "
"monitoring mode"
)
if device is None:
mylist = self.device
else:
dev = device if issubclass(type(device), Device) \
else self.device.__getitem__(device)
mylist = [dev]
for dev in mylist:
self._myfh.clear_dirtybytes(dev._offset + dev._slc_out.start)
def net_setdefaultvalues(self, device=None):
"""Konfiguriert den PLC Slave mit den piCtory Defaultwerten.
@param device nur auf einzelnes Device anwenden, sonst auf Alle"""
if self.monitoring:
raise RuntimeError(
"can not send default values, while system is in "
"monitoring mode"
)
if device is None:
mylist = self.device
else:
dev = device if issubclass(type(device), Device) \
else self.device.__getitem__(device)
mylist = [dev]
for dev in mylist:
dirtybytes = bytearray()
for lst_io in self.io[dev._slc_outoff]:
listlen = len(lst_io)
if listlen == 1:
# Byteorientierte Outputs direkt übernehmen
dirtybytes += lst_io[0]._defaultvalue
elif listlen > 1:
# Bitorientierte Outputs in ein Byte zusammenfassen
int_byte = 0
lstbyte = lst_io.copy()
lstbyte.reverse()
for bitio in lstbyte:
# Von hinten die bits nach vorne schieben
int_byte <<= 1
if bitio is not None:
int_byte += 1 if bitio._defaultvalue else 0
# Errechneten Int-Wert in ein Byte umwandeln
dirtybytes += \
int_byte.to_bytes(length=1, byteorder="little")
# Dirtybytes an PLC Slave senden
self._myfh.set_dirtybytes(
dev._offset + dev._slc_out.start, dirtybytes
)
class RevPiNetIOSelected(RevPiNetIO):
"""Klasse fuer die Verwaltung einzelner Devices aus piCtory.
Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration
und bilded sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des
Adressbereichs im Prozessabbild an dem sich die angegebenen Devices
befinden und stellt sicher, dass die Daten synchron sind.
"""
def __init__(
self, address, deviceselection, autorefresh=False,
monitoring=False, syncoutputs=True, simulator=False):
"""Instantiiert nur fuer angegebene Devices die Grundfunktionen.
Der Parameter deviceselection kann eine einzelne
Device Position / einzelner Device Name sein oder eine Liste mit
mehreren Positionen / Namen
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
@param deviceselection Positionsnummer oder Devicename
@see #RevPiNetIO.__init__ RevPiNetIO.__init__(...)
"""
super().__init__(
address, autorefresh, monitoring, syncoutputs, simulator
)
# Device liste erstellen
if type(deviceselection) == list:
for dev in deviceselection:
self._lst_devselect.append(dev)
else:
self._lst_devselect.append(deviceselection)
for vdev in self._lst_devselect:
if type(vdev) != int and type(vdev) != str:
raise ValueError(
"need device position as <class 'int'> or device name as "
"<class 'str'>"
)
self._configure(self.get_jconfigrsc())
if len(self.device) == 0:
if type(self) == RevPiNetIODriver:
raise RuntimeError(
"could not find any given VIRTUAL devices in config"
)
else:
raise RuntimeError(
"could not find any given devices in config"
)
elif len(self.device) != len(self._lst_devselect):
if type(self) == RevPiNetIODriver:
raise RuntimeError(
"could not find all given VIRTUAL devices in config"
)
else:
raise RuntimeError(
"could not find all given devices in config"
)
class RevPiNetIODriver(RevPiNetIOSelected):
"""Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen.
Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO
verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs
verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen
dann ueber logiCAD an den Devices abgerufen werden.
"""
def __init__(
self, address, virtdev, autorefresh=False, monitoring=False,
syncoutputs=True):
"""Instantiiert die Grundfunktionen.
Parameter 'monitoring' und 'simulator' stehen hier nicht zur
Verfuegung, da diese automatisch gesetzt werden.
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
@param virtdev Virtuelles Device oder mehrere als <class 'list'>
@see #RevPiModIO.__init__ RevPiModIO.__init__(...)
"""
# Parent mit monitoring=False und simulator=True laden
super().__init__(
address, virtdev, autorefresh, False, syncoutputs, True
)

View File

@@ -16,7 +16,7 @@ setup(
license="LGPLv3",
name="revpimodio2",
version="2.0.4",
version="2.1.0",
packages=["revpimodio2"],