Merge branch 'develop'

This commit is contained in:
2021-01-14 19:02:59 +01:00
10 changed files with 506 additions and 322 deletions

2
.idea/misc.xml generated
View File

@@ -3,7 +3,7 @@
<component name="JavaScriptSettings"> <component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" /> <option name="languageLevel" value="ES6" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser"> <component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>

2
.idea/revpimodio2.iml generated
View File

@@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.8" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="TestRunnerService"> <component name="TestRunnerService">

View File

@@ -16,13 +16,13 @@ __all__ = [
"RevPiModIO", "RevPiModIODriver", "RevPiModIOSelected", "run_plc", "RevPiModIO", "RevPiModIODriver", "RevPiModIOSelected", "run_plc",
"RevPiNetIO", "RevPiNetIODriver", "RevPiNetIOSelected", "RevPiNetIO", "RevPiNetIODriver", "RevPiNetIOSelected",
"Cycletools", "EventCallback", "Cycletools", "EventCallback",
"AIO", "DI", "DO", "DIO", "AIO", "COMPACT", "DI", "DO", "DIO",
] ]
__author__ = "Sven Sager <akira@revpimodio.org>" __author__ = "Sven Sager <akira@revpimodio.org>"
__copyright__ = "Copyright (C) 2020 Sven Sager" __copyright__ = "Copyright (C) 2020 Sven Sager"
__license__ = "LGPLv3" __license__ = "LGPLv3"
__name__ = "revpimodio2" __name__ = "revpimodio2"
__version__ = "2.5.3" __version__ = "2.5.3d"
# Global package values # Global package values
OFF = 0 OFF = 0
@@ -96,7 +96,7 @@ def consttostr(value) -> str:
# Benötigte Klassen importieren # Benötigte Klassen importieren
from .pictory import AIO, DI, DO, DIO from .pictory import AIO, COMPACT, DI, DO, DIO
from .helper import Cycletools, EventCallback from .helper import Cycletools, EventCallback
from .modio import RevPiModIO, RevPiModIODriver, RevPiModIOSelected, run_plc from .modio import RevPiModIO, RevPiModIODriver, RevPiModIOSelected, run_plc
from .netio import RevPiNetIO, RevPiNetIODriver, RevPiNetIOSelected from .netio import RevPiNetIO, RevPiNetIODriver, RevPiNetIOSelected

View File

@@ -125,10 +125,11 @@ class Device(object):
""" """
__slots__ = "__my_io_list", "_ba_devdata", "_ba_datacp", \ __slots__ = "__my_io_list", "_ba_devdata", "_ba_datacp", \
"_dict_events", "_filelock", "_length", "_modio", "_name", "_offset", \ "_dict_events", "_filelock", "_length", "_modio", "_name", \
"_position", "_producttype", "_selfupdate", "_slc_devoff", \ "_offset", "_position", "_producttype", "_selfupdate", \
"_slc_inp", "_slc_inpoff", "_slc_mem", "_slc_memoff", \ "_slc_devoff", "_slc_inp", "_slc_inpoff", "_slc_mem", \
"_slc_out", "_slc_outoff", "bmk", "catalognr", "comment", "extend", \ "_slc_memoff", "_slc_out", "_slc_outoff", "_shared_procimg", \
"bmk", "catalognr", "comment", "extend", \
"guid", "id", "inpvariant", "outvariant", "type" "guid", "id", "inpvariant", "outvariant", "type"
def __init__(self, parentmodio, dict_device, simulator=False): def __init__(self, parentmodio, dict_device, simulator=False):
@@ -146,6 +147,7 @@ class Device(object):
self._length = 0 self._length = 0
self.__my_io_list = [] self.__my_io_list = []
self._selfupdate = False self._selfupdate = False
self._shared_procimg = parentmodio._shared_procimg
# Wertzuweisung aus dict_device # Wertzuweisung aus dict_device
self._name = dict_device.get("name") self._name = dict_device.get("name")
@@ -500,6 +502,17 @@ class Device(object):
""" """
self._modio.setdefaultvalues(self) self._modio.setdefaultvalues(self)
def shared_procimg(self, activate: bool) -> None:
"""
Activate sharing of process image just for this device.
WARNING: All outputs will set immediately in process image on value
change. That is also inside the cycle loop!
:param activate: Set True to activate process image sharing
"""
self._shared_procimg = True if activate else False
def syncoutputs(self) -> bool: def syncoutputs(self) -> bool:
""" """
Lesen aller Outputs im Prozessabbild fuer dieses Device. Lesen aller Outputs im Prozessabbild fuer dieses Device.
@@ -593,11 +606,13 @@ class Core(Base):
exp_a1red = lst_led[1].export exp_a1red = lst_led[1].export
exp_a2green = lst_led[2].export exp_a2green = lst_led[2].export
exp_a2red = lst_led[3].export exp_a2red = lst_led[3].export
# exp_wd = lst_led[7].export
else: else:
exp_a1green = lst_led[0].export exp_a1green = lst_led[0].export
exp_a1red = exp_a1green exp_a1red = exp_a1green
exp_a2green = exp_a1green exp_a2green = exp_a1green
exp_a2red = exp_a1green exp_a2red = exp_a1green
# exp_wd = exp_a1green
# Echte IOs erzeugen # Echte IOs erzeugen
self.a1green = IOBase(self, [ self.a1green = IOBase(self, [
@@ -673,13 +688,8 @@ class Core(Base):
:param value: 0=aus, 1=gruen, 2=rot :param value: 0=aus, 1=gruen, 2=rot
""" """
if 0 <= value <= 3: if 0 <= value <= 3:
proc_value = self._ba_devdata[self._slc_led.start] self.a1green(bool(value & 1))
proc_value_calc = proc_value & 3 self.a1red(bool(value & 2))
if proc_value_calc == value:
return
# Set new value
self._ba_devdata[self._slc_led.start] = \
proc_value - proc_value_calc + value
else: else:
raise ValueError("led status must be between 0 and 3") raise ValueError("led status must be between 0 and 3")
@@ -690,14 +700,8 @@ class Core(Base):
:param value: 0=aus, 1=gruen, 2=rot :param value: 0=aus, 1=gruen, 2=rot
""" """
if 0 <= value <= 3: if 0 <= value <= 3:
value <<= 2 self.a2green(bool(value & 1))
proc_value = self._ba_devdata[self._slc_led.start] self.a2red(bool(value & 2))
proc_value_calc = proc_value & 12
if proc_value_calc == value:
return
# Set new value
self._ba_devdata[self._slc_led.start] = \
proc_value - proc_value_calc + value
else: else:
raise ValueError("led status must be between 0 and 3") raise ValueError("led status must be between 0 and 3")
@@ -907,12 +911,10 @@ class Connect(Core):
exp_a3green = lst_led[4].export exp_a3green = lst_led[4].export
exp_a3red = lst_led[5].export exp_a3red = lst_led[5].export
exp_x2out = lst_led[6].export exp_x2out = lst_led[6].export
exp_wd = lst_led[7].export
else: else:
exp_a3green = lst_led[0].export exp_a3green = lst_led[0].export
exp_a3red = exp_a3green exp_a3red = exp_a3green
exp_x2out = exp_a3green exp_x2out = exp_a3green
exp_wd = exp_a3green
lst_status = lst_myios[self._slc_statusbyte.start] lst_status = lst_myios[self._slc_statusbyte.start]
if len(lst_status) == 8: if len(lst_status) == 8:
exp_x2in = lst_status[6].export exp_x2in = lst_status[6].export
@@ -963,14 +965,8 @@ class Connect(Core):
:param: value 0=aus, 1=gruen, 2=rot :param: value 0=aus, 1=gruen, 2=rot
""" """
if 0 <= value <= 3: if 0 <= value <= 3:
value <<= 4 self.a3green(bool(value & 1))
proc_value = self._ba_devdata[self._slc_led.start] self.a3red(bool(value & 2))
proc_value_calc = proc_value & 48
if proc_value_calc == value:
return
# Set new value
self._ba_devdata[self._slc_led.start] = \
proc_value - proc_value_calc + value
else: else:
raise ValueError("led status must be between 0 and 3") raise ValueError("led status must be between 0 and 3")
@@ -1013,7 +1009,7 @@ class Connect(Core):
class Compact(Base): class Compact(Base):
""" """
Klasse fuer den RevPi Connect. Klasse fuer den RevPi Compact.
Stellt Funktionen fuer die LEDs zur Verfuegung. Auf IOs wird ueber das .io Stellt Funktionen fuer die LEDs zur Verfuegung. Auf IOs wird ueber das .io
Objekt zugegriffen. Objekt zugegriffen.
@@ -1025,7 +1021,7 @@ class Compact(Base):
def __setattr__(self, key, value): def __setattr__(self, key, value):
"""Verhindert Ueberschreibung der LEDs.""" """Verhindert Ueberschreibung der LEDs."""
if hasattr(self, key) and key in ( if hasattr(self, key) and key in (
"a1green", "a1red", "a2green", "a2red"): "a1green", "a1red", "a2green", "a2red", "wd"):
raise AttributeError( raise AttributeError(
"direct assignment is not supported - use .value Attribute" "direct assignment is not supported - use .value Attribute"
) )
@@ -1102,13 +1098,8 @@ class Compact(Base):
:param value: 0=aus, 1=gruen, 2=rot :param value: 0=aus, 1=gruen, 2=rot
""" """
if 0 <= value <= 3: if 0 <= value <= 3:
proc_value = self._ba_devdata[self._slc_led.start] self.a1green(bool(value & 1))
proc_value_calc = proc_value & 3 self.a1red(bool(value & 2))
if proc_value_calc == value:
return
# Set new value
self._ba_devdata[self._slc_led.start] = \
proc_value - proc_value_calc + value
else: else:
raise ValueError("led status must be between 0 and 3") raise ValueError("led status must be between 0 and 3")
@@ -1119,14 +1110,8 @@ class Compact(Base):
:param value: 0=aus, 1=gruen, 2=rot :param value: 0=aus, 1=gruen, 2=rot
""" """
if 0 <= value <= 3: if 0 <= value <= 3:
value <<= 2 self.a2green(bool(value & 1))
proc_value = self._ba_devdata[self._slc_led.start] self.a2red(bool(value & 2))
proc_value_calc = proc_value & 12
if proc_value_calc == value:
return
# Set new value
self._ba_devdata[self._slc_led.start] = \
proc_value - proc_value_calc + value
else: else:
raise ValueError("led status must be between 0 and 3") raise ValueError("led status must be between 0 and 3")
@@ -1160,6 +1145,250 @@ class Compact(Base):
) * 10 ) * 10
class Flat(Base):
"""
Klasse fuer den RevPi Flat.
Stellt Funktionen fuer die LEDs zur Verfuegung. Auf IOs wird ueber das .io
Objekt zugegriffen.
"""
__slots__ = "_slc_temperature", "_slc_frequency", "_slc_led", \
"a1green", "a1red", "a2green", "a2red", \
"a3green", "a3red", "a4green", "a4red", \
"a5green", "a5red", "wd"
def __setattr__(self, key, value):
"""Verhindert Ueberschreibung der LEDs."""
if hasattr(self, key) and key in (
"a1green", "a1red", "a2green", "a2red",
"a3green", "a3red", "a4green", "a4red",
"a5green", "a5red", "wd"):
raise AttributeError(
"direct assignment is not supported - use .value Attribute"
)
else:
object.__setattr__(self, key, value)
def _devconfigure(self) -> None:
"""Core-Klasse vorbereiten."""
# Statische IO Verknüpfungen des Compacts
self._slc_led = slice(6, 8)
self._slc_temperature = slice(4, 5)
self._slc_frequency = slice(5, 6)
# Exportflags prüfen (Byte oder Bit)
lst_led = self._modio.io[self._slc_devoff][self._slc_led.start]
if len(lst_led) == 8:
exp_a1green = lst_led[0].export
exp_a1red = lst_led[1].export
exp_a2green = lst_led[2].export
exp_a2red = lst_led[3].export
exp_a3green = lst_led[4].export
exp_a3red = lst_led[5].export
exp_a4green = lst_led[6].export
exp_a4red = lst_led[7].export
# Next byte
lst_led = self._modio.io[self._slc_devoff][self._slc_led.start + 1]
exp_a5green = lst_led[0].export
exp_a5red = lst_led[1].export
else:
exp_a1green = lst_led[0].export
exp_a1red = exp_a1green
exp_a2green = exp_a1green
exp_a2red = exp_a1green
exp_a3green = exp_a1green
exp_a3red = exp_a1green
exp_a4green = exp_a1green
exp_a4red = exp_a1green
exp_a5green = exp_a1green
exp_a5red = exp_a1green
# Echte IOs erzeugen
self.a1green = IOBase(self, [
"core.a1green", 0, 1, self._slc_led.start,
exp_a1green, None, "LED_A1_GREEN", "0"
], OUT, "little", False)
self.a1red = IOBase(self, [
"core.a1red", 0, 1, self._slc_led.start,
exp_a1red, None, "LED_A1_RED", "1"
], OUT, "little", False)
self.a2green = IOBase(self, [
"core.a2green", 0, 1, self._slc_led.start,
exp_a2green, None, "LED_A2_GREEN", "2"
], OUT, "little", False)
self.a2red = IOBase(self, [
"core.a2red", 0, 1, self._slc_led.start,
exp_a2red, None, "LED_A2_RED", "3"
], OUT, "little", False)
self.a3green = IOBase(self, [
"core.a3green", 0, 1, self._slc_led.start,
exp_a3green, None, "LED_A3_GREEN", "4"
], OUT, "little", False)
self.a3red = IOBase(self, [
"core.a3red", 0, 1, self._slc_led.start,
exp_a3red, None, "LED_A3_RED", "5"
], OUT, "little", False)
self.a4green = IOBase(self, [
"core.a4green", 0, 1, self._slc_led.start,
exp_a4green, None, "LED_A4_GREEN", "6"
], OUT, "little", False)
self.a4red = IOBase(self, [
"core.a4red", 0, 1, self._slc_led.start,
exp_a4red, None, "LED_A4_RED", "7"
], OUT, "little", False)
self.a5green = IOBase(self, [
"core.a5green", 0, 1, self._slc_led.start,
exp_a5green, None, "LED_A5_GREEN", "8"
], OUT, "little", False)
self.a5red = IOBase(self, [
"core.a5red", 0, 1, self._slc_led.start,
exp_a5red, None, "LED_A5_RED", "9"
], OUT, "little", False)
# todo: Add internal switch and relay, like Connect
# Software watchdog einrichten
self.wd = IOBase(self, [
"core.wd", 0, 1, self._slc_led.start,
False, None, "WatchDog", "15"
], OUT, "little", False)
def _get_leda1(self) -> int:
"""
Get value of LED A1 from RevPi Flat device.
:return: 0=off, 1=green, 2=red
"""
return self._ba_devdata[self._slc_led.start] & 0b11
def _get_leda2(self) -> int:
"""
Get value of LED A2 from RevPi Flat device.
:return: 0=off, 1=green, 2=red
"""
return (self._ba_devdata[self._slc_led.start] & 0b1100) >> 2
def _get_leda3(self) -> int:
"""
Get value of LED A3 from RevPi Flat device.
:return: 0=off, 1=green, 2=red
"""
return (self._ba_devdata[self._slc_led.start] & 0b110000) >> 4
def _get_leda4(self) -> int:
"""
Get value of LED A4 from RevPi Flat device.
:return: 0=off, 1=green, 2=red
"""
return (self._ba_devdata[self._slc_led.start] & 0b11000000) >> 6
def _get_leda5(self) -> int:
"""
Get value of LED A5 from RevPi Flat device.
:return: 0=off, 1=green, 2=red
"""
return self._ba_devdata[self._slc_led.start + 1] & 0b11
def _set_leda1(self, value: int) -> None:
"""
Set LED A1 on RevPi Flat device.
:param value: 0=off, 1=green, 2=red
"""
if 0 <= value <= 3:
self.a1green(bool(value & 1))
self.a1red(bool(value & 2))
else:
raise ValueError("led status must be between 0 and 3")
def _set_leda2(self, value: int) -> None:
"""
Set LED A2 on RevPi Flat device.
:param value: 0=off, 1=green, 2=red
"""
if 0 <= value <= 3:
self.a2green(bool(value & 1))
self.a2red(bool(value & 2))
else:
raise ValueError("led status must be between 0 and 3")
def _set_leda3(self, value: int) -> None:
"""
Set LED A3 on RevPi Flat device.
:param value: 0=off, 1=green, 2=red
"""
if 0 <= value <= 3:
self.a3green(bool(value & 1))
self.a3red(bool(value & 2))
else:
raise ValueError("led status must be between 0 and 3")
def _set_leda4(self, value: int) -> None:
"""
Set LED A4 on RevPi Flat device.
:param value: 0=off, 1=green, 2=red
"""
if 0 <= value <= 3:
self.a4green(bool(value & 1))
self.a4red(bool(value & 2))
else:
raise ValueError("led status must be between 0 and 3")
def _set_leda5(self, value: int) -> None:
"""
Set LED A5 on RevPi Flat device.
:param value: 0=off, 1=green, 2=red
"""
if 0 <= value <= 3:
self.a5green(bool(value & 1))
self.a5red(bool(value & 2))
else:
raise ValueError("led status must be between 0 and 3")
def wd_toggle(self):
"""Toggle watchdog bit to prevent a timeout."""
self.wd.value = not self.wd.value
A1 = property(_get_leda1, _set_leda1)
A2 = property(_get_leda2, _set_leda2)
A3 = property(_get_leda3, _set_leda3)
A4 = property(_get_leda4, _set_leda4)
A5 = property(_get_leda5, _set_leda5)
@property
def temperature(self) -> int:
"""
Gibt CPU-Temperatur zurueck.
:return: CPU-Temperatur in Celsius (-273 wenn nich verfuegbar)
"""
return -273 if self._slc_temperature is None else int.from_bytes(
self._ba_devdata[self._slc_temperature], byteorder="little"
)
@property
def frequency(self) -> int:
"""
Gibt CPU Taktfrequenz zurueck.
:return: CPU Taktfrequenz in MHz (-1 wenn nicht verfuegbar)
"""
return -1 if self._slc_frequency is None else int.from_bytes(
self._ba_devdata[self._slc_frequency], byteorder="little"
) * 10
class DioModule(Device): class DioModule(Device):
"""Stellt ein DIO / DI / DO Modul dar.""" """Stellt ein DIO / DI / DO Modul dar."""

View File

@@ -4,6 +4,7 @@ import queue
import warnings import warnings
from math import ceil from math import ceil
from threading import Event, Lock, Thread from threading import Event, Lock, Thread
from time import sleep
from timeit import default_timer from timeit import default_timer
from revpimodio2 import BOTH, FALLING, RISING from revpimodio2 import BOTH, FALLING, RISING
@@ -463,6 +464,7 @@ class ProcimgWriter(Thread):
tup_fireth[0].func, tup_fireth[1], tup_fireth[2] tup_fireth[0].func, tup_fireth[1], tup_fireth[2]
) )
th.start() th.start()
self._eventqth.task_done()
except queue.Empty: except queue.Empty:
pass pass
@@ -532,20 +534,18 @@ class ProcimgWriter(Thread):
fh.seek(0) fh.seek(0)
fh.readinto(bytesbuff) fh.readinto(bytesbuff)
if self._modio._monitoring or self._modio._direct_output: for dev in self._modio._lst_refresh:
# Inputs und Outputs in Puffer with dev._filelock:
for dev in self._modio._lst_refresh: if self._modio._monitoring or dev._shared_procimg:
with dev._filelock: # Inputs und Outputs in Puffer
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff] dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
if self.__eventwork \ if self.__eventwork \
and len(dev._dict_events) > 0 \ and len(dev._dict_events) > 0 \
and dev._ba_datacp != dev._ba_devdata: and dev._ba_datacp != dev._ba_devdata:
self.__check_change(dev) self.__check_change(dev)
else: else:
# Inputs in Puffer, Outputs in Prozessabbild # Inputs in Puffer, Outputs in Prozessabbild
for dev in self._modio._lst_refresh:
with dev._filelock:
dev._ba_devdata[dev._slc_inp] = \ dev._ba_devdata[dev._slc_inp] = \
bytesbuff[dev._slc_inpoff] bytesbuff[dev._slc_inpoff]
if self.__eventwork \ if self.__eventwork \
@@ -556,8 +556,8 @@ class ProcimgWriter(Thread):
fh.seek(dev._slc_outoff.start) fh.seek(dev._slc_outoff.start)
fh.write(dev._ba_devdata[dev._slc_out]) fh.write(dev._ba_devdata[dev._slc_out])
if self._modio._buffedwrite: if self._modio._buffedwrite:
fh.flush() fh.flush()
except IOError as e: except IOError as e:
self._modio._gotioerror("autorefresh", e, mrk_warn) self._modio._gotioerror("autorefresh", e, mrk_warn)
@@ -602,8 +602,8 @@ class ProcimgWriter(Thread):
self._eventq.put(tup_fire, False) self._eventq.put(tup_fire, False)
del self.__dict_delay[tup_fire] del self.__dict_delay[tup_fire]
# Refresh abwarten # Sleep and not .wait (.wait uses system clock)
self._work.wait(self._adjwait) sleep(self._adjwait)
# Wartezeit anpassen um echte self._refresh zu erreichen # Wartezeit anpassen um echte self._refresh zu erreichen
mrk_dt = default_timer() mrk_dt = default_timer()

View File

@@ -624,7 +624,7 @@ class IOBase(object):
# Versuchen egal welchen Typ in Bool zu konvertieren # Versuchen egal welchen Typ in Bool zu konvertieren
value = bool(value) value = bool(value)
if self._parentdevice._modio._direct_output: if self._parentdevice._shared_procimg:
# Direktes Schreiben der Outputs # Direktes Schreiben der Outputs
if self._parentdevice._modio._run_on_pi: if self._parentdevice._modio._run_on_pi:
@@ -640,6 +640,7 @@ class IOBase(object):
) )
except Exception as e: except Exception as e:
self._parentdevice._modio._gotioerror("ioset", e) self._parentdevice._modio._gotioerror("ioset", e)
return
elif hasattr(self._parentdevice._modio._myfh, "ioctl"): elif hasattr(self._parentdevice._modio._myfh, "ioctl"):
# IOCTL über Netzwerk # IOCTL über Netzwerk
@@ -653,6 +654,7 @@ class IOBase(object):
except Exception as e: except Exception as e:
self._parentdevice._modio._gotioerror( self._parentdevice._modio._gotioerror(
"net_ioset", e) "net_ioset", e)
return
else: else:
# IOCTL in Datei simulieren # IOCTL in Datei simulieren
@@ -665,28 +667,26 @@ class IOBase(object):
) )
except Exception as e: except Exception as e:
self._parentdevice._modio._gotioerror("file_ioset", e) self._parentdevice._modio._gotioerror("file_ioset", e)
return
else: # Für Bitoperationen sperren
# Gepuffertes Schreiben der Outputs self._parentdevice._filelock.acquire()
# Für Bitoperationen sperren # Hier gibt es immer nur ein byte, als int holen
self._parentdevice._filelock.acquire() int_byte = self._parentdevice._ba_devdata[self._slc_address.start]
# Hier gibt es immer nur ein byte, als int holen # Aktuellen Wert vergleichen und ggf. setzen
int_byte = self._parentdevice._ba_devdata[self._slc_address.start] if not bool(int_byte & self._bitshift) == value:
if value:
int_byte += self._bitshift
else:
int_byte -= self._bitshift
# Aktuellen Wert vergleichen und ggf. setzen # Zurückschreiben wenn verändert
if not bool(int_byte & self._bitshift) == value: self._parentdevice._ba_devdata[self._slc_address.start] = \
if value: int_byte
int_byte += self._bitshift
else:
int_byte -= self._bitshift
# Zurückschreiben wenn verändert self._parentdevice._filelock.release()
self._parentdevice._ba_devdata[self._slc_address.start] = \
int_byte
self._parentdevice._filelock.release()
else: else:
if type(value) != bytes: if type(value) != bytes:
@@ -704,7 +704,7 @@ class IOBase(object):
) )
) )
if self._parentdevice._modio._direct_output: if self._parentdevice._shared_procimg:
with self._parentdevice._modio._myfh_lck: with self._parentdevice._modio._myfh_lck:
try: try:
self._parentdevice._modio._myfh.seek( self._parentdevice._modio._myfh.seek(
@@ -715,8 +715,9 @@ class IOBase(object):
self._parentdevice._modio._myfh.flush() self._parentdevice._modio._myfh.flush()
except IOError as e: except IOError as e:
self._parentdevice._modio._gotioerror("ioset", e) self._parentdevice._modio._gotioerror("ioset", e)
else: return
self._parentdevice._ba_devdata[self._slc_address] = value
self._parentdevice._ba_devdata[self._slc_address] = value
def unreg_event(self, func=None, edge=None) -> None: def unreg_event(self, func=None, edge=None) -> None:
""" """

View File

@@ -32,7 +32,7 @@ class RevPiModIO(object):
""" """
__slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", "_exit_level", \ __slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", "_exit_level", \
"_configrsc", "_direct_output", "_exit", "_imgwriter", "_ioerror", \ "_configrsc", "_shared_procimg", "_exit", "_imgwriter", "_ioerror", \
"_length", "_looprunning", "_lst_devselect", "_lst_refresh", \ "_length", "_looprunning", "_lst_devselect", "_lst_refresh", \
"_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \ "_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \
"_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \ "_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \
@@ -76,10 +76,10 @@ class RevPiModIO(object):
self._autorefresh = autorefresh self._autorefresh = autorefresh
self._configrsc = configrsc self._configrsc = configrsc
self._direct_output = shared_procimg or direct_output
self._monitoring = monitoring self._monitoring = monitoring
self._procimg = "/dev/piControl0" if procimg is None else procimg self._procimg = "/dev/piControl0" if procimg is None else procimg
self._simulator = simulator self._simulator = simulator
self._shared_procimg = shared_procimg or direct_output
self._syncoutputs = syncoutputs self._syncoutputs = syncoutputs
# TODO: bei simulator und procimg prüfen ob datei existiert / anlegen? # TODO: bei simulator und procimg prüfen ob datei existiert / anlegen?
@@ -254,6 +254,12 @@ class RevPiModIO(object):
self, device, simulator=self._simulator self, device, simulator=self._simulator
) )
self.core = dev_new self.core = dev_new
elif pt == 135:
# RevPi Flat
dev_new = devicemodule.Flat(
self, device, simulator=self._simulator
)
self.core = dev_new
else: else:
# Base immer als Fallback verwenden # Base immer als Fallback verwenden
dev_new = devicemodule.Base( dev_new = devicemodule.Base(
@@ -682,7 +688,7 @@ class RevPiModIO(object):
self._exit_level |= 2 self._exit_level |= 2
self.exit(full=True) self.exit(full=True)
def cycleloop(self, func, cycletime=50): def cycleloop(self, func, cycletime=50, blocking=True):
""" """
Startet den Cycleloop. Startet den Cycleloop.
@@ -709,6 +715,7 @@ class RevPiModIO(object):
:param func: Funktion, die ausgefuehrt werden soll :param func: Funktion, die ausgefuehrt werden soll
:param cycletime: Zykluszeit in Millisekunden - Standardwert 50 ms :param cycletime: Zykluszeit in Millisekunden - Standardwert 50 ms
:param blocking: Wenn False, blockiert das Programm hier NICHT
:return: None or the return value of the cycle function :return: None or the return value of the cycle function
""" """
# Prüfen ob ein Loop bereits läuft # Prüfen ob ein Loop bereits läuft
@@ -730,6 +737,16 @@ class RevPiModIO(object):
"registered function '{0}' ist not callable".format(func) "registered function '{0}' ist not callable".format(func)
) )
# Thread erstellen, wenn nicht blockieren soll
if not blocking:
self._th_mainloop = Thread(
target=self.cycleloop,
args=(func,),
kwargs={"cycletime": cycletime, "blocking": True}
)
self._th_mainloop.start()
return
# Zykluszeit übernehmen # Zykluszeit übernehmen
old_cycletime = self._imgwriter.refresh old_cycletime = self._imgwriter.refresh
if not cycletime == self._imgwriter.refresh: if not cycletime == self._imgwriter.refresh:
@@ -751,12 +768,18 @@ class RevPiModIO(object):
while ec is None and not cycleinfo.last: while ec is None and not cycleinfo.last:
# Auf neue Daten warten und nur ausführen wenn set() # Auf neue Daten warten und nur ausführen wenn set()
if not self._imgwriter.newdata.wait(2.5): if not self._imgwriter.newdata.wait(2.5):
self.exit(full=False) if not self._imgwriter.is_alive():
if self._imgwriter.is_alive(): self.exit(full=False)
e = RuntimeError("no new io data in cycle loop")
else:
e = RuntimeError("autorefresh thread not running") e = RuntimeError("autorefresh thread not running")
break break
# Just warn, user has to use maxioerrors to kill program
warnings.warn(RuntimeWarning(
"no new io data in cycle loop for 2500 milliseconds"
))
cycleinfo.last = self._exit.is_set()
continue
self._imgwriter.newdata.clear() self._imgwriter.newdata.clear()
# Vor Aufruf der Funktion autorefresh sperren # Vor Aufruf der Funktion autorefresh sperren
@@ -775,11 +798,13 @@ class RevPiModIO(object):
except Exception as ex: except Exception as ex:
if self._imgwriter.lck_refresh.locked(): if self._imgwriter.lck_refresh.locked():
self._imgwriter.lck_refresh.release() self._imgwriter.lck_refresh.release()
self.exit(full=False) if self._th_mainloop is None:
self.exit(full=False)
e = ex e = ex
finally: finally:
# Cycleloop beenden # Cycleloop beenden
self._looprunning = False self._looprunning = False
self._th_mainloop = None
# Alte autorefresh Zeit setzen # Alte autorefresh Zeit setzen
self._imgwriter.refresh = old_cycletime self._imgwriter.refresh = old_cycletime
@@ -1023,6 +1048,7 @@ class RevPiModIO(object):
# Direct callen da Prüfung in io.IOBase.reg_event ist # Direct callen da Prüfung in io.IOBase.reg_event ist
tup_fire[0].func(tup_fire[1], tup_fire[2]) tup_fire[0].func(tup_fire[1], tup_fire[2])
self._imgwriter._eventq.task_done()
# Laufzeitprüfung # Laufzeitprüfung
if runtime != -1 and \ if runtime != -1 and \
@@ -1094,7 +1120,7 @@ class RevPiModIO(object):
# FileHandler sperren # FileHandler sperren
dev._filelock.acquire() dev._filelock.acquire()
if self._monitoring or self._direct_output: if self._monitoring or dev._shared_procimg:
# Alles vom Bus einlesen # Alles vom Bus einlesen
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff] dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
else: else:
@@ -1181,9 +1207,6 @@ class RevPiModIO(object):
:param device: nur auf einzelnes Device anwenden :param device: nur auf einzelnes Device anwenden
:return: True, wenn Arbeiten an allen Devices erfolgreich waren :return: True, wenn Arbeiten an allen Devices erfolgreich waren
""" """
if self._direct_output:
return True
if self._monitoring: if self._monitoring:
raise RuntimeError( raise RuntimeError(
"can not write process image, while system is in monitoring " "can not write process image, while system is in monitoring "
@@ -1206,7 +1229,9 @@ class RevPiModIO(object):
global_ex = None global_ex = None
workokay = True workokay = True
for dev in mylist: for dev in mylist:
if not dev._selfupdate: if dev._shared_procimg:
continue
elif not dev._selfupdate:
dev._filelock.acquire() dev._filelock.acquire()
# Outpus auf Bus schreiben # Outpus auf Bus schreiben
@@ -1348,7 +1373,9 @@ class RevPiModIODriver(RevPiModIOSelected):
) )
def run_plc(func, cycletime=50, replace_io_file=None): def run_plc(
func, cycletime=50, replace_io_file=None, debug=True,
procimg=None, configrsc=None):
""" """
Run Revoluton Pi as real plc with cycle loop and exclusive IO access. Run Revoluton Pi as real plc with cycle loop and exclusive IO access.
@@ -1356,17 +1383,27 @@ def run_plc(func, cycletime=50, replace_io_file=None):
handle the program exit signal. You will access the .io, .core, .device handle the program exit signal. You will access the .io, .core, .device
via the cycletools in your cycle function. via the cycletools in your cycle function.
Shortcut vor this source code: Shortcut for this source code:
rpi = RevPiModIO(autorefresh=True, replace_io_file=replace_io_file) rpi = RevPiModIO(autorefresh=True, replace_io_file=..., debug=...)
rpi.handlesignalend() rpi.handlesignalend()
return rpi.cycleloop(func, cycletime) return rpi.cycleloop(func, cycletime)
:param func: Function to run every set milliseconds :param func: Function to run every set milliseconds
:param cycletime: Cycle time in milliseconds :param cycletime: Cycle time in milliseconds
:param replace_io_file: Load replace IO configuration from file :param replace_io_file: Load replace IO configuration from file
:param debug: Print all warnings and detailed error messages
:param procimg: Use different process image
:param configrsc: Use different piCtory configuration
:return: None or the return value of the cycle function :return: None or the return value of the cycle function
""" """
rpi = RevPiModIO(autorefresh=True, replace_io_file=replace_io_file) rpi = RevPiModIO(
autorefresh=True,
replace_io_file=replace_io_file,
debug=debug,
procimg=procimg,
configrsc=configrsc,
)
rpi.handlesignalend() rpi.handlesignalend()
return rpi.cycleloop(func, cycletime) return rpi.cycleloop(func, cycletime)

View File

@@ -82,7 +82,6 @@ class NetFH(Thread):
self.__config_changed = False self.__config_changed = False
self.__int_buff = 0 self.__int_buff = 0
self.__dictdirty = {} self.__dictdirty = {}
self.__flusherr = False
self.__replace_ios_h = b'' self.__replace_ios_h = b''
self.__pictory_h = b'' self.__pictory_h = b''
self.__sockerr = Event() self.__sockerr = Event()
@@ -210,7 +209,6 @@ class NetFH(Thread):
self._slavesock = so self._slavesock = so
self.__sockerr.clear() self.__sockerr.clear()
self.__flusherr = False
# Timeout setzen # Timeout setzen
self.set_timeout(int(self.__timeout * 1000)) self.set_timeout(int(self.__timeout * 1000))
@@ -219,18 +217,25 @@ class NetFH(Thread):
for pos in self.__dictdirty: for pos in self.__dictdirty:
self.set_dirtybytes(pos, self.__dictdirty[pos]) self.set_dirtybytes(pos, self.__dictdirty[pos])
def _direct_send(self, send_bytes: bytes, recv_len: int) -> bytes: def _direct_sr(self, send_bytes: bytes, recv_len: int) -> bytes:
""" """
Sicherer Zugriff auf send / recv Schnittstelle mit Laengenpruefung. Secure send and receive function for network handler.
:param send_bytes: Bytes, die gesendet werden sollen Will raise exception on closed network handler or network errors and
:param recv_len: Anzahl der die empfangen werden sollen set the sockerr flag.
:return: Empfangende Bytes
:param send_bytes: Bytes to send or empty
:param recv_len: Amount of bytes to receive
:return: Received bytes
""" """
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("I/O operation on closed file") raise ValueError("I/O operation on closed file")
if self.__sockerr.is_set():
raise IOError("not allowed while reconnect")
try:
self.__socklock.acquire()
with self.__socklock:
counter = 0 counter = 0
send_len = len(send_bytes) send_len = len(send_bytes)
while counter < send_len: while counter < send_len:
@@ -238,7 +243,7 @@ class NetFH(Thread):
sent = self._slavesock.send(send_bytes[counter:]) sent = self._slavesock.send(send_bytes[counter:])
if sent == 0: if sent == 0:
self.__sockerr.set() self.__sockerr.set()
raise IOError("lost network connection") raise IOError("lost network connection while send")
counter += sent counter += sent
self.__buff_recv.clear() self.__buff_recv.clear()
@@ -247,12 +252,20 @@ class NetFH(Thread):
self.__buff_block, min(recv_len, self.__buff_size) self.__buff_block, min(recv_len, self.__buff_size)
) )
if count == 0: if count == 0:
self.__sockerr.set() raise IOError("lost network connection while receive")
raise IOError("lost network connection")
self.__buff_recv += self.__buff_block[:count] self.__buff_recv += self.__buff_block[:count]
recv_len -= count recv_len -= count
return bytes(self.__buff_recv) # Create copy in socklock environment
return_buffer = bytes(self.__buff_recv)
except Exception:
self.__sockerr.set()
raise
finally:
self.__socklock.release()
return return_buffer
def clear_dirtybytes(self, position=None) -> None: def clear_dirtybytes(self, position=None) -> None:
""" """
@@ -268,42 +281,32 @@ class NetFH(Thread):
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("I/O operation on closed file") raise ValueError("I/O operation on closed file")
error = False
try:
self.__socklock.acquire()
if position is None:
# Alle Dirtybytes löschen
self._slavesock.sendall(_sysdeldirty)
else:
# Nur bestimmte Dirtybytes löschen
# b CM ii xx c0000000 b = 16
self._slavesock.sendall(pack(
"=c2sH2xc7xc",
HEADER_START, b'EY', position, b'\xfe', HEADER_STOP
))
check = self._slavesock.recv(1)
if check != b'\x1e':
# ACL prüfen und ggf Fehler werfen
self.__check_acl(check)
raise IOError("clear dirtybytes error on network")
except AclException:
raise
except Exception:
error = True
finally:
self.__socklock.release()
# Daten immer übernehmen # Daten immer übernehmen
if position is None: if position is None:
self.__dictdirty = {} self.__dictdirty.clear()
elif position in self.__dictdirty: elif position in self.__dictdirty:
del self.__dictdirty[position] del self.__dictdirty[position]
if error: try:
# Fehler nach übernahme der Daten auslösen um diese zu setzen if position is None:
# Alle Dirtybytes löschen
buff = self._direct_sr(_sysdeldirty, 1)
else:
# Nur bestimmte Dirtybytes löschen
# b CM ii xx c0000000 b = 16
buff = self._direct_sr(pack(
"=c2sH2xc7xc",
HEADER_START, b'EY', position, b'\xfe', HEADER_STOP
), 1)
if buff != b'\x1e':
# ACL prüfen und ggf Fehler werfen
self.__check_acl(buff)
raise IOError("clear dirtybytes error on network")
except AclException:
self.__dictdirty.clear()
raise
except Exception:
self.__sockerr.set() self.__sockerr.set()
def close(self) -> None: def close(self) -> None:
@@ -334,32 +337,28 @@ class NetFH(Thread):
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("flush of closed file") raise ValueError("flush of closed file")
with self.__socklock: if self.__int_buff == 0:
return
try:
# b CM ii ii 00000000 b = 16 # b CM ii ii 00000000 b = 16
try: buff = self._direct_sr(pack(
self._slavesock.sendall(pack( "=c2sHH8xc",
"=c2sHH8xc", HEADER_START, b'FD', self.__int_buff, len(self.__by_buff), HEADER_STOP
HEADER_START, b'FD', self.__int_buff, len(self.__by_buff), HEADER_STOP ) + self.__by_buff, 1)
) + self.__by_buff) except Exception:
except Exception: raise
self.__flusherr = True finally:
raise # Puffer immer leeren
finally: self.__int_buff = 0
# Puffer immer leeren self.__by_buff.clear()
self.__int_buff = 0
self.__by_buff.clear()
# Rückmeldebyte auswerten if buff != b'\x1e':
blockok = self._slavesock.recv(1) # ACL prüfen und ggf Fehler werfen
if blockok != b'\x1e': self.__check_acl(buff)
# ACL prüfen und ggf Fehler werfen
self.__check_acl(blockok)
self.__flusherr = True self.__sockerr.set()
self.__sockerr.set() raise IOError("flush error on network")
raise IOError("flush error on network")
else:
self.__flusherr = False
def get_closed(self) -> bool: def get_closed(self) -> bool:
""" """
@@ -416,21 +415,17 @@ class NetFH(Thread):
if not (isinstance(arg, bytes) and len(arg) <= 1024): if not (isinstance(arg, bytes) and len(arg) <= 1024):
raise TypeError("arg must be <class 'bytes'>") raise TypeError("arg must be <class 'bytes'>")
with self.__socklock: # b CM xx ii iiii0000 b = 16
# b CM xx ii iiii0000 b = 16 buff = self._direct_sr(pack(
self._slavesock.sendall(pack( "=c2s2xHI4xc",
"=c2s2xHI4xc", HEADER_START, b'IC', len(arg), request, HEADER_STOP
HEADER_START, b'IC', len(arg), request, HEADER_STOP ) + arg, 1)
) + arg) if buff != b'\x1e':
# ACL prüfen und ggf Fehler werfen
self.__check_acl(buff)
# Rückmeldebyte auswerten self.__sockerr.set()
blockok = self._slavesock.recv(1) raise IOError("ioctl error on network")
if blockok != b'\x1e':
# ACL prüfen und ggf Fehler werfen
self.__check_acl(blockok)
self.__sockerr.set()
raise IOError("ioctl error on network")
def read(self, length: int) -> bytes: def read(self, length: int) -> bytes:
""" """
@@ -444,28 +439,14 @@ class NetFH(Thread):
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("read of closed file") raise ValueError("read of closed file")
with self.__socklock: # b CM ii ii 00000000 b = 16
# b CM ii ii 00000000 b = 16 buff = self._direct_sr(pack(
self._slavesock.sendall(pack( "=c2sHH8xc",
"=c2sHH8xc", HEADER_START, b'DA', self.__position, length, HEADER_STOP
HEADER_START, b'DA', self.__position, length, HEADER_STOP ), length)
))
self.__position += length self.__position += length
return buff
self.__buff_recv.clear()
while length > 0:
count = self._slavesock.recv_into(
self.__buff_block,
min(length, self.__buff_size),
)
if count == 0:
self.__sockerr.set()
raise IOError("read error on network")
self.__buff_recv += self.__buff_block[:count]
length -= count
return bytes(self.__buff_recv)
def readinto(self, buffer: bytearray) -> int: def readinto(self, buffer: bytearray) -> int:
""" """
@@ -480,29 +461,14 @@ class NetFH(Thread):
raise ValueError("read of closed file") raise ValueError("read of closed file")
length = len(buffer) length = len(buffer)
with self.__socklock:
# b CM ii ii 00000000 b = 16
self._slavesock.sendall(pack(
"=c2sHH8xc",
HEADER_START, b'DA', self.__position, length, HEADER_STOP
))
net_buffer = b'' # b CM ii ii 00000000 b = 16
while length > 0: buff = self._direct_sr(pack(
count = self._slavesock.recv_into( "=c2sHH8xc",
self.__buff_block, HEADER_START, b'DA', self.__position, length, HEADER_STOP
min(length, self.__buff_size), ), length)
)
if count == 0:
self.__sockerr.set()
raise IOError("read error on network")
net_buffer += self.__buff_block[:count]
length -= count
# We received the correct amount of bytes here
self.__position += length
buffer[:] = net_buffer
buffer[:] = buff
return len(buffer) return len(buffer)
def readpictory(self) -> bytes: def readpictory(self) -> bytes:
@@ -519,31 +485,9 @@ class NetFH(Thread):
"could not read/parse piCtory configuration over network" "could not read/parse piCtory configuration over network"
) )
with self.__socklock: buff = self._direct_sr(_syspictory, 4)
self._slavesock.sendall(_syspictory) recv_length, = unpack("=I", buff)
return self._direct_sr(b'', recv_length)
self.__buff_recv.clear()
recv_lenght = 4
while recv_lenght > 0:
count = self._slavesock.recv_into(
self.__buff_block, recv_lenght
)
if count == 0:
raise IOError("readpictory length error on network")
self.__buff_recv += self.__buff_block[:count]
recv_lenght -= count
recv_lenght, = unpack("=I", self.__buff_recv)
while recv_lenght > 0:
count = self._slavesock.recv_into(
self.__buff_block, min(recv_lenght, self.__buff_size)
)
if count == 0:
raise IOError("readpictory file error on network")
self.__buff_recv += self.__buff_block[:count]
recv_lenght -= count
return bytes(self.__buff_recv[4:])
def readreplaceio(self) -> bytes: def readreplaceio(self) -> bytes:
""" """
@@ -559,31 +503,9 @@ class NetFH(Thread):
"replace_io_file: could not read/parse over network" "replace_io_file: could not read/parse over network"
) )
with self.__socklock: buff = self._direct_sr(_sysreplaceio, 4)
self._slavesock.sendall(_sysreplaceio) recv_length, = unpack("=I", buff)
return self._direct_sr(b'', recv_length)
self.__buff_recv.clear()
recv_lenght = 4
while recv_lenght > 0:
count = self._slavesock.recv_into(
self.__buff_block, recv_lenght
)
if count == 0:
raise IOError("readreplaceio length error on network")
self.__buff_recv += self.__buff_block[:count]
recv_lenght -= count
recv_lenght, = unpack("=I", self.__buff_recv)
while recv_lenght > 0:
count = self._slavesock.recv_into(
self.__buff_block, min(recv_lenght, self.__buff_size)
)
if count == 0:
raise IOError("readreplaceio file error on network")
self.__buff_recv += self.__buff_block[:count]
recv_lenght -= count
return bytes(self.__buff_recv[4:])
def run(self) -> None: def run(self) -> None:
"""Handler fuer Synchronisierung.""" """Handler fuer Synchronisierung."""
@@ -622,7 +544,7 @@ class NetFH(Thread):
self.__buff_block, recv_lenght self.__buff_block, recv_lenght
) )
if count == 0: if count == 0:
raise IOError("lost network connection") raise IOError("lost network connection on sync")
self.__buff_recv += self.__buff_block[:count] self.__buff_recv += self.__buff_block[:count]
recv_lenght -= count recv_lenght -= count
@@ -666,34 +588,26 @@ class NetFH(Thread):
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("I/O operation on closed file") raise ValueError("I/O operation on closed file")
error = False
try:
self.__socklock.acquire()
# b CM ii ii 00000000 b = 16
self._slavesock.sendall(pack(
"=c2sHH8xc",
HEADER_START, b'EY', position, len(dirtybytes), HEADER_STOP
) + dirtybytes)
check = self._slavesock.recv(1)
if check != b'\x1e':
# ACL prüfen und ggf Fehler werfen
self.__check_acl(check)
raise IOError("set dirtybytes error on network")
except AclException:
raise
except Exception:
error = True
finally:
self.__socklock.release()
# Daten immer übernehmen # Daten immer übernehmen
self.__dictdirty[position] = dirtybytes self.__dictdirty[position] = dirtybytes
if error: try:
# Fehler nach übernahme der Daten auslösen um diese zu setzen # b CM ii ii 00000000 b = 16
buff = self._direct_sr(pack(
"=c2sHH8xc",
HEADER_START, b'EY', position, len(dirtybytes), HEADER_STOP
) + dirtybytes, 1)
if buff != b'\x1e':
# ACL prüfen und ggf Fehler werfen
self.__check_acl(buff)
raise IOError("set dirtybytes error on network")
except AclException:
# Not allowed, clear for reconnect
self.__dictdirty.clear()
raise
except Exception:
self.__sockerr.set() self.__sockerr.set()
def set_timeout(self, value: int) -> None: def set_timeout(self, value: int) -> None:
@@ -709,20 +623,15 @@ class NetFH(Thread):
self.__set_systimeout(value) self.__set_systimeout(value)
try: try:
self.__socklock.acquire()
# b CM ii xx 00000000 b = 16 # b CM ii xx 00000000 b = 16
self._slavesock.sendall(pack( buff = self._direct_sr(pack(
"=c2sH10xc", "=c2sH10xc",
HEADER_START, b'CF', value, HEADER_STOP HEADER_START, b'CF', value, HEADER_STOP
)) ), 1)
check = self._slavesock.recv(1) if buff != b'\x1e':
if check != b'\x1e':
raise IOError("set timeout error on network") raise IOError("set timeout error on network")
except Exception: except Exception:
self.__sockerr.set() self.__sockerr.set()
finally:
self.__socklock.release()
def tell(self) -> int: def tell(self) -> int:
""" """
@@ -747,14 +656,13 @@ class NetFH(Thread):
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("write to closed file") raise ValueError("write to closed file")
if self.__sockerr.is_set():
if self.__flusherr: raise IOError("not allowed while reconnect")
raise IOError("I/O error since last flush")
with self.__socklock: with self.__socklock:
self.__int_buff += 1 self.__int_buff += 1
# Datenblöcke mit Group Seperator in Puffer ablegen # Datenblock mit Position und Länge in Puffer ablegen
self.__by_buff += self.__position.to_bytes(length=2, byteorder="little") + \ self.__by_buff += self.__position.to_bytes(length=2, byteorder="little") + \
len(bytebuff).to_bytes(length=2, byteorder="little") + \ len(bytebuff).to_bytes(length=2, byteorder="little") + \
bytebuff bytebuff
@@ -802,7 +710,7 @@ class RevPiNetIO(_RevPiModIO):
:param direct_output: Deprecated, use shared_procimg :param direct_output: Deprecated, use shared_procimg
""" """
check_ip = compile( check_ip = compile(
r"^(?P<ipn>(25[0-5]|(2[0-4]|[01]?\d|)\d))(\.(?P=ipn)){3}$" r"^(25[0-5]|(2[0-4]|[01]?\d|)\d)(\.(25[0-5]|(2[0-4]|[01]?\d|)\d)){3}$"
) )
# Adresse verarbeiten # Adresse verarbeiten

View File

@@ -15,9 +15,8 @@ __license__ = "LGPLv3"
# - RevPiGateCANopen_20161102_1_0.rap # - RevPiGateCANopen_20161102_1_0.rap
# Can be used for :
# RevPi AIO 1.0 (RevPiAIO_20170301_1_0.rap)
class AIO: class AIO:
"""Memory value mappings for RevPi AIO 1.0 (RevPiAIO_20170301_1_0.rap)."""
OUT_RANGE_OFF = 0 # Off OUT_RANGE_OFF = 0 # Off
OUT_RANGE_0_5V = 1 # 0 - 5V OUT_RANGE_0_5V = 1 # 0 - 5V
OUT_RANGE_0_10V = 2 # 0 - 10V OUT_RANGE_0_10V = 2 # 0 - 10V
@@ -89,9 +88,8 @@ class AIO:
RTD_4_WIRE = 1 # 4-wire RTD_4_WIRE = 1 # 4-wire
# Can be used for :
# RevPi DI 1.0 (RevPiDI_20160818_1_0.rap)
class DI: class DI:
"""Memory value mappings for RevPi DI 1.0 (RevPiDI_20160818_1_0.rap)."""
IN_MODE_DIRECT = 0 # Direct IN_MODE_DIRECT = 0 # Direct
IN_MODE_COUNT_RISING = 1 # Counter, rising edge IN_MODE_COUNT_RISING = 1 # Counter, rising edge
IN_MODE_COUNT_FALLING = 2 # Counter, falling edge IN_MODE_COUNT_FALLING = 2 # Counter, falling edge
@@ -103,9 +101,8 @@ class DI:
IN_DEBOUNCE_3MS = 3 # 3ms IN_DEBOUNCE_3MS = 3 # 3ms
# Can be used for :
# RevPi DO 1.0 (RevPiDO_20160818_1_0.rap)
class DO: class DO:
"""Memory value mappings for RevPi DO 1.0 (RevPiDO_20160818_1_0.rap)."""
OUT_PWM_FREQ_40HZ = 1 # 40Hz 1% OUT_PWM_FREQ_40HZ = 1 # 40Hz 1%
OUT_PWM_FREQ_80HZ = 2 # 80Hz 2% OUT_PWM_FREQ_80HZ = 2 # 80Hz 2%
OUT_PWM_FREQ_160HZ = 4 # 160Hz 4% OUT_PWM_FREQ_160HZ = 4 # 160Hz 4%
@@ -113,7 +110,19 @@ class DO:
OUT_PWM_FREQ_400HZ = 10 # 400Hz 10% OUT_PWM_FREQ_400HZ = 10 # 400Hz 10%
# Can be used for :
# RevPi DIO 1.0 (RevPiDIO_20160818_1_0.rap)
class DIO(DI, DO): class DIO(DI, DO):
"""Memory value mappings for RevPi DIO 1.0 (RevPiDIO_20160818_1_0.rap)."""
pass pass
class COMPACT:
"""Memory value mappings for RevPi Compact 1.0 (RevPiCompact_20171023_1_0.rap)."""
DIN_DEBOUNCE_OFF = 0 # Off
DIN_DEBOUNCE_25US = 1 # 25us
DIN_DEBOUNCE_750US = 2 # 750us
DIN_DEBOUNCE_3MS = 3 # 3ms
AIN_MODE_OFF = 0 # Off
AIN_MODE_0_10V = 1 # 0 - 10V
AIN_MODE_PT100 = 3 # PT100
AIN_MODE_PT1000 = 7 # PT1000

View File

@@ -17,7 +17,7 @@ setup(
license="LGPLv3", license="LGPLv3",
name="revpimodio2", name="revpimodio2",
version="2.5.3", version="2.5.3g",
packages=["revpimodio2"], packages=["revpimodio2"],
python_requires="~=3.2", python_requires="~=3.2",