mirror of
https://github.com/naruxde/revpimodio2.git
synced 2025-11-08 22:03:53 +01:00
Merge branch 'develop'
This commit is contained in:
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -3,7 +3,7 @@
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</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">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
|
||||
2
.idea/revpimodio2.iml
generated
2
.idea/revpimodio2.iml
generated
@@ -2,7 +2,7 @@
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<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" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
|
||||
@@ -16,13 +16,13 @@ __all__ = [
|
||||
"RevPiModIO", "RevPiModIODriver", "RevPiModIOSelected", "run_plc",
|
||||
"RevPiNetIO", "RevPiNetIODriver", "RevPiNetIOSelected",
|
||||
"Cycletools", "EventCallback",
|
||||
"AIO", "DI", "DO", "DIO",
|
||||
"AIO", "COMPACT", "DI", "DO", "DIO",
|
||||
]
|
||||
__author__ = "Sven Sager <akira@revpimodio.org>"
|
||||
__copyright__ = "Copyright (C) 2020 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
__name__ = "revpimodio2"
|
||||
__version__ = "2.5.3"
|
||||
__version__ = "2.5.3d"
|
||||
|
||||
# Global package values
|
||||
OFF = 0
|
||||
@@ -96,7 +96,7 @@ def consttostr(value) -> str:
|
||||
|
||||
|
||||
# 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 .modio import RevPiModIO, RevPiModIODriver, RevPiModIOSelected, run_plc
|
||||
from .netio import RevPiNetIO, RevPiNetIODriver, RevPiNetIOSelected
|
||||
|
||||
@@ -125,10 +125,11 @@ class Device(object):
|
||||
"""
|
||||
|
||||
__slots__ = "__my_io_list", "_ba_devdata", "_ba_datacp", \
|
||||
"_dict_events", "_filelock", "_length", "_modio", "_name", "_offset", \
|
||||
"_position", "_producttype", "_selfupdate", "_slc_devoff", \
|
||||
"_slc_inp", "_slc_inpoff", "_slc_mem", "_slc_memoff", \
|
||||
"_slc_out", "_slc_outoff", "bmk", "catalognr", "comment", "extend", \
|
||||
"_dict_events", "_filelock", "_length", "_modio", "_name", \
|
||||
"_offset", "_position", "_producttype", "_selfupdate", \
|
||||
"_slc_devoff", "_slc_inp", "_slc_inpoff", "_slc_mem", \
|
||||
"_slc_memoff", "_slc_out", "_slc_outoff", "_shared_procimg", \
|
||||
"bmk", "catalognr", "comment", "extend", \
|
||||
"guid", "id", "inpvariant", "outvariant", "type"
|
||||
|
||||
def __init__(self, parentmodio, dict_device, simulator=False):
|
||||
@@ -146,6 +147,7 @@ class Device(object):
|
||||
self._length = 0
|
||||
self.__my_io_list = []
|
||||
self._selfupdate = False
|
||||
self._shared_procimg = parentmodio._shared_procimg
|
||||
|
||||
# Wertzuweisung aus dict_device
|
||||
self._name = dict_device.get("name")
|
||||
@@ -500,6 +502,17 @@ class Device(object):
|
||||
"""
|
||||
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:
|
||||
"""
|
||||
Lesen aller Outputs im Prozessabbild fuer dieses Device.
|
||||
@@ -593,11 +606,13 @@ class Core(Base):
|
||||
exp_a1red = lst_led[1].export
|
||||
exp_a2green = lst_led[2].export
|
||||
exp_a2red = lst_led[3].export
|
||||
# exp_wd = lst_led[7].export
|
||||
else:
|
||||
exp_a1green = lst_led[0].export
|
||||
exp_a1red = exp_a1green
|
||||
exp_a2green = exp_a1green
|
||||
exp_a2red = exp_a1green
|
||||
# exp_wd = exp_a1green
|
||||
|
||||
# Echte IOs erzeugen
|
||||
self.a1green = IOBase(self, [
|
||||
@@ -673,13 +688,8 @@ class Core(Base):
|
||||
:param value: 0=aus, 1=gruen, 2=rot
|
||||
"""
|
||||
if 0 <= value <= 3:
|
||||
proc_value = self._ba_devdata[self._slc_led.start]
|
||||
proc_value_calc = proc_value & 3
|
||||
if proc_value_calc == value:
|
||||
return
|
||||
# Set new value
|
||||
self._ba_devdata[self._slc_led.start] = \
|
||||
proc_value - proc_value_calc + value
|
||||
self.a1green(bool(value & 1))
|
||||
self.a1red(bool(value & 2))
|
||||
else:
|
||||
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
|
||||
"""
|
||||
if 0 <= value <= 3:
|
||||
value <<= 2
|
||||
proc_value = self._ba_devdata[self._slc_led.start]
|
||||
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
|
||||
self.a2green(bool(value & 1))
|
||||
self.a2red(bool(value & 2))
|
||||
else:
|
||||
raise ValueError("led status must be between 0 and 3")
|
||||
|
||||
@@ -907,12 +911,10 @@ class Connect(Core):
|
||||
exp_a3green = lst_led[4].export
|
||||
exp_a3red = lst_led[5].export
|
||||
exp_x2out = lst_led[6].export
|
||||
exp_wd = lst_led[7].export
|
||||
else:
|
||||
exp_a3green = lst_led[0].export
|
||||
exp_a3red = exp_a3green
|
||||
exp_x2out = exp_a3green
|
||||
exp_wd = exp_a3green
|
||||
lst_status = lst_myios[self._slc_statusbyte.start]
|
||||
if len(lst_status) == 8:
|
||||
exp_x2in = lst_status[6].export
|
||||
@@ -963,14 +965,8 @@ class Connect(Core):
|
||||
:param: value 0=aus, 1=gruen, 2=rot
|
||||
"""
|
||||
if 0 <= value <= 3:
|
||||
value <<= 4
|
||||
proc_value = self._ba_devdata[self._slc_led.start]
|
||||
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
|
||||
self.a3green(bool(value & 1))
|
||||
self.a3red(bool(value & 2))
|
||||
else:
|
||||
raise ValueError("led status must be between 0 and 3")
|
||||
|
||||
@@ -1013,7 +1009,7 @@ class Connect(Core):
|
||||
|
||||
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
|
||||
Objekt zugegriffen.
|
||||
@@ -1025,7 +1021,7 @@ class Compact(Base):
|
||||
def __setattr__(self, key, value):
|
||||
"""Verhindert Ueberschreibung der LEDs."""
|
||||
if hasattr(self, key) and key in (
|
||||
"a1green", "a1red", "a2green", "a2red"):
|
||||
"a1green", "a1red", "a2green", "a2red", "wd"):
|
||||
raise AttributeError(
|
||||
"direct assignment is not supported - use .value Attribute"
|
||||
)
|
||||
@@ -1102,13 +1098,8 @@ class Compact(Base):
|
||||
:param value: 0=aus, 1=gruen, 2=rot
|
||||
"""
|
||||
if 0 <= value <= 3:
|
||||
proc_value = self._ba_devdata[self._slc_led.start]
|
||||
proc_value_calc = proc_value & 3
|
||||
if proc_value_calc == value:
|
||||
return
|
||||
# Set new value
|
||||
self._ba_devdata[self._slc_led.start] = \
|
||||
proc_value - proc_value_calc + value
|
||||
self.a1green(bool(value & 1))
|
||||
self.a1red(bool(value & 2))
|
||||
else:
|
||||
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
|
||||
"""
|
||||
if 0 <= value <= 3:
|
||||
value <<= 2
|
||||
proc_value = self._ba_devdata[self._slc_led.start]
|
||||
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
|
||||
self.a2green(bool(value & 1))
|
||||
self.a2red(bool(value & 2))
|
||||
else:
|
||||
raise ValueError("led status must be between 0 and 3")
|
||||
|
||||
@@ -1160,6 +1145,250 @@ class Compact(Base):
|
||||
) * 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):
|
||||
"""Stellt ein DIO / DI / DO Modul dar."""
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import queue
|
||||
import warnings
|
||||
from math import ceil
|
||||
from threading import Event, Lock, Thread
|
||||
from time import sleep
|
||||
from timeit import default_timer
|
||||
|
||||
from revpimodio2 import BOTH, FALLING, RISING
|
||||
@@ -463,6 +464,7 @@ class ProcimgWriter(Thread):
|
||||
tup_fireth[0].func, tup_fireth[1], tup_fireth[2]
|
||||
)
|
||||
th.start()
|
||||
self._eventqth.task_done()
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
@@ -532,10 +534,10 @@ class ProcimgWriter(Thread):
|
||||
fh.seek(0)
|
||||
fh.readinto(bytesbuff)
|
||||
|
||||
if self._modio._monitoring or self._modio._direct_output:
|
||||
# Inputs und Outputs in Puffer
|
||||
for dev in self._modio._lst_refresh:
|
||||
with dev._filelock:
|
||||
if self._modio._monitoring or dev._shared_procimg:
|
||||
# Inputs und Outputs in Puffer
|
||||
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
|
||||
if self.__eventwork \
|
||||
and len(dev._dict_events) > 0 \
|
||||
@@ -544,8 +546,6 @@ class ProcimgWriter(Thread):
|
||||
|
||||
else:
|
||||
# Inputs in Puffer, Outputs in Prozessabbild
|
||||
for dev in self._modio._lst_refresh:
|
||||
with dev._filelock:
|
||||
dev._ba_devdata[dev._slc_inp] = \
|
||||
bytesbuff[dev._slc_inpoff]
|
||||
if self.__eventwork \
|
||||
@@ -602,8 +602,8 @@ class ProcimgWriter(Thread):
|
||||
self._eventq.put(tup_fire, False)
|
||||
del self.__dict_delay[tup_fire]
|
||||
|
||||
# Refresh abwarten
|
||||
self._work.wait(self._adjwait)
|
||||
# Sleep and not .wait (.wait uses system clock)
|
||||
sleep(self._adjwait)
|
||||
|
||||
# Wartezeit anpassen um echte self._refresh zu erreichen
|
||||
mrk_dt = default_timer()
|
||||
|
||||
@@ -624,7 +624,7 @@ class IOBase(object):
|
||||
# Versuchen egal welchen Typ in Bool zu konvertieren
|
||||
value = bool(value)
|
||||
|
||||
if self._parentdevice._modio._direct_output:
|
||||
if self._parentdevice._shared_procimg:
|
||||
# Direktes Schreiben der Outputs
|
||||
|
||||
if self._parentdevice._modio._run_on_pi:
|
||||
@@ -640,6 +640,7 @@ class IOBase(object):
|
||||
)
|
||||
except Exception as e:
|
||||
self._parentdevice._modio._gotioerror("ioset", e)
|
||||
return
|
||||
|
||||
elif hasattr(self._parentdevice._modio._myfh, "ioctl"):
|
||||
# IOCTL über Netzwerk
|
||||
@@ -653,6 +654,7 @@ class IOBase(object):
|
||||
except Exception as e:
|
||||
self._parentdevice._modio._gotioerror(
|
||||
"net_ioset", e)
|
||||
return
|
||||
|
||||
else:
|
||||
# IOCTL in Datei simulieren
|
||||
@@ -665,9 +667,7 @@ class IOBase(object):
|
||||
)
|
||||
except Exception as e:
|
||||
self._parentdevice._modio._gotioerror("file_ioset", e)
|
||||
|
||||
else:
|
||||
# Gepuffertes Schreiben der Outputs
|
||||
return
|
||||
|
||||
# Für Bitoperationen sperren
|
||||
self._parentdevice._filelock.acquire()
|
||||
@@ -704,7 +704,7 @@ class IOBase(object):
|
||||
)
|
||||
)
|
||||
|
||||
if self._parentdevice._modio._direct_output:
|
||||
if self._parentdevice._shared_procimg:
|
||||
with self._parentdevice._modio._myfh_lck:
|
||||
try:
|
||||
self._parentdevice._modio._myfh.seek(
|
||||
@@ -715,7 +715,8 @@ class IOBase(object):
|
||||
self._parentdevice._modio._myfh.flush()
|
||||
except IOError as e:
|
||||
self._parentdevice._modio._gotioerror("ioset", e)
|
||||
else:
|
||||
return
|
||||
|
||||
self._parentdevice._ba_devdata[self._slc_address] = value
|
||||
|
||||
def unreg_event(self, func=None, edge=None) -> None:
|
||||
|
||||
@@ -32,7 +32,7 @@ class RevPiModIO(object):
|
||||
"""
|
||||
|
||||
__slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", "_exit_level", \
|
||||
"_configrsc", "_direct_output", "_exit", "_imgwriter", "_ioerror", \
|
||||
"_configrsc", "_shared_procimg", "_exit", "_imgwriter", "_ioerror", \
|
||||
"_length", "_looprunning", "_lst_devselect", "_lst_refresh", \
|
||||
"_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \
|
||||
"_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \
|
||||
@@ -76,10 +76,10 @@ class RevPiModIO(object):
|
||||
|
||||
self._autorefresh = autorefresh
|
||||
self._configrsc = configrsc
|
||||
self._direct_output = shared_procimg or direct_output
|
||||
self._monitoring = monitoring
|
||||
self._procimg = "/dev/piControl0" if procimg is None else procimg
|
||||
self._simulator = simulator
|
||||
self._shared_procimg = shared_procimg or direct_output
|
||||
self._syncoutputs = syncoutputs
|
||||
|
||||
# TODO: bei simulator und procimg prüfen ob datei existiert / anlegen?
|
||||
@@ -254,6 +254,12 @@ class RevPiModIO(object):
|
||||
self, device, simulator=self._simulator
|
||||
)
|
||||
self.core = dev_new
|
||||
elif pt == 135:
|
||||
# RevPi Flat
|
||||
dev_new = devicemodule.Flat(
|
||||
self, device, simulator=self._simulator
|
||||
)
|
||||
self.core = dev_new
|
||||
else:
|
||||
# Base immer als Fallback verwenden
|
||||
dev_new = devicemodule.Base(
|
||||
@@ -682,7 +688,7 @@ class RevPiModIO(object):
|
||||
self._exit_level |= 2
|
||||
self.exit(full=True)
|
||||
|
||||
def cycleloop(self, func, cycletime=50):
|
||||
def cycleloop(self, func, cycletime=50, blocking=True):
|
||||
"""
|
||||
Startet den Cycleloop.
|
||||
|
||||
@@ -709,6 +715,7 @@ class RevPiModIO(object):
|
||||
|
||||
:param func: Funktion, die ausgefuehrt werden soll
|
||||
: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
|
||||
"""
|
||||
# Prüfen ob ein Loop bereits läuft
|
||||
@@ -730,6 +737,16 @@ class RevPiModIO(object):
|
||||
"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
|
||||
old_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:
|
||||
# Auf neue Daten warten und nur ausführen wenn set()
|
||||
if not self._imgwriter.newdata.wait(2.5):
|
||||
if not self._imgwriter.is_alive():
|
||||
self.exit(full=False)
|
||||
if self._imgwriter.is_alive():
|
||||
e = RuntimeError("no new io data in cycle loop")
|
||||
else:
|
||||
e = RuntimeError("autorefresh thread not running")
|
||||
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()
|
||||
|
||||
# Vor Aufruf der Funktion autorefresh sperren
|
||||
@@ -775,11 +798,13 @@ class RevPiModIO(object):
|
||||
except Exception as ex:
|
||||
if self._imgwriter.lck_refresh.locked():
|
||||
self._imgwriter.lck_refresh.release()
|
||||
if self._th_mainloop is None:
|
||||
self.exit(full=False)
|
||||
e = ex
|
||||
finally:
|
||||
# Cycleloop beenden
|
||||
self._looprunning = False
|
||||
self._th_mainloop = None
|
||||
|
||||
# Alte autorefresh Zeit setzen
|
||||
self._imgwriter.refresh = old_cycletime
|
||||
@@ -1023,6 +1048,7 @@ class RevPiModIO(object):
|
||||
|
||||
# Direct callen da Prüfung in io.IOBase.reg_event ist
|
||||
tup_fire[0].func(tup_fire[1], tup_fire[2])
|
||||
self._imgwriter._eventq.task_done()
|
||||
|
||||
# Laufzeitprüfung
|
||||
if runtime != -1 and \
|
||||
@@ -1094,7 +1120,7 @@ class RevPiModIO(object):
|
||||
# FileHandler sperren
|
||||
dev._filelock.acquire()
|
||||
|
||||
if self._monitoring or self._direct_output:
|
||||
if self._monitoring or dev._shared_procimg:
|
||||
# Alles vom Bus einlesen
|
||||
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
|
||||
else:
|
||||
@@ -1181,9 +1207,6 @@ class RevPiModIO(object):
|
||||
:param device: nur auf einzelnes Device anwenden
|
||||
:return: True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
"""
|
||||
if self._direct_output:
|
||||
return True
|
||||
|
||||
if self._monitoring:
|
||||
raise RuntimeError(
|
||||
"can not write process image, while system is in monitoring "
|
||||
@@ -1206,7 +1229,9 @@ class RevPiModIO(object):
|
||||
global_ex = None
|
||||
workokay = True
|
||||
for dev in mylist:
|
||||
if not dev._selfupdate:
|
||||
if dev._shared_procimg:
|
||||
continue
|
||||
elif not dev._selfupdate:
|
||||
dev._filelock.acquire()
|
||||
|
||||
# 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.
|
||||
|
||||
@@ -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
|
||||
via the cycletools in your cycle function.
|
||||
|
||||
Shortcut vor this source code:
|
||||
rpi = RevPiModIO(autorefresh=True, replace_io_file=replace_io_file)
|
||||
Shortcut for this source code:
|
||||
rpi = RevPiModIO(autorefresh=True, replace_io_file=..., debug=...)
|
||||
rpi.handlesignalend()
|
||||
return rpi.cycleloop(func, cycletime)
|
||||
|
||||
:param func: Function to run every set milliseconds
|
||||
:param cycletime: Cycle time in milliseconds
|
||||
: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
|
||||
"""
|
||||
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()
|
||||
return rpi.cycleloop(func, cycletime)
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ class NetFH(Thread):
|
||||
self.__config_changed = False
|
||||
self.__int_buff = 0
|
||||
self.__dictdirty = {}
|
||||
self.__flusherr = False
|
||||
self.__replace_ios_h = b''
|
||||
self.__pictory_h = b''
|
||||
self.__sockerr = Event()
|
||||
@@ -210,7 +209,6 @@ class NetFH(Thread):
|
||||
|
||||
self._slavesock = so
|
||||
self.__sockerr.clear()
|
||||
self.__flusherr = False
|
||||
|
||||
# Timeout setzen
|
||||
self.set_timeout(int(self.__timeout * 1000))
|
||||
@@ -219,18 +217,25 @@ class NetFH(Thread):
|
||||
for pos in self.__dictdirty:
|
||||
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
|
||||
:param recv_len: Anzahl der die empfangen werden sollen
|
||||
:return: Empfangende Bytes
|
||||
Will raise exception on closed network handler or network errors and
|
||||
set the sockerr flag.
|
||||
|
||||
:param send_bytes: Bytes to send or empty
|
||||
:param recv_len: Amount of bytes to receive
|
||||
:return: Received bytes
|
||||
"""
|
||||
if self.__sockend.is_set():
|
||||
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
|
||||
send_len = len(send_bytes)
|
||||
while counter < send_len:
|
||||
@@ -238,7 +243,7 @@ class NetFH(Thread):
|
||||
sent = self._slavesock.send(send_bytes[counter:])
|
||||
if sent == 0:
|
||||
self.__sockerr.set()
|
||||
raise IOError("lost network connection")
|
||||
raise IOError("lost network connection while send")
|
||||
counter += sent
|
||||
|
||||
self.__buff_recv.clear()
|
||||
@@ -247,12 +252,20 @@ class NetFH(Thread):
|
||||
self.__buff_block, min(recv_len, self.__buff_size)
|
||||
)
|
||||
if count == 0:
|
||||
self.__sockerr.set()
|
||||
raise IOError("lost network connection")
|
||||
raise IOError("lost network connection while receive")
|
||||
self.__buff_recv += self.__buff_block[: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:
|
||||
"""
|
||||
@@ -268,42 +281,32 @@ class NetFH(Thread):
|
||||
if self.__sockend.is_set():
|
||||
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
|
||||
if position is None:
|
||||
self.__dictdirty = {}
|
||||
self.__dictdirty.clear()
|
||||
elif position in self.__dictdirty:
|
||||
del self.__dictdirty[position]
|
||||
|
||||
if error:
|
||||
# Fehler nach übernahme der Daten auslösen um diese zu setzen
|
||||
try:
|
||||
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()
|
||||
|
||||
def close(self) -> None:
|
||||
@@ -334,32 +337,28 @@ class NetFH(Thread):
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("flush of closed file")
|
||||
|
||||
with self.__socklock:
|
||||
# b CM ii ii 00000000 b = 16
|
||||
if self.__int_buff == 0:
|
||||
return
|
||||
|
||||
try:
|
||||
self._slavesock.sendall(pack(
|
||||
# b CM ii ii 00000000 b = 16
|
||||
buff = self._direct_sr(pack(
|
||||
"=c2sHH8xc",
|
||||
HEADER_START, b'FD', self.__int_buff, len(self.__by_buff), HEADER_STOP
|
||||
) + self.__by_buff)
|
||||
) + self.__by_buff, 1)
|
||||
except Exception:
|
||||
self.__flusherr = True
|
||||
raise
|
||||
finally:
|
||||
# Puffer immer leeren
|
||||
self.__int_buff = 0
|
||||
self.__by_buff.clear()
|
||||
|
||||
# Rückmeldebyte auswerten
|
||||
blockok = self._slavesock.recv(1)
|
||||
if blockok != b'\x1e':
|
||||
if buff != b'\x1e':
|
||||
# ACL prüfen und ggf Fehler werfen
|
||||
self.__check_acl(blockok)
|
||||
self.__check_acl(buff)
|
||||
|
||||
self.__flusherr = True
|
||||
self.__sockerr.set()
|
||||
raise IOError("flush error on network")
|
||||
else:
|
||||
self.__flusherr = False
|
||||
|
||||
def get_closed(self) -> bool:
|
||||
"""
|
||||
@@ -416,18 +415,14 @@ class NetFH(Thread):
|
||||
if not (isinstance(arg, bytes) and len(arg) <= 1024):
|
||||
raise TypeError("arg must be <class 'bytes'>")
|
||||
|
||||
with self.__socklock:
|
||||
# b CM xx ii iiii0000 b = 16
|
||||
self._slavesock.sendall(pack(
|
||||
buff = self._direct_sr(pack(
|
||||
"=c2s2xHI4xc",
|
||||
HEADER_START, b'IC', len(arg), request, HEADER_STOP
|
||||
) + arg)
|
||||
|
||||
# Rückmeldebyte auswerten
|
||||
blockok = self._slavesock.recv(1)
|
||||
if blockok != b'\x1e':
|
||||
) + arg, 1)
|
||||
if buff != b'\x1e':
|
||||
# ACL prüfen und ggf Fehler werfen
|
||||
self.__check_acl(blockok)
|
||||
self.__check_acl(buff)
|
||||
|
||||
self.__sockerr.set()
|
||||
raise IOError("ioctl error on network")
|
||||
@@ -444,28 +439,14 @@ class NetFH(Thread):
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("read of closed file")
|
||||
|
||||
with self.__socklock:
|
||||
# b CM ii ii 00000000 b = 16
|
||||
self._slavesock.sendall(pack(
|
||||
buff = self._direct_sr(pack(
|
||||
"=c2sHH8xc",
|
||||
HEADER_START, b'DA', self.__position, length, HEADER_STOP
|
||||
))
|
||||
), length)
|
||||
|
||||
self.__position += length
|
||||
|
||||
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)
|
||||
return buff
|
||||
|
||||
def readinto(self, buffer: bytearray) -> int:
|
||||
"""
|
||||
@@ -480,29 +461,14 @@ class NetFH(Thread):
|
||||
raise ValueError("read of closed file")
|
||||
|
||||
length = len(buffer)
|
||||
with self.__socklock:
|
||||
|
||||
# b CM ii ii 00000000 b = 16
|
||||
self._slavesock.sendall(pack(
|
||||
buff = self._direct_sr(pack(
|
||||
"=c2sHH8xc",
|
||||
HEADER_START, b'DA', self.__position, length, HEADER_STOP
|
||||
))
|
||||
|
||||
net_buffer = b''
|
||||
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")
|
||||
net_buffer += self.__buff_block[:count]
|
||||
length -= count
|
||||
|
||||
# We received the correct amount of bytes here
|
||||
self.__position += length
|
||||
buffer[:] = net_buffer
|
||||
), length)
|
||||
|
||||
buffer[:] = buff
|
||||
return len(buffer)
|
||||
|
||||
def readpictory(self) -> bytes:
|
||||
@@ -519,31 +485,9 @@ class NetFH(Thread):
|
||||
"could not read/parse piCtory configuration over network"
|
||||
)
|
||||
|
||||
with self.__socklock:
|
||||
self._slavesock.sendall(_syspictory)
|
||||
|
||||
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:])
|
||||
buff = self._direct_sr(_syspictory, 4)
|
||||
recv_length, = unpack("=I", buff)
|
||||
return self._direct_sr(b'', recv_length)
|
||||
|
||||
def readreplaceio(self) -> bytes:
|
||||
"""
|
||||
@@ -559,31 +503,9 @@ class NetFH(Thread):
|
||||
"replace_io_file: could not read/parse over network"
|
||||
)
|
||||
|
||||
with self.__socklock:
|
||||
self._slavesock.sendall(_sysreplaceio)
|
||||
|
||||
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:])
|
||||
buff = self._direct_sr(_sysreplaceio, 4)
|
||||
recv_length, = unpack("=I", buff)
|
||||
return self._direct_sr(b'', recv_length)
|
||||
|
||||
def run(self) -> None:
|
||||
"""Handler fuer Synchronisierung."""
|
||||
@@ -622,7 +544,7 @@ class NetFH(Thread):
|
||||
self.__buff_block, recv_lenght
|
||||
)
|
||||
if count == 0:
|
||||
raise IOError("lost network connection")
|
||||
raise IOError("lost network connection on sync")
|
||||
self.__buff_recv += self.__buff_block[:count]
|
||||
recv_lenght -= count
|
||||
|
||||
@@ -666,34 +588,26 @@ class NetFH(Thread):
|
||||
if self.__sockend.is_set():
|
||||
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
|
||||
self.__dictdirty[position] = dirtybytes
|
||||
|
||||
if error:
|
||||
# Fehler nach übernahme der Daten auslösen um diese zu setzen
|
||||
try:
|
||||
# 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()
|
||||
|
||||
def set_timeout(self, value: int) -> None:
|
||||
@@ -709,20 +623,15 @@ class NetFH(Thread):
|
||||
self.__set_systimeout(value)
|
||||
|
||||
try:
|
||||
self.__socklock.acquire()
|
||||
|
||||
# b CM ii xx 00000000 b = 16
|
||||
self._slavesock.sendall(pack(
|
||||
buff = self._direct_sr(pack(
|
||||
"=c2sH10xc",
|
||||
HEADER_START, b'CF', value, HEADER_STOP
|
||||
))
|
||||
check = self._slavesock.recv(1)
|
||||
if check != b'\x1e':
|
||||
), 1)
|
||||
if buff != b'\x1e':
|
||||
raise IOError("set timeout error on network")
|
||||
except Exception:
|
||||
self.__sockerr.set()
|
||||
finally:
|
||||
self.__socklock.release()
|
||||
|
||||
def tell(self) -> int:
|
||||
"""
|
||||
@@ -747,14 +656,13 @@ class NetFH(Thread):
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("write to closed file")
|
||||
|
||||
if self.__flusherr:
|
||||
raise IOError("I/O error since last flush")
|
||||
if self.__sockerr.is_set():
|
||||
raise IOError("not allowed while reconnect")
|
||||
|
||||
with self.__socklock:
|
||||
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") + \
|
||||
len(bytebuff).to_bytes(length=2, byteorder="little") + \
|
||||
bytebuff
|
||||
@@ -802,7 +710,7 @@ class RevPiNetIO(_RevPiModIO):
|
||||
:param direct_output: Deprecated, use shared_procimg
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -15,9 +15,8 @@ __license__ = "LGPLv3"
|
||||
# - RevPiGateCANopen_20161102_1_0.rap
|
||||
|
||||
|
||||
# Can be used for :
|
||||
# RevPi AIO 1.0 (RevPiAIO_20170301_1_0.rap)
|
||||
class AIO:
|
||||
"""Memory value mappings for RevPi AIO 1.0 (RevPiAIO_20170301_1_0.rap)."""
|
||||
OUT_RANGE_OFF = 0 # Off
|
||||
OUT_RANGE_0_5V = 1 # 0 - 5V
|
||||
OUT_RANGE_0_10V = 2 # 0 - 10V
|
||||
@@ -89,9 +88,8 @@ class AIO:
|
||||
RTD_4_WIRE = 1 # 4-wire
|
||||
|
||||
|
||||
# Can be used for :
|
||||
# RevPi DI 1.0 (RevPiDI_20160818_1_0.rap)
|
||||
class DI:
|
||||
"""Memory value mappings for RevPi DI 1.0 (RevPiDI_20160818_1_0.rap)."""
|
||||
IN_MODE_DIRECT = 0 # Direct
|
||||
IN_MODE_COUNT_RISING = 1 # Counter, rising edge
|
||||
IN_MODE_COUNT_FALLING = 2 # Counter, falling edge
|
||||
@@ -103,9 +101,8 @@ class DI:
|
||||
IN_DEBOUNCE_3MS = 3 # 3ms
|
||||
|
||||
|
||||
# Can be used for :
|
||||
# RevPi DO 1.0 (RevPiDO_20160818_1_0.rap)
|
||||
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_80HZ = 2 # 80Hz 2%
|
||||
OUT_PWM_FREQ_160HZ = 4 # 160Hz 4%
|
||||
@@ -113,7 +110,19 @@ class DO:
|
||||
OUT_PWM_FREQ_400HZ = 10 # 400Hz 10%
|
||||
|
||||
|
||||
# Can be used for :
|
||||
# RevPi DIO 1.0 (RevPiDIO_20160818_1_0.rap)
|
||||
class DIO(DI, DO):
|
||||
"""Memory value mappings for RevPi DIO 1.0 (RevPiDIO_20160818_1_0.rap)."""
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user