style: Format everything with black (100 characters per line)

Signed-off-by: Sven Sager <akira@narux.de>
This commit is contained in:
2023-10-27 14:44:44 +02:00
parent ba708fc145
commit ecf91f56db
8 changed files with 1164 additions and 984 deletions

View File

@@ -14,10 +14,25 @@ fuehrt das Modul bei Datenaenderung aus.
"""
__all__ = [
"IOEvent",
"RevPiModIO", "RevPiModIODriver", "RevPiModIOSelected", "run_plc",
"RevPiNetIO", "RevPiNetIODriver", "RevPiNetIOSelected", "run_net_plc",
"Cycletools", "EventCallback",
"ProductType", "DeviceType", "AIO", "COMPACT", "DI", "DO", "DIO", "FLAT", "MIO",
"RevPiModIO",
"RevPiModIODriver",
"RevPiModIOSelected",
"run_plc",
"RevPiNetIO",
"RevPiNetIODriver",
"RevPiNetIOSelected",
"run_net_plc",
"Cycletools",
"EventCallback",
"ProductType",
"DeviceType",
"AIO",
"COMPACT",
"DI",
"DO",
"DIO",
"FLAT",
"MIO",
]
__author__ = "Sven Sager <akira@revpimodio.org>"
__copyright__ = "Copyright (C) 2023 Sven Sager"

View File

@@ -29,11 +29,11 @@ def acheck(check_type, **kwargs) -> None:
for var_name in kwargs:
none_okay = var_name.endswith("_noneok")
if not (isinstance(kwargs[var_name], check_type) or
none_okay and kwargs[var_name] is None):
if not (isinstance(kwargs[var_name], check_type) or none_okay and kwargs[var_name] is None):
msg = "Argument '{0}' must be {1}{2}".format(
var_name.rstrip("_noneok"), str(check_type),
" or <class 'NoneType'>" if none_okay else ""
var_name.rstrip("_noneok"),
str(check_type),
" or <class 'NoneType'>" if none_okay else "",
)
raise TypeError(msg)

File diff suppressed because it is too large Load Diff

View File

@@ -87,12 +87,31 @@ class Cycletools:
Lampen synchron blinken zu lassen.
"""
__slots__ = "__cycle", "__cycletime", "__ucycle", "__dict_ton", \
"__dict_tof", "__dict_tp", "__dict_change", \
"_start_timer", "core", "device", \
"first", "io", "last", "var", \
"flag1c", "flag5c", "flag10c", "flag15c", "flag20c", \
"flank5c", "flank10c", "flank15c", "flank20c"
__slots__ = (
"__cycle",
"__cycletime",
"__ucycle",
"__dict_ton",
"__dict_tof",
"__dict_tp",
"__dict_change",
"_start_timer",
"core",
"device",
"first",
"io",
"last",
"var",
"flag1c",
"flag5c",
"flag10c",
"flag15c",
"flag20c",
"flank5c",
"flank10c",
"flank15c",
"flank20c",
)
def __init__(self, cycletime, revpi_object):
"""Init Cycletools class."""
@@ -208,16 +227,13 @@ class Cycletools:
else:
value = io.get_value()
return self.__dict_change[io] != value and (
value and edge == RISING or
not value and edge == FALLING
value and edge == RISING or not value and edge == FALLING
)
else:
if not isinstance(io, IOBase):
raise TypeError("parameter 'io' must be an io object")
if not (edge == BOTH or type(io.value) == bool):
raise ValueError(
"parameter 'edge' can be used with bit io objects only"
)
raise ValueError("parameter 'edge' can be used with bit io objects only")
self.__dict_change[io] = None
return False
@@ -283,8 +299,7 @@ class Cycletools:
:param milliseconds: Millisekunden, der Verzoegerung wenn neu gestartet
"""
if self.__dict_ton.get(name, [-1])[0] == -1:
self.__dict_ton[name] = \
[ceil(milliseconds / self.__cycletime), True]
self.__dict_ton[name] = [ceil(milliseconds / self.__cycletime), True]
else:
self.__dict_ton[name][1] = True
@@ -326,8 +341,7 @@ class Cycletools:
:param milliseconds: Millisekunden, die der Impuls anstehen soll
"""
if self.__dict_tp.get(name, [-1])[0] == -1:
self.__dict_tp[name] = \
[ceil(milliseconds / self.__cycletime), True]
self.__dict_tp[name] = [ceil(milliseconds / self.__cycletime), True]
else:
self.__dict_tp[name][1] = True
@@ -364,9 +378,19 @@ class ProcimgWriter(Thread):
Event-Handling verwendet.
"""
__slots__ = "__dict_delay", "__eventth", "_eventqth", "__eventwork", \
"_eventq", "_modio", \
"_refresh", "_work", "daemon", "lck_refresh", "newdata"
__slots__ = (
"__dict_delay",
"__eventth",
"_eventqth",
"__eventwork",
"_eventq",
"_modio",
"_refresh",
"_work",
"daemon",
"lck_refresh",
"newdata",
)
def __init__(self, parentmodio):
"""Init ProcimgWriter class."""
@@ -387,43 +411,38 @@ class ProcimgWriter(Thread):
def __check_change(self, dev) -> None:
"""Findet Aenderungen fuer die Eventueberwachung."""
for io_event in dev._dict_events:
if dev._ba_datacp[io_event._slc_address] == \
dev._ba_devdata[io_event._slc_address]:
if dev._ba_datacp[io_event._slc_address] == dev._ba_devdata[io_event._slc_address]:
continue
if io_event._bitshift:
boolcp = dev._ba_datacp[io_event._slc_address.start] \
& io_event._bitshift
boolor = dev._ba_devdata[io_event._slc_address.start] \
& io_event._bitshift
boolcp = dev._ba_datacp[io_event._slc_address.start] & io_event._bitshift
boolor = dev._ba_devdata[io_event._slc_address.start] & io_event._bitshift
if boolor == boolcp:
continue
for regfunc in dev._dict_events[io_event]:
if regfunc.edge == BOTH \
or regfunc.edge == RISING and boolor \
or regfunc.edge == FALLING and not boolor:
if (
regfunc.edge == BOTH
or regfunc.edge == RISING
and boolor
or regfunc.edge == FALLING
and not boolor
):
if regfunc.delay == 0:
if regfunc.as_thread:
self._eventqth.put(
(regfunc, io_event._name, io_event.value),
False
)
self._eventqth.put((regfunc, io_event._name, io_event.value), False)
else:
self._eventq.put(
(regfunc, io_event._name, io_event.value),
False
)
self._eventq.put((regfunc, io_event._name, io_event.value), False)
else:
# Verzögertes Event in dict einfügen
tup_fire = (
regfunc, io_event._name, io_event.value,
regfunc,
io_event._name,
io_event.value,
io_event,
)
if regfunc.overwrite \
or tup_fire not in self.__dict_delay:
if regfunc.overwrite or tup_fire not in self.__dict_delay:
self.__dict_delay[tup_fire] = ceil(
regfunc.delay / 1000 / self._refresh
)
@@ -431,26 +450,19 @@ class ProcimgWriter(Thread):
for regfunc in dev._dict_events[io_event]:
if regfunc.delay == 0:
if regfunc.as_thread:
self._eventqth.put(
(regfunc, io_event._name, io_event.value),
False
)
self._eventqth.put((regfunc, io_event._name, io_event.value), False)
else:
self._eventq.put(
(regfunc, io_event._name, io_event.value),
False
)
self._eventq.put((regfunc, io_event._name, io_event.value), False)
else:
# Verzögertes Event in dict einfügen
tup_fire = (
regfunc, io_event._name, io_event.value,
regfunc,
io_event._name,
io_event.value,
io_event,
)
if regfunc.overwrite \
or tup_fire not in self.__dict_delay:
self.__dict_delay[tup_fire] = ceil(
regfunc.delay / 1000 / self._refresh
)
if regfunc.overwrite or tup_fire not in self.__dict_delay:
self.__dict_delay[tup_fire] = ceil(regfunc.delay / 1000 / self._refresh)
# Nach Verarbeitung aller IOs die Bytes kopieren (Lock ist noch drauf)
dev._ba_datacp = dev._ba_devdata[:]
@@ -460,9 +472,7 @@ class ProcimgWriter(Thread):
while self.__eventwork:
try:
tup_fireth = self._eventqth.get(timeout=1)
th = EventCallback(
tup_fireth[0].func, tup_fireth[1], tup_fireth[2]
)
th = EventCallback(tup_fireth[0].func, tup_fireth[1], tup_fireth[2])
th.start()
self._eventqth.task_done()
except queue.Empty:
@@ -524,7 +534,7 @@ class ProcimgWriter(Thread):
warnings.warn(
"cycle time of {0} ms exceeded in your cycle function"
"".format(int(self._refresh * 1000)),
RuntimeWarning
RuntimeWarning,
)
mrk_delay = self._refresh
# Nur durch cycleloop erreichbar - keine verzögerten Events
@@ -545,23 +555,25 @@ class ProcimgWriter(Thread):
# Read all device bytes, because it is shared
fh.seek(dev.offset)
bytesbuff[dev._slc_devoff] = \
fh.read(len(dev._ba_devdata))
bytesbuff[dev._slc_devoff] = fh.read(len(dev._ba_devdata))
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 \
and dev._ba_datacp != dev._ba_devdata:
if (
self.__eventwork
and len(dev._dict_events) > 0
and dev._ba_datacp != dev._ba_devdata
):
self.__check_change(dev)
else:
# Inputs in Puffer, Outputs in Prozessabbild
dev._ba_devdata[dev._slc_inp] = \
bytesbuff[dev._slc_inpoff]
if self.__eventwork \
and len(dev._dict_events) > 0 \
and dev._ba_datacp != dev._ba_devdata:
dev._ba_devdata[dev._slc_inp] = bytesbuff[dev._slc_inpoff]
if (
self.__eventwork
and len(dev._dict_events) > 0
and dev._ba_datacp != dev._ba_devdata
):
self.__check_change(dev)
fh.seek(dev._slc_outoff.start)
@@ -579,16 +591,13 @@ class ProcimgWriter(Thread):
else:
if not mrk_warn:
if self._modio._debug == 0:
warnings.warn(
"recover from io errors on process image",
RuntimeWarning
)
warnings.warn("recover from io errors on process image", RuntimeWarning)
else:
warnings.warn(
"recover from io errors on process image - total "
"count of {0} errors now"
"".format(self._modio._ioerror),
RuntimeWarning
RuntimeWarning,
)
mrk_warn = True
@@ -600,8 +609,7 @@ class ProcimgWriter(Thread):
# Verzögerte Events prüfen
if self.__eventwork:
for tup_fire in tuple(self.__dict_delay.keys()):
if tup_fire[0].overwrite and \
tup_fire[3].value != tup_fire[2]:
if tup_fire[0].overwrite and tup_fire[3].value != tup_fire[2]:
del self.__dict_delay[tup_fire]
else:
self.__dict_delay[tup_fire] -= 1
@@ -617,9 +625,8 @@ class ProcimgWriter(Thread):
# Second default_timer call include calculation time from above
if default_timer() - ot > self._refresh:
warnings.warn(
"io refresh time of {0} ms exceeded!"
"".format(int(self._refresh * 1000)),
RuntimeWarning
"io refresh time of {0} ms exceeded!".format(int(self._refresh * 1000)),
RuntimeWarning,
)
mrk_delay = 0.0
else:
@@ -641,8 +648,6 @@ class ProcimgWriter(Thread):
if type(value) == int and 5 <= value <= 2000:
self._refresh = value / 1000
else:
raise ValueError(
"refresh time must be 5 to 2000 milliseconds"
)
raise ValueError("refresh time must be 5 to 2000 milliseconds")
refresh = property(get_refresh, set_refresh)

View File

@@ -8,8 +8,7 @@ import struct
from re import match as rematch
from threading import Event
from ._internal import consttostr, RISING, FALLING, BOTH, INP, OUT, \
MEM, PROCESS_IMAGE_SIZE
from ._internal import consttostr, RISING, FALLING, BOTH, INP, OUT, MEM, PROCESS_IMAGE_SIZE
try:
# Funktioniert nur auf Unix
@@ -69,8 +68,16 @@ class IOList(object):
self.__dict_iobyte[io_del.address][io_del._bitaddress] = None
# Do not use any() because we want to know None, not 0
if self.__dict_iobyte[io_del.address] == \
[None, None, None, None, None, None, None, None]:
if self.__dict_iobyte[io_del.address] == [
None,
None,
None,
None,
None,
None,
None,
None,
]:
self.__dict_iobyte[io_del.address] = []
else:
self.__dict_iobyte[io_del.address].remove(io_del)
@@ -110,9 +117,7 @@ class IOList(object):
elif type(key) == slice:
return [
self.__dict_iobyte[int_io]
for int_io in range(
key.start, key.stop, 1 if key.step is None else key.step
)
for int_io in range(key.start, key.stop, 1 if key.step is None else key.step)
]
else:
return getattr(self, key)
@@ -143,15 +148,10 @@ class IOList(object):
def __setattr__(self, key, value):
"""Verbietet aus Leistungsguenden das direkte Setzen von Attributen."""
if key in (
"_IOList__dict_iobyte",
"_IOList__dict_iorefname"
):
if key in ("_IOList__dict_iobyte", "_IOList__dict_iorefname"):
object.__setattr__(self, key, value)
else:
raise AttributeError(
"direct assignment is not supported - use .value Attribute"
)
raise AttributeError("direct assignment is not supported - use .value Attribute")
def __private_replace_oldio_with_newio(self, io) -> None:
"""
@@ -168,16 +168,17 @@ class IOList(object):
scan_stop = scan_start + (1 if io._length == 0 else io._length)
# Defaultvalue über mehrere Bytes sammeln
calc_defaultvalue = b''
calc_defaultvalue = b""
for i in range(scan_start, scan_stop):
for oldio in self.__dict_iobyte[i]:
if type(oldio) == StructIO:
# Hier gibt es schon einen neuen IO
if oldio._bitshift:
if io._bitshift == oldio._bitshift \
and io._slc_address == oldio._slc_address:
if (
io._bitshift == oldio._bitshift
and io._slc_address == oldio._slc_address
):
raise MemoryError(
"bit {0} already assigned to '{1}'".format(
io._bitaddress, oldio._name
@@ -186,9 +187,7 @@ class IOList(object):
else:
# Bereits überschriebene bytes sind ungültig
raise MemoryError(
"new io '{0}' overlaps memory of '{1}'".format(
io._name, oldio._name
)
"new io '{0}' overlaps memory of '{1}'".format(io._name, oldio._name)
)
elif oldio is not None:
# IOs im Speicherbereich des neuen IO merken
@@ -201,8 +200,7 @@ class IOList(object):
if io._byteorder == "little":
calc_defaultvalue += oldio._defaultvalue
else:
calc_defaultvalue = \
oldio._defaultvalue + calc_defaultvalue
calc_defaultvalue = oldio._defaultvalue + calc_defaultvalue
# ios aus listen entfernen
delattr(self, oldio._name)
@@ -211,9 +209,7 @@ class IOList(object):
# Nur bei StructIO und keiner gegebenen defaultvalue übernehmen
if io._bitshift:
io_byte_address = io._parentio_address - io.address
io._defaultvalue = bool(
io._parentio_defaultvalue[io_byte_address] & io._bitshift
)
io._defaultvalue = bool(io._parentio_defaultvalue[io_byte_address] & io._bitshift)
else:
io._defaultvalue = calc_defaultvalue
@@ -226,8 +222,7 @@ class IOList(object):
if isinstance(new_io, IOBase):
if hasattr(self, new_io._name):
raise AttributeError(
"attribute {0} already exists - can not set io"
"".format(new_io._name)
"attribute {0} already exists - can not set io".format(new_io._name)
)
if type(new_io) is StructIO:
@@ -239,8 +234,16 @@ class IOList(object):
if new_io._bitshift:
if len(self.__dict_iobyte[new_io.address]) != 8:
# "schnell" 8 Einträge erstellen da es BIT IOs sind
self.__dict_iobyte[new_io.address] += \
[None, None, None, None, None, None, None, None]
self.__dict_iobyte[new_io.address] += [
None,
None,
None,
None,
None,
None,
None,
None,
]
self.__dict_iobyte[new_io.address][new_io._bitaddress] = new_io
else:
self.__dict_iobyte[new_io.address].append(new_io)
@@ -289,13 +292,26 @@ class IOBase(object):
auch als <class 'int'> verwendet werden koennen.
"""
__slots__ = "__bit_ioctl_off", "__bit_ioctl_on", "_bitaddress", \
"_bitshift", "_bitlength", "_byteorder", "_defaultvalue", \
"_export", "_iotype", "_length", "_name", "_parentdevice", \
"_read_only_io", "_signed", "_slc_address", "bmk"
__slots__ = (
"__bit_ioctl_off",
"__bit_ioctl_on",
"_bitaddress",
"_bitshift",
"_bitlength",
"_byteorder",
"_defaultvalue",
"_export",
"_iotype",
"_length",
"_name",
"_parentdevice",
"_read_only_io",
"_signed",
"_slc_address",
"bmk",
)
def __init__(self, parentdevice, valuelist: list, iotype: int,
byteorder: str, signed: bool):
def __init__(self, parentdevice, valuelist: list, iotype: int, byteorder: str, signed: bool):
"""
Instantiierung der IOBase-Klasse.
@@ -312,8 +328,7 @@ class IOBase(object):
# Bitadressen auf Bytes aufbrechen und umrechnen
self._bitaddress = -1 if valuelist[7] == "" else int(valuelist[7]) % 8
self._bitshift = None if self._bitaddress == -1 \
else 1 << self._bitaddress
self._bitshift = None if self._bitaddress == -1 else 1 << self._bitaddress
# Längenberechnung
self._bitlength = int(valuelist[2])
@@ -333,9 +348,7 @@ class IOBase(object):
if self._bitshift:
# Höhere Bits als 7 auf nächste Bytes umbrechen
int_startaddress += int(int(valuelist[7]) / 8)
self._slc_address = slice(
int_startaddress, int_startaddress + 1
)
self._slc_address = slice(int_startaddress, int_startaddress + 1)
# Defaultvalue ermitteln, sonst False
if valuelist[1] is None and type(self) == StructIO:
@@ -347,14 +360,10 @@ class IOBase(object):
self._defaultvalue = False
# Ioctl für Bitsetzung setzen
self.__bit_ioctl_off = struct.pack(
"<HB", self._get_address(), self._bitaddress
)
self.__bit_ioctl_on = self.__bit_ioctl_off + b'\x01'
self.__bit_ioctl_off = struct.pack("<HB", self._get_address(), self._bitaddress)
self.__bit_ioctl_on = self.__bit_ioctl_off + b"\x01"
else:
self._slc_address = slice(
int_startaddress, int_startaddress + self._length
)
self._slc_address = slice(int_startaddress, int_startaddress + self._length)
if str(valuelist[1]).isdigit():
# Defaultvalue aus Zahl in Bytes umrechnen
self._defaultvalue = int(valuelist[1]).to_bytes(
@@ -382,8 +391,7 @@ class IOBase(object):
try:
buff = valuelist[1].encode("ASCII")
if len(buff) <= self._length:
self._defaultvalue = \
buff + bytes(self._length - len(buff))
self._defaultvalue = buff + bytes(self._length - len(buff))
except Exception:
pass
@@ -394,10 +402,7 @@ class IOBase(object):
:return: <class 'bool'> Nur False wenn False oder 0 sonst True
"""
if self._bitshift:
return bool(
self._parentdevice._ba_devdata[self._slc_address.start]
& self._bitshift
)
return bool(self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift)
else:
return any(self._parentdevice._ba_devdata[self._slc_address])
@@ -406,8 +411,7 @@ class IOBase(object):
# Inline get_value()
if self._bitshift:
return bool(
self._parentdevice._ba_devdata[self._slc_address.start]
& self._bitshift
self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift
)
else:
return bytes(self._parentdevice._ba_devdata[self._slc_address])
@@ -430,8 +434,9 @@ class IOBase(object):
"""
return self._name
def __reg_xevent(self, func, delay: int, edge: int, as_thread: bool,
overwrite: bool, prefire: bool) -> None:
def __reg_xevent(
self, func, delay: int, edge: int, as_thread: bool, overwrite: bool, prefire: bool
) -> None:
"""
Verwaltet reg_event und reg_timerevent.
@@ -444,26 +449,19 @@ class IOBase(object):
"""
# Prüfen ob Funktion callable ist
if not callable(func):
raise ValueError(
"registered function '{0}' is not callable".format(func)
)
raise ValueError("registered function '{0}' is not callable".format(func))
if type(delay) != int or delay < 0:
raise ValueError(
"'delay' must be <class 'int'> and greater or equal 0"
)
raise ValueError("'delay' must be <class 'int'> and greater or equal 0")
if edge != BOTH and not self._bitshift:
raise ValueError(
"parameter 'edge' can be used with bit io objects only"
)
raise ValueError("parameter 'edge' can be used with bit io objects only")
if prefire and self._parentdevice._modio._looprunning:
raise RuntimeError(
"prefire can not be used if mainloop is running"
)
raise RuntimeError("prefire can not be used if mainloop is running")
if self not in self._parentdevice._dict_events:
with self._parentdevice._filelock:
self._parentdevice._dict_events[self] = \
[IOEvent(func, edge, as_thread, delay, overwrite, prefire)]
self._parentdevice._dict_events[self] = [
IOEvent(func, edge, as_thread, delay, overwrite, prefire)
]
else:
# Prüfen ob Funktion schon registriert ist
for regfunc in self._parentdevice._dict_events[self]:
@@ -476,10 +474,7 @@ class IOBase(object):
raise RuntimeError(
"io '{0}' with function '{1}' already in list "
"with edge '{2}' - edge '{3}' not allowed anymore"
"".format(
self._name, func,
consttostr(regfunc.edge), consttostr(edge)
)
"".format(self._name, func, consttostr(regfunc.edge), consttostr(edge))
)
else:
raise RuntimeError(
@@ -490,9 +485,7 @@ class IOBase(object):
elif regfunc.edge == edge:
raise RuntimeError(
"io '{0}' with function '{1}' for given edge '{2}' "
"already in list".format(
self._name, func, consttostr(edge)
)
"already in list".format(self._name, func, consttostr(edge))
)
# Eventfunktion einfügen
@@ -548,9 +541,7 @@ class IOBase(object):
if self._bitshift:
# Write single bit to process image
value = \
self._parentdevice._ba_devdata[self._slc_address.start] & \
self._bitshift
value = self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift
if self._parentdevice._modio._run_on_pi:
# IOCTL auf dem RevPi
with self._parentdevice._modio._myfh_lck:
@@ -559,8 +550,7 @@ class IOBase(object):
ioctl(
self._parentdevice._modio._myfh,
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
self.__bit_ioctl_on if value else self.__bit_ioctl_off,
)
except Exception as e:
self._parentdevice._modio._gotioerror("ioset", e)
@@ -572,12 +562,10 @@ class IOBase(object):
try:
self._parentdevice._modio._myfh.ioctl(
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
self.__bit_ioctl_on if value else self.__bit_ioctl_off,
)
except Exception as e:
self._parentdevice._modio._gotioerror(
"net_ioset", e)
self._parentdevice._modio._gotioerror("net_ioset", e)
return False
else:
@@ -586,8 +574,7 @@ class IOBase(object):
# Set value durchführen (Funktion K+16)
self._parentdevice._modio._simulate_ioctl(
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
self.__bit_ioctl_on if value else self.__bit_ioctl_off,
)
except Exception as e:
self._parentdevice._modio._gotioerror("file_ioset", e)
@@ -598,9 +585,7 @@ class IOBase(object):
value = bytes(self._parentdevice._ba_devdata[self._slc_address])
with self._parentdevice._modio._myfh_lck:
try:
self._parentdevice._modio._myfh.seek(
self._get_address()
)
self._parentdevice._modio._myfh.seek(self._get_address())
self._parentdevice._modio._myfh.write(value)
if self._parentdevice._modio._buffedwrite:
self._parentdevice._modio._myfh.flush()
@@ -625,15 +610,11 @@ class IOBase(object):
:return: IO-Wert als <class 'bytes'> oder <class 'bool'>
"""
if self._bitshift:
return bool(
self._parentdevice._ba_devdata[self._slc_address.start]
& self._bitshift
)
return bool(self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift)
else:
return bytes(self._parentdevice._ba_devdata[self._slc_address])
def reg_event(
self, func, delay=0, edge=BOTH, as_thread=False, prefire=False):
def reg_event(self, func, delay=0, edge=BOTH, as_thread=False, prefire=False):
"""
Registriert fuer IO ein Event bei der Eventueberwachung.
@@ -652,8 +633,7 @@ class IOBase(object):
"""
self.__reg_xevent(func, delay, edge, as_thread, True, prefire)
def reg_timerevent(
self, func, delay, edge=BOTH, as_thread=False, prefire=False):
def reg_timerevent(self, func, delay, edge=BOTH, as_thread=False, prefire=False):
"""
Registriert fuer IO einen Timer, welcher nach delay func ausfuehrt.
@@ -685,20 +665,13 @@ class IOBase(object):
if self._iotype == INP:
if self._parentdevice._modio._simulator:
raise RuntimeError(
"can not write to output '{0}' in simulator mode"
"".format(self._name)
"can not write to output '{0}' in simulator mode".format(self._name)
)
else:
raise RuntimeError(
"can not write to input '{0}'".format(self._name)
)
raise RuntimeError("can not write to input '{0}'".format(self._name))
elif self._iotype == MEM:
raise RuntimeError(
"can not write to memory '{0}'".format(self._name)
)
raise RuntimeError(
"the io object '{0}' is read only".format(self._name)
)
raise RuntimeError("can not write to memory '{0}'".format(self._name))
raise RuntimeError("the io object '{0}' is read only".format(self._name))
if self._bitshift:
# Versuchen egal welchen Typ in Bool zu konvertieren
@@ -722,8 +695,7 @@ class IOBase(object):
int_byte -= self._bitshift
# Zurückschreiben wenn verändert
self._parentdevice._ba_devdata[self._slc_address.start] = \
int_byte
self._parentdevice._ba_devdata[self._slc_address.start] = int_byte
self._parentdevice._filelock.release()
@@ -738,9 +710,7 @@ class IOBase(object):
if self._length != len(value):
raise ValueError(
"'{0}' requires a <class 'bytes'> object of "
"length {1}, but {2} was given".format(
self._name, self._length, len(value)
)
"length {1}, but {2} was given".format(self._name, self._length, len(value))
)
if self._parentdevice._shared_procimg:
@@ -764,8 +734,7 @@ class IOBase(object):
else:
newlist = []
for regfunc in self._parentdevice._dict_events[self]:
if regfunc.func != func or edge is not None \
and regfunc.edge != edge:
if regfunc.func != func or edge is not None and regfunc.edge != edge:
newlist.append(regfunc)
# Wenn Funktionen übrig bleiben, diese übernehmen
@@ -832,17 +801,11 @@ class IOBase(object):
"revpimodio2.FALLING or revpimodio2.BOTH"
)
if not (exitevent is None or type(exitevent) == Event):
raise TypeError(
"parameter 'exitevent' must be <class 'threading.Event'>"
)
raise TypeError("parameter 'exitevent' must be <class 'threading.Event'>")
if type(timeout) != int or timeout < 0:
raise ValueError(
"parameter 'timeout' must be <class 'int'> and greater than 0"
)
raise ValueError("parameter 'timeout' must be <class 'int'> and greater than 0")
if edge != BOTH and not self._bitshift:
raise ValueError(
"parameter 'edge' can be used with bit Inputs only"
)
raise ValueError("parameter 'edge' can be used with bit Inputs only")
# Abbruchwert prüfen
if okvalue == self.value:
@@ -858,23 +821,27 @@ class IOBase(object):
exitevent = Event()
flt_timecount = 0 if bool_timecount else -1
while not self._parentdevice._modio._waitexit.is_set() \
and not exitevent.is_set() \
and flt_timecount < timeout:
while (
not self._parentdevice._modio._waitexit.is_set()
and not exitevent.is_set()
and flt_timecount < timeout
):
if self._parentdevice._modio._imgwriter.newdata.wait(2.5):
self._parentdevice._modio._imgwriter.newdata.clear()
if val_start != self.value:
if edge == BOTH \
or edge == RISING and not val_start \
or edge == FALLING and val_start:
if (
edge == BOTH
or edge == RISING
and not val_start
or edge == FALLING
and val_start
):
return 0
else:
val_start = not val_start
if bool_timecount:
flt_timecount += \
self._parentdevice._modio._imgwriter._refresh
flt_timecount += self._parentdevice._modio._imgwriter._refresh
elif bool_timecount:
flt_timecount += 2.5
@@ -922,7 +889,7 @@ class IntIO(IOBase):
return int.from_bytes(
self._parentdevice._ba_devdata[self._slc_address],
byteorder=self._byteorder,
signed=self._signed
signed=self._signed,
)
def __call__(self, value=None):
@@ -931,16 +898,18 @@ class IntIO(IOBase):
return int.from_bytes(
self._parentdevice._ba_devdata[self._slc_address],
byteorder=self._byteorder,
signed=self._signed
signed=self._signed,
)
else:
# Inline from set_intvalue()
if type(value) == int:
self.set_value(value.to_bytes(
self._length,
byteorder=self._byteorder,
signed=self._signed
))
self.set_value(
value.to_bytes(
self._length,
byteorder=self._byteorder,
signed=self._signed,
)
)
else:
raise TypeError(
"'{0}' need a <class 'int'> value, but {1} was given"
@@ -983,9 +952,7 @@ class IntIO(IOBase):
:return: <class 'int'> Defaultvalue
"""
return int.from_bytes(
self._defaultvalue, byteorder=self._byteorder, signed=self._signed
)
return int.from_bytes(self._defaultvalue, byteorder=self._byteorder, signed=self._signed)
def get_intvalue(self) -> int:
"""
@@ -996,7 +963,7 @@ class IntIO(IOBase):
return int.from_bytes(
self._parentdevice._ba_devdata[self._slc_address],
byteorder=self._byteorder,
signed=self._signed
signed=self._signed,
)
def set_intvalue(self, value: int) -> None:
@@ -1006,11 +973,13 @@ class IntIO(IOBase):
:param value: <class 'int'> Wert
"""
if type(value) == int:
self.set_value(value.to_bytes(
self._length,
byteorder=self._byteorder,
signed=self._signed
))
self.set_value(
value.to_bytes(
self._length,
byteorder=self._byteorder,
signed=self._signed,
)
)
else:
raise TypeError(
"'{0}' need a <class 'int'> value, but {1} was given"
@@ -1028,9 +997,7 @@ class IntIOCounter(IntIO):
__slots__ = ("__ioctl_arg",)
def __init__(
self, counter_id,
parentdevice, valuelist, iotype, byteorder, signed):
def __init__(self, counter_id, parentdevice, valuelist, iotype, byteorder, signed):
"""
Instantiierung der IntIOCounter-Klasse.
@@ -1044,10 +1011,11 @@ class IntIOCounter(IntIO):
# Deviceposition + leer + Counter_ID
# ID-Bits: 7|6|5|4|3|2|1|0|15|14|13|12|11|10|9|8
self.__ioctl_arg = \
parentdevice._position.to_bytes(1, "little") \
+ b'\x00' \
self.__ioctl_arg = (
parentdevice._position.to_bytes(1, "little")
+ b"\x00"
+ (1 << counter_id).to_bytes(2, "little")
)
"""
IOCTL fuellt dieses struct, welches durch padding im Speicher nach
@@ -1067,23 +1035,16 @@ class IntIOCounter(IntIO):
def reset(self) -> None:
"""Setzt den Counter des Inputs zurueck."""
if self._parentdevice._modio._monitoring:
raise RuntimeError(
"can not reset counter, while system is in monitoring mode"
)
raise RuntimeError("can not reset counter, while system is in monitoring mode")
if self._parentdevice._modio._simulator:
raise RuntimeError(
"can not reset counter, while system is in simulator mode"
)
raise RuntimeError("can not reset counter, while system is in simulator mode")
if self._parentdevice._modio._run_on_pi:
# IOCTL auf dem RevPi
with self._parentdevice._modio._myfh_lck:
try:
# Counter reset durchführen (Funktion K+20)
ioctl(
self._parentdevice._modio._myfh,
19220, self.__ioctl_arg
)
ioctl(self._parentdevice._modio._myfh, 19220, self.__ioctl_arg)
except Exception as e:
self._parentdevice._modio._gotioerror("iorst", e)
@@ -1091,9 +1052,7 @@ class IntIOCounter(IntIO):
# IOCTL über Netzwerk
with self._parentdevice._modio._myfh_lck:
try:
self._parentdevice._modio._myfh.ioctl(
19220, self.__ioctl_arg
)
self._parentdevice._modio._myfh.ioctl(19220, self.__ioctl_arg)
except Exception as e:
self._parentdevice._modio._gotioerror("net_iorst", e)
@@ -1101,9 +1060,7 @@ class IntIOCounter(IntIO):
# IOCTL in Datei simulieren
try:
# Set value durchführen (Funktion K+20)
self._parentdevice._modio._simulate_ioctl(
19220, self.__ioctl_arg
)
self._parentdevice._modio._simulate_ioctl(19220, self.__ioctl_arg)
except Exception as e:
self._parentdevice._modio._gotioerror("file_iorst", e)
@@ -1153,12 +1110,7 @@ class IntIOReplaceable(IntIO):
`<https://docs.python.org/3/library/struct.html#format-characters>`_
"""
# StructIO erzeugen
io_new = StructIO(
self,
name,
frm,
**kwargs
)
io_new = StructIO(self, name, frm, **kwargs)
# StructIO in IO-Liste einfügen
self._parentdevice._modio.io._private_register_new_io_object(io_new)
@@ -1170,7 +1122,7 @@ class IntIOReplaceable(IntIO):
reg_event,
kwargs.get("delay", 0),
kwargs.get("edge", BOTH),
kwargs.get("as_thread", False)
kwargs.get("as_thread", False),
)
@@ -1182,8 +1134,14 @@ class StructIO(IOBase):
bereit. Der struct-Formatwert wird bei der Instantiierung festgelegt.
"""
__slots__ = "__frm", "_parentio_address", "_parentio_defaultvalue", \
"_parentio_length", "_parentio_name", "_wordorder"
__slots__ = (
"__frm",
"_parentio_address",
"_parentio_defaultvalue",
"_parentio_length",
"_parentio_name",
"_wordorder",
)
def __init__(self, parentio, name: str, frm: str, **kwargs):
"""
@@ -1215,15 +1173,12 @@ class StructIO(IOBase):
if frm == "?":
if self._wordorder:
raise ValueError(
"you can not use wordorder for bit based ios"
)
raise ValueError("you can not use wordorder for bit based ios")
bitaddress = kwargs.get("bit", 0)
max_bits = parentio._length * 8
if not (0 <= bitaddress < max_bits):
raise ValueError(
"bitaddress must be a value between 0 and {0}"
"".format(max_bits - 1)
"bitaddress must be a value between 0 and {0}".format(max_bits - 1)
)
bitlength = 1
@@ -1246,8 +1201,7 @@ class StructIO(IOBase):
raise ValueError("wordorder must be 'little' or 'big'")
if byte_length % 2 != 0:
raise ValueError(
"the byte length of new io must must be even to "
"use wordorder"
"the byte length of new io must must be even to use wordorder"
)
# [name,default,anzbits,adressbyte,export,adressid,bmk,bitaddress]
@@ -1260,7 +1214,7 @@ class StructIO(IOBase):
False,
str(parentio._slc_address.start).rjust(4, "0"),
kwargs.get("bmk", ""),
bitaddress
bitaddress,
]
else:
raise ValueError(
@@ -1270,11 +1224,7 @@ class StructIO(IOBase):
# Basisklasse instantiieren
super().__init__(
parentio._parentdevice,
valuelist,
parentio._iotype,
byteorder,
frm == frm.lower()
parentio._parentdevice, valuelist, parentio._iotype, byteorder, frm == frm.lower()
)
self.__frm = bofrm + frm
if "export" in kwargs:
@@ -1286,13 +1236,11 @@ class StructIO(IOBase):
self._export = parentio._export
# Platz für neuen IO prüfen
if not (self._slc_address.start >=
parentio._parentdevice._dict_slc[parentio._iotype].start and
self._slc_address.stop <=
parentio._parentdevice._dict_slc[parentio._iotype].stop):
raise BufferError(
"registered value does not fit process image scope"
)
if not (
self._slc_address.start >= parentio._parentdevice._dict_slc[parentio._iotype].start
and self._slc_address.stop <= parentio._parentdevice._dict_slc[parentio._iotype].stop
):
raise BufferError("registered value does not fit process image scope")
def __call__(self, value=None):
if value is None:
@@ -1310,9 +1258,7 @@ class StructIO(IOBase):
if self._bitshift:
self.set_value(value)
elif self._wordorder == "little" and self._length > 2:
self.set_value(
self._swap_word_order(struct.pack(self.__frm, value))
)
self.set_value(self._swap_word_order(struct.pack(self.__frm, value)))
else:
self.set_value(struct.pack(self.__frm, value))
@@ -1343,8 +1289,10 @@ class StructIO(IOBase):
array_length = len(bytes_to_swap)
swap_array = bytearray(bytes_to_swap)
for i in range(0, array_length // 2, 2):
swap_array[-i - 2:array_length - i], swap_array[i:i + 2] = \
swap_array[i:i + 2], swap_array[-i - 2:array_length - i]
swap_array[-i - 2 : array_length - i], swap_array[i : i + 2] = (
swap_array[i : i + 2],
swap_array[-i - 2 : array_length - i],
)
return bytes(swap_array)
def get_structdefaultvalue(self):
@@ -1394,9 +1342,7 @@ class StructIO(IOBase):
if self._bitshift:
self.set_value(value)
elif self._wordorder == "little" and self._length > 2:
self.set_value(
self._swap_word_order(struct.pack(self.__frm, value))
)
self.set_value(self._swap_word_order(struct.pack(self.__frm, value)))
else:
self.set_value(struct.pack(self.__frm, value))
@@ -1422,7 +1368,7 @@ class MemIO(IOBase):
if self._bitlength > 64:
# STRING
try:
val = val.strip(b'\x00').decode()
val = val.strip(b"\x00").decode()
except Exception:
pass
return val

View File

@@ -31,10 +31,10 @@ class DevSelect:
__slots__ = "type", "other_device_key", "values"
def __init__(
self,
device_type=DeviceType.IGNORED,
search_key: str = None,
search_values=(),
self,
device_type=DeviceType.IGNORED,
search_key: str = None,
search_values=(),
):
"""
Create a customized search filter for RevPiModIOSelected search.
@@ -65,20 +65,55 @@ class RevPiModIO(object):
Device Positionen oder Device Namen.
"""
__slots__ = "__cleanupfunc", \
"_autorefresh", "_buffedwrite", "_configrsc", "_debug", "_devselect", \
"_exit", "_exit_level", "_imgwriter", "_ioerror", \
"_length", "_looprunning", "_lst_devselect", "_lst_refresh", \
"_maxioerrors", "_monitoring", "_myfh", "_myfh_lck", \
"_procimg", "_replace_io_file", "_run_on_pi", \
"_set_device_based_cycle_time", "_simulator", "_init_shared_procimg", \
"_syncoutputs", "_th_mainloop", "_waitexit", \
"app", "core", "device", "exitsignal", "io", "summary"
__slots__ = (
"__cleanupfunc",
"_autorefresh",
"_buffedwrite",
"_configrsc",
"_debug",
"_devselect",
"_exit",
"_exit_level",
"_imgwriter",
"_ioerror",
"_length",
"_looprunning",
"_lst_devselect",
"_lst_refresh",
"_maxioerrors",
"_monitoring",
"_myfh",
"_myfh_lck",
"_procimg",
"_replace_io_file",
"_run_on_pi",
"_set_device_based_cycle_time",
"_simulator",
"_init_shared_procimg",
"_syncoutputs",
"_th_mainloop",
"_waitexit",
"app",
"core",
"device",
"exitsignal",
"io",
"summary",
)
def __init__(
self, autorefresh=False, monitoring=False, syncoutputs=True,
procimg=None, configrsc=None, simulator=False, debug=True,
replace_io_file=None, shared_procimg=False, direct_output=False):
self,
autorefresh=False,
monitoring=False,
syncoutputs=True,
procimg=None,
configrsc=None,
simulator=False,
debug=True,
replace_io_file=None,
shared_procimg=False,
direct_output=False,
):
"""
Instantiiert die Grundfunktionen.
@@ -96,20 +131,27 @@ class RevPiModIO(object):
"""
# Parameterprüfung
acheck(
bool, autorefresh=autorefresh, monitoring=monitoring,
syncoutputs=syncoutputs, simulator=simulator, debug=debug,
shared_procimg=shared_procimg, direct_output=direct_output
bool,
autorefresh=autorefresh,
monitoring=monitoring,
syncoutputs=syncoutputs,
simulator=simulator,
debug=debug,
shared_procimg=shared_procimg,
direct_output=direct_output,
)
acheck(
str, procimg_noneok=procimg, configrsc_noneok=configrsc,
replace_io_file_noneok=replace_io_file
str,
procimg_noneok=procimg,
configrsc_noneok=configrsc,
replace_io_file_noneok=replace_io_file,
)
# TODO: Remove in next release
if direct_output:
warnings.warn(DeprecationWarning(
"direct_output is deprecated - use shared_procimg instead!"
))
warnings.warn(
DeprecationWarning("direct_output is deprecated - use shared_procimg instead!")
)
self._autorefresh = autorefresh
self._configrsc = configrsc
@@ -233,13 +275,11 @@ class RevPiModIO(object):
# Apply device filter
if self._devselect.values:
# Check for supported types in values
for dev in self._devselect.values:
if type(dev) not in (int, str):
raise ValueError(
"need device position as <class 'int'> or "
"device name as <class 'str'>"
"need device position as <class 'int'> or device name as <class 'str'>"
)
lst_devices = []
@@ -253,8 +293,10 @@ class RevPiModIO(object):
continue
else:
# Auto search depending of value item type
if not (dev["name"] in self._devselect.values
or int(dev["position"]) in self._devselect.values):
if not (
dev["name"] in self._devselect.values
or int(dev["position"]) in self._devselect.values
):
continue
lst_devices.append(dev)
@@ -269,7 +311,6 @@ class RevPiModIO(object):
# Devices initialisieren
err_names_check = {}
for device in sorted(lst_devices, key=lambda x: x["offset"]):
# VDev alter piCtory Versionen auf KUNBUS-Standard ändern
if device["position"] == "adap.":
device["position"] = 64
@@ -281,64 +322,42 @@ class RevPiModIO(object):
pt = int(device["productType"])
if pt == ProductType.REVPI_CORE:
# RevPi Core
dev_new = devicemodule.Core(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Core(self, device, simulator=self._simulator)
self.core = dev_new
elif pt == ProductType.REVPI_CONNECT:
# RevPi Connect
dev_new = devicemodule.Connect(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Connect(self, device, simulator=self._simulator)
self.core = dev_new
elif pt == ProductType.REVPI_CONNECT_4:
# RevPi Connect 4
dev_new = devicemodule.Connect4(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Connect4(self, device, simulator=self._simulator)
self.core = dev_new
elif pt == ProductType.REVPI_COMPACT:
# RevPi Compact
dev_new = devicemodule.Compact(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Compact(self, device, simulator=self._simulator)
self.core = dev_new
elif pt == ProductType.REVPI_FLAT:
# RevPi Flat
dev_new = devicemodule.Flat(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Flat(self, device, simulator=self._simulator)
self.core = dev_new
else:
# Base immer als Fallback verwenden
dev_new = devicemodule.Base(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Base(self, device, simulator=self._simulator)
elif device["type"] == DeviceType.LEFT_RIGHT:
# IOs
pt = int(device["productType"])
if pt == ProductType.DIO \
or pt == ProductType.DI \
or pt == ProductType.DO:
if pt == ProductType.DIO or pt == ProductType.DI or pt == ProductType.DO:
# DIO / DI / DO
dev_new = devicemodule.DioModule(
self, device, simulator=self._simulator
)
dev_new = devicemodule.DioModule(self, device, simulator=self._simulator)
else:
# Alle anderen IO-Devices
dev_new = devicemodule.Device(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Device(self, device, simulator=self._simulator)
elif device["type"] == DeviceType.VIRTUAL:
# Virtuals
dev_new = devicemodule.Virtual(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Virtual(self, device, simulator=self._simulator)
elif device["type"] == DeviceType.EDGE:
# Gateways
dev_new = devicemodule.Gateway(
self, device, simulator=self._simulator
)
dev_new = devicemodule.Gateway(self, device, simulator=self._simulator)
elif device["type"] == DeviceType.RIGHT:
# Connectdevice
dev_new = None
@@ -347,7 +366,7 @@ class RevPiModIO(object):
warnings.warn(
"device type '{0}' on position {1} unknown"
"".format(device["type"], device["position"]),
Warning
Warning,
)
dev_new = None
@@ -378,7 +397,7 @@ class RevPiModIO(object):
"equal device name '{0}' in pictory configuration. you can "
"access this devices by position number .device[{1}] only!"
"".format(check_dev, "|".join(err_names_check[check_dev])),
Warning
Warning,
)
# ImgWriter erstellen
@@ -393,17 +412,12 @@ class RevPiModIO(object):
self.syncoutputs()
# Für RS485 errors am core defaults laden sollte procimg NULL sein
if isinstance(self.core, devicemodule.Core) and \
not (self._monitoring or self._simulator):
if isinstance(self.core, devicemodule.Core) and not (self._monitoring or self._simulator):
if self.core._slc_errorlimit1 is not None:
io = self.io[
self.core.offset + self.core._slc_errorlimit1.start
][0]
io = self.io[self.core.offset + self.core._slc_errorlimit1.start][0]
io.set_value(io._defaultvalue)
if self.core._slc_errorlimit2 is not None:
io = self.io[
self.core.offset + self.core._slc_errorlimit2.start
][0]
io = self.io[self.core.offset + self.core._slc_errorlimit2.start][0]
io.set_value(io._defaultvalue)
# RS485 errors schreiben
@@ -471,8 +485,7 @@ class RevPiModIO(object):
if "defaultvalue" in creplaceio[io]:
if dict_replace["frm"] == "?":
try:
dict_replace["defaultvalue"] = \
creplaceio[io].getboolean("defaultvalue")
dict_replace["defaultvalue"] = creplaceio[io].getboolean("defaultvalue")
except Exception:
raise ValueError(
"replace_io_file: could not convert '{0}' "
@@ -494,8 +507,7 @@ class RevPiModIO(object):
)
else:
try:
dict_replace["defaultvalue"] = \
creplaceio[io].getint("defaultvalue")
dict_replace["defaultvalue"] = creplaceio[io].getint("defaultvalue")
except Exception:
raise ValueError(
"replace_io_file: could not convert '{0}' "
@@ -634,23 +646,19 @@ class RevPiModIO(object):
self._ioerror += 1
if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors:
raise RuntimeError(
"reach max io error count {0} on process image"
"".format(self._maxioerrors)
"reach max io error count {0} on process image".format(self._maxioerrors)
)
if not show_warn or self._debug == -1:
return
if self._debug == 0:
warnings.warn(
"got io error on process image",
RuntimeWarning
)
warnings.warn("got io error on process image", RuntimeWarning)
else:
warnings.warn(
"got io error during '{0}' and count {1} errors now | {2}"
"".format(action, self._ioerror, str(e)),
RuntimeWarning
RuntimeWarning,
)
def _set_cycletime(self, milliseconds: int) -> None:
@@ -660,10 +668,7 @@ class RevPiModIO(object):
:param milliseconds: <class 'int'> in Millisekunden
"""
if self._looprunning:
raise RuntimeError(
"can not change cycletime when cycleloop or mainloop is "
"running"
)
raise RuntimeError("can not change cycletime when cycleloop or mainloop is running")
else:
self._imgwriter.refresh = milliseconds
@@ -701,7 +706,7 @@ class RevPiModIO(object):
else:
raise ValueError("value must be 0 or a positive integer")
def _simulate_ioctl(self, request: int, arg=b'') -> None:
def _simulate_ioctl(self, request: int, arg=b"") -> None:
"""
Simuliert IOCTL Funktionen auf procimg Datei.
@@ -717,9 +722,7 @@ class RevPiModIO(object):
# Simulatonsmodus schreibt direkt in Datei
with self._myfh_lck:
self._myfh.seek(byte_address)
int_byte = int.from_bytes(
self._myfh.read(1), byteorder="little"
)
int_byte = int.from_bytes(self._myfh.read(1), byteorder="little")
int_bit = 1 << bit_address
if not bool(int_byte & int_bit) == new_value:
@@ -741,8 +744,9 @@ class RevPiModIO(object):
for i in range(16):
if bool(bit_field & 1 << i):
io_byte = self.device[dev_position].offset \
+ int(self.device[dev_position]._lst_counter[i])
io_byte = self.device[dev_position].offset + int(
self.device[dev_position]._lst_counter[i]
)
break
if io_byte == -1:
@@ -750,7 +754,7 @@ class RevPiModIO(object):
with self._myfh_lck:
self._myfh.seek(io_byte)
self._myfh.write(b'\x00\x00\x00\x00')
self._myfh.write(b"\x00\x00\x00\x00")
if self._buffedwrite:
self._myfh.flush()
@@ -796,9 +800,7 @@ class RevPiModIO(object):
"""
# Prüfen ob ein Loop bereits läuft
if self._looprunning:
raise RuntimeError(
"can not start multiple loops mainloop/cycleloop"
)
raise RuntimeError("can not start multiple loops mainloop/cycleloop")
# Prüfen ob Devices in autorefresh sind
if len(self._lst_refresh) == 0:
@@ -809,16 +811,14 @@ class RevPiModIO(object):
# Prüfen ob Funktion callable ist
if not callable(func):
raise RuntimeError(
"registered function '{0}' ist not callable".format(func)
)
raise RuntimeError("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}
kwargs={"cycletime": cycletime, "blocking": True},
)
self._th_mainloop.start()
return
@@ -851,9 +851,9 @@ class RevPiModIO(object):
break
# Just warn, user has to use maxioerrors to kill program
warnings.warn(RuntimeWarning(
"no new io data in cycle loop for 2500 milliseconds"
))
warnings.warn(
RuntimeWarning("no new io data in cycle loop for 2500 milliseconds")
)
cycleinfo.last = self._exit.is_set()
continue
@@ -942,7 +942,6 @@ class RevPiModIO(object):
cp = ConfigParser()
for io in self.io:
if isinstance(io, StructIO):
# Required values
cp.add_section(io.name)
cp[io.name]["replace"] = io._parentio_name
@@ -958,8 +957,7 @@ class RevPiModIO(object):
if type(io.defaultvalue) is bytes:
if any(io.defaultvalue):
# Convert each byte to an integer
cp[io.name]["defaultvalue"] = \
" ".join(map(str, io.defaultvalue))
cp[io.name]["defaultvalue"] = " ".join(map(str, io.defaultvalue))
elif io.defaultvalue != 0:
cp[io.name]["defaultvalue"] = str(io.defaultvalue)
if io.bmk != "":
@@ -971,10 +969,7 @@ class RevPiModIO(object):
with open(filename, "w") as fh:
cp.write(fh)
except Exception as e:
raise RuntimeError(
"could not write export file '{0}' | {1}"
"".format(filename, e)
)
raise RuntimeError("could not write export file '{0}' | {1}".format(filename, e))
def get_jconfigrsc(self) -> dict:
"""
@@ -986,8 +981,8 @@ class RevPiModIO(object):
if self._configrsc is not None:
if not access(self._configrsc, F_OK | R_OK):
raise RuntimeError(
"can not access pictory configuration at {0}".format(
self._configrsc))
"can not access pictory configuration at {0}".format(self._configrsc)
)
else:
# piCtory Konfiguration an bekannten Stellen prüfen
lst_rsc = ["/etc/revpi/config.rsc", "/opt/KUNBUS/config.rsc"]
@@ -1035,10 +1030,7 @@ class RevPiModIO(object):
"""
# Prüfen ob Funktion callable ist
if not (cleanupfunc is None or callable(cleanupfunc)):
raise RuntimeError(
"registered function '{0}' ist not callable"
"".format(cleanupfunc)
)
raise RuntimeError("registered function '{0}' ist not callable".format(cleanupfunc))
self.__cleanupfunc = cleanupfunc
signal(SIGINT, self.__evt_exit)
signal(SIGTERM, self.__evt_exit)
@@ -1063,9 +1055,7 @@ class RevPiModIO(object):
"""
# Prüfen ob ein Loop bereits läuft
if self._looprunning:
raise RuntimeError(
"can not start multiple loops mainloop/cycleloop"
)
raise RuntimeError("can not start multiple loops mainloop/cycleloop")
# Prüfen ob Devices in autorefresh sind
if len(self._lst_refresh) == 0:
@@ -1076,9 +1066,7 @@ class RevPiModIO(object):
# Thread erstellen, wenn nicht blockieren soll
if not blocking:
self._th_mainloop = Thread(
target=self.mainloop, kwargs={"blocking": True}
)
self._th_mainloop = Thread(target=self.mainloop, kwargs={"blocking": True})
self._th_mainloop.start()
return
@@ -1100,17 +1088,17 @@ class RevPiModIO(object):
if not regfunc.prefire:
continue
if regfunc.edge == BOTH \
or regfunc.edge == RISING and io.value \
or regfunc.edge == FALLING and not io.value:
if (
regfunc.edge == BOTH
or regfunc.edge == RISING
and io.value
or regfunc.edge == FALLING
and not io.value
):
if regfunc.as_thread:
self._imgwriter._eventqth.put(
(regfunc, io._name, io.value), False
)
self._imgwriter._eventqth.put((regfunc, io._name, io.value), False)
else:
self._imgwriter._eventq.put(
(regfunc, io._name, io.value), False
)
self._imgwriter._eventq.put((regfunc, io._name, io.value), False)
# ImgWriter mit Eventüberwachung aktivieren
self._imgwriter._collect_events(True)
@@ -1118,7 +1106,6 @@ class RevPiModIO(object):
runtime = -1 if self._debug == -1 else 0
while not self._exit.is_set():
# Laufzeit der Eventqueue auf 0 setzen
if self._imgwriter._eventq.qsize() == 0:
runtime = -1 if self._debug == -1 else 0
@@ -1135,13 +1122,12 @@ class RevPiModIO(object):
self._imgwriter._eventq.task_done()
# Laufzeitprüfung
if runtime != -1 and \
default_timer() - runtime > self._imgwriter._refresh:
if runtime != -1 and default_timer() - runtime > self._imgwriter._refresh:
runtime = -1
warnings.warn(
"can not execute all event functions in one cycle - "
"optimize your event functions or rise .cycletime",
RuntimeWarning
RuntimeWarning,
)
except Empty:
if not self._exit.is_set() and not self._imgwriter.is_alive():
@@ -1177,8 +1163,11 @@ class RevPiModIO(object):
if device is None:
mylist = self.device
else:
dev = device if isinstance(device, devicemodule.Device) \
dev = (
device
if isinstance(device, devicemodule.Device)
else self.device.__getitem__(device)
)
if dev._selfupdate:
raise RuntimeError(
@@ -1200,7 +1189,6 @@ class RevPiModIO(object):
for dev in mylist:
if not dev._selfupdate:
# FileHandler sperren
dev._filelock.acquire()
@@ -1226,16 +1214,16 @@ class RevPiModIO(object):
:param device: nur auf einzelnes Device anwenden
"""
if self._monitoring:
raise RuntimeError(
"can not set default values, while system is in monitoring "
"mode"
)
raise RuntimeError("can not set default values, while system is in monitoring mode")
if device is None:
mylist = self.device
else:
dev = device if isinstance(device, devicemodule.Device) \
dev = (
device
if isinstance(device, devicemodule.Device)
else self.device.__getitem__(device)
)
mylist = [dev]
for dev in mylist:
@@ -1254,8 +1242,11 @@ class RevPiModIO(object):
if device is None:
mylist = self.device
else:
dev = device if isinstance(device, devicemodule.Device) \
dev = (
device
if isinstance(device, devicemodule.Device)
else self.device.__getitem__(device)
)
if dev._selfupdate:
raise RuntimeError(
@@ -1292,16 +1283,16 @@ class RevPiModIO(object):
:return: True, wenn Arbeiten an allen Devices erfolgreich waren
"""
if self._monitoring:
raise RuntimeError(
"can not write process image, while system is in monitoring "
"mode"
)
raise RuntimeError("can not write process image, while system is in monitoring mode")
if device is None:
mylist = self.device
else:
dev = device if isinstance(device, devicemodule.Device) \
dev = (
device
if isinstance(device, devicemodule.Device)
else self.device.__getitem__(device)
)
if dev._selfupdate:
raise RuntimeError(
@@ -1321,9 +1312,7 @@ class RevPiModIO(object):
if dev._shared_procimg:
for io in dev._shared_write:
if not io._write_to_procimg():
global_ex = IOError(
"error on shared procimg while write"
)
global_ex = IOError("error on shared procimg while write")
dev._shared_write.clear()
else:
# Outpus auf Bus schreiben
@@ -1375,10 +1364,19 @@ class RevPiModIOSelected(RevPiModIO):
__slots__ = ()
def __init__(
self, deviceselection, autorefresh=False, monitoring=False,
syncoutputs=True, procimg=None, configrsc=None,
simulator=False, debug=True, replace_io_file=None,
shared_procimg=False, direct_output=False):
self,
deviceselection,
autorefresh=False,
monitoring=False,
syncoutputs=True,
procimg=None,
configrsc=None,
simulator=False,
debug=True,
replace_io_file=None,
shared_procimg=False,
direct_output=False,
):
"""
Instantiiert nur fuer angegebene Devices die Grundfunktionen.
@@ -1390,8 +1388,16 @@ class RevPiModIOSelected(RevPiModIO):
:ref: :func:`RevPiModIO.__init__(...)`
"""
super().__init__(
autorefresh, monitoring, syncoutputs, procimg, configrsc,
simulator, debug, replace_io_file, shared_procimg, direct_output
autorefresh,
monitoring,
syncoutputs,
procimg,
configrsc,
simulator,
debug,
replace_io_file,
shared_procimg,
direct_output,
)
if type(deviceselection) is not DevSelect:
@@ -1410,24 +1416,19 @@ class RevPiModIOSelected(RevPiModIO):
if len(self.device) == 0:
if self._devselect.type:
raise DeviceNotFoundError(
"could not find ANY given {0} devices in config"
"".format(self._devselect.type)
"could not find ANY given {0} devices in config".format(self._devselect.type)
)
else:
raise DeviceNotFoundError(
"could not find ANY given devices in config"
)
elif not self._devselect.other_device_key \
and len(self.device) != len(self._devselect.values):
raise DeviceNotFoundError("could not find ANY given devices in config")
elif not self._devselect.other_device_key and len(self.device) != len(
self._devselect.values
):
if self._devselect.type:
raise DeviceNotFoundError(
"could not find ALL given {0} devices in config"
"".format(self._devselect.type)
"could not find ALL given {0} devices in config".format(self._devselect.type)
)
else:
raise DeviceNotFoundError(
"could not find ALL given devices in config"
)
raise DeviceNotFoundError("could not find ALL given devices in config")
class RevPiModIODriver(RevPiModIOSelected):
@@ -1443,9 +1444,17 @@ class RevPiModIODriver(RevPiModIOSelected):
__slots__ = ()
def __init__(
self, virtdev, autorefresh=False,
syncoutputs=True, procimg=None, configrsc=None, debug=True,
replace_io_file=None, shared_procimg=False, direct_output=False):
self,
virtdev,
autorefresh=False,
syncoutputs=True,
procimg=None,
configrsc=None,
debug=True,
replace_io_file=None,
shared_procimg=False,
direct_output=False,
):
"""
Instantiiert die Grundfunktionen.
@@ -1460,14 +1469,21 @@ class RevPiModIODriver(RevPiModIOSelected):
virtdev = (virtdev,)
dev_select = DevSelect(DeviceType.VIRTUAL, "", virtdev)
super().__init__(
dev_select, autorefresh, False, syncoutputs, procimg, configrsc,
True, debug, replace_io_file, shared_procimg, direct_output
dev_select,
autorefresh,
False,
syncoutputs,
procimg,
configrsc,
True,
debug,
replace_io_file,
shared_procimg,
direct_output,
)
def run_plc(
func, cycletime=50, replace_io_file=None, debug=True,
procimg=None, configrsc=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.

View File

@@ -18,22 +18,22 @@ from .modio import DevSelect, RevPiModIO as _RevPiModIO
from .pictory import DeviceType
# Synchronisierungsbefehl
_syssync = b'\x01\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
_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'
_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'
_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'
_syspictoryh = b'\x01PH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
_syspictory = b"\x01PI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17"
_syspictoryh = b"\x01PH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17"
# ReplaceIO Konfiguration laden
_sysreplaceio = b'\x01RP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
_sysreplaceioh = b'\x01RH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
_sysreplaceio = b"\x01RP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17"
_sysreplaceioh = b"\x01RH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17"
# Hashvalues
HASH_FAIL = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
HASH_FAIL = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
# Header start/stop
HEADER_START = b'\x01'
HEADER_STOP = b'\x17'
HEADER_START = b"\x01"
HEADER_STOP = b"\x17"
class AclException(Exception):
@@ -57,12 +57,28 @@ class NetFH(Thread):
so gesteuert werden.
"""
__slots__ = "__buff_size", "__buff_block", "__buff_recv", \
"__by_buff", "__check_replace_ios", "__config_changed", \
"__int_buff", "__dictdirty", "__flusherr", "__replace_ios_h", \
"__pictory_h", "__position", "__sockerr", "__sockend", \
"__socklock", "__timeout", "__waitsync", "_address", \
"_serversock", "daemon"
__slots__ = (
"__buff_size",
"__buff_block",
"__buff_recv",
"__by_buff",
"__check_replace_ios",
"__config_changed",
"__int_buff",
"__dictdirty",
"__flusherr",
"__replace_ios_h",
"__pictory_h",
"__position",
"__sockerr",
"__sockend",
"__socklock",
"__timeout",
"__waitsync",
"_address",
"_serversock",
"daemon",
)
def __init__(self, address: tuple, check_replace_ios: bool, timeout=500):
"""
@@ -83,8 +99,8 @@ class NetFH(Thread):
self.__config_changed = False
self.__int_buff = 0
self.__dictdirty = {}
self.__replace_ios_h = b''
self.__pictory_h = b''
self.__replace_ios_h = b""
self.__pictory_h = b""
self.__sockerr = Event()
self.__sockend = Event()
self.__socklock = Lock()
@@ -95,9 +111,7 @@ class NetFH(Thread):
# Parameterprüfung
if not isinstance(address, tuple):
raise TypeError(
"parameter address must be <class 'tuple'> ('IP', PORT)"
)
raise TypeError("parameter address must be <class 'tuple'> ('IP', PORT)")
if not isinstance(timeout, int):
raise TypeError("parameter timeout must be <class 'int'>")
@@ -125,7 +139,7 @@ class NetFH(Thread):
:param bytecode: Antwort, die geprueft werden solll
"""
if bytecode == b'\x18':
if bytecode == b"\x18":
# Alles beenden, wenn nicht erlaubt
self.__sockend.set()
self.__sockerr.set()
@@ -174,7 +188,7 @@ class NetFH(Thread):
buff_recv = bytearray(recv_len)
while recv_len > 0:
block = so.recv(recv_len)
if block == b'':
if block == b"":
raise OSError("lost connection on hash receive")
buff_recv += block
recv_len -= len(block)
@@ -183,18 +197,19 @@ class NetFH(Thread):
if self.__pictory_h and buff_recv[:16] != self.__pictory_h:
self.__config_changed = True
self.close()
raise ConfigChanged(
"configuration on revolution pi was changed")
raise ConfigChanged("configuration on revolution pi was changed")
else:
self.__pictory_h = buff_recv[:16]
# Änderung an replace_ios prüfen
if self.__check_replace_ios and self.__replace_ios_h \
and buff_recv[16:] != self.__replace_ios_h:
if (
self.__check_replace_ios
and self.__replace_ios_h
and buff_recv[16:] != self.__replace_ios_h
):
self.__config_changed = True
self.close()
raise ConfigChanged(
"configuration on revolution pi was changed")
raise ConfigChanged("configuration on revolution pi was changed")
else:
self.__replace_ios_h = buff_recv[16:]
except ConfigChanged:
@@ -295,11 +310,18 @@ class NetFH(Thread):
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':
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)
@@ -343,12 +365,18 @@ class NetFH(Thread):
try:
# 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, 1)
buff = self._direct_sr(
pack(
"=c2sHH8xc",
HEADER_START,
b"FD",
self.__int_buff,
len(self.__by_buff),
HEADER_STOP,
)
+ self.__by_buff,
1,
)
except Exception:
raise
finally:
@@ -356,7 +384,7 @@ class NetFH(Thread):
self.__int_buff = 0
self.__by_buff.clear()
if buff != b'\x1e':
if buff != b"\x1e":
# ACL prüfen und ggf Fehler werfen
self.__check_acl(buff)
@@ -403,7 +431,7 @@ class NetFH(Thread):
"""
return int(self.__timeout * 1000)
def ioctl(self, request: int, arg=b'') -> None:
def ioctl(self, request: int, arg=b"") -> None:
"""
IOCTL Befehle ueber das Netzwerk senden.
@@ -419,11 +447,10 @@ class NetFH(Thread):
raise TypeError("arg must be <class 'bytes'>")
# b CM xx ii iiii0000 b = 16
buff = self._direct_sr(pack(
"=c2s2xHI4xc",
HEADER_START, b'IC', len(arg), request, HEADER_STOP
) + arg, 1)
if buff != b'\x1e':
buff = self._direct_sr(
pack("=c2s2xHI4xc", HEADER_START, b"IC", len(arg), request, HEADER_STOP) + arg, 1
)
if buff != b"\x1e":
# ACL prüfen und ggf Fehler werfen
self.__check_acl(buff)
@@ -443,10 +470,9 @@ class NetFH(Thread):
raise ValueError("read of closed file")
# b CM ii ii 00000000 b = 16
buff = self._direct_sr(pack(
"=c2sHH8xc",
HEADER_START, b'DA', self.__position, length, HEADER_STOP
), length)
buff = self._direct_sr(
pack("=c2sHH8xc", HEADER_START, b"DA", self.__position, length, HEADER_STOP), length
)
self.__position += length
return buff
@@ -466,10 +492,9 @@ class NetFH(Thread):
length = len(buffer)
# b CM ii ii 00000000 b = 16
buff = self._direct_sr(pack(
"=c2sHH8xc",
HEADER_START, b'DA', self.__position, length, HEADER_STOP
), length)
buff = self._direct_sr(
pack("=c2sHH8xc", HEADER_START, b"DA", self.__position, length, HEADER_STOP), length
)
buffer[:] = buff
return len(buffer)
@@ -484,13 +509,11 @@ class NetFH(Thread):
raise ValueError("read of closed file")
if self.__pictory_h == HASH_FAIL:
raise RuntimeError(
"could not read/parse piCtory configuration over network"
)
raise RuntimeError("could not read/parse piCtory configuration over network")
buff = self._direct_sr(_syspictory, 4)
recv_length, = unpack("=I", buff)
return self._direct_sr(b'', recv_length)
(recv_length,) = unpack("=I", buff)
return self._direct_sr(b"", recv_length)
def readreplaceio(self) -> bytes:
"""
@@ -502,27 +525,21 @@ class NetFH(Thread):
raise ValueError("read of closed file")
if self.__replace_ios_h == HASH_FAIL:
raise RuntimeError(
"replace_io_file: could not read/parse over network"
)
raise RuntimeError("replace_io_file: could not read/parse over network")
buff = self._direct_sr(_sysreplaceio, 4)
recv_length, = unpack("=I", buff)
return self._direct_sr(b'', recv_length)
(recv_length,) = unpack("=I", buff)
return self._direct_sr(b"", recv_length)
def run(self) -> None:
"""Handler fuer Synchronisierung."""
state_reconnect = False
while not self.__sockend.is_set():
# Bei Fehlermeldung neu verbinden
if self.__sockerr.is_set():
if not state_reconnect:
state_reconnect = True
warnings.warn(
"got a network error and try to reconnect",
RuntimeWarning
)
warnings.warn("got a network error and try to reconnect", RuntimeWarning)
self._connect()
if self.__sockerr.is_set():
# Verhindert beim Scheitern 100% CPU last
@@ -530,10 +547,7 @@ class NetFH(Thread):
continue
else:
state_reconnect = False
warnings.warn(
"successfully reconnected after network error",
RuntimeWarning
)
warnings.warn("successfully reconnected after network error", RuntimeWarning)
# Kein Fehler aufgetreten, sync durchführen wenn socket frei
if self.__socklock.acquire(blocking=False):
@@ -543,9 +557,7 @@ class NetFH(Thread):
self.__buff_recv.clear()
recv_lenght = 2
while recv_lenght > 0:
count = self._serversock.recv_into(
self.__buff_block, recv_lenght
)
count = self._serversock.recv_into(self.__buff_block, recv_lenght)
if count == 0:
raise IOError("lost network connection on sync")
self.__buff_recv += self.__buff_block[:count]
@@ -554,11 +566,8 @@ class NetFH(Thread):
except IOError:
self.__sockerr.set()
else:
if self.__buff_recv != b'\x06\x16':
warnings.warn(
"data error on network sync",
RuntimeWarning
)
if self.__buff_recv != b"\x06\x16":
warnings.warn("data error on network sync", RuntimeWarning)
self.__sockerr.set()
continue
finally:
@@ -596,12 +605,13 @@ class NetFH(Thread):
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)
buff = self._direct_sr(
pack("=c2sHH8xc", HEADER_START, b"EY", position, len(dirtybytes), HEADER_STOP)
+ dirtybytes,
1,
)
if buff != b'\x1e':
if buff != b"\x1e":
# ACL prüfen und ggf Fehler werfen
self.__check_acl(buff)
@@ -627,11 +637,8 @@ class NetFH(Thread):
try:
# b CM ii xx 00000000 b = 16
buff = self._direct_sr(pack(
"=c2sH10xc",
HEADER_START, b'CF', value, HEADER_STOP
), 1)
if buff != b'\x1e':
buff = self._direct_sr(pack("=c2sH10xc", HEADER_START, b"CF", value, HEADER_STOP), 1)
if buff != b"\x1e":
raise IOError("set timeout error on network")
except Exception:
self.__sockerr.set()
@@ -666,10 +673,11 @@ class NetFH(Thread):
self.__int_buff += 1
# 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") \
self.__by_buff += (
self.__position.to_bytes(length=2, byteorder="little")
+ len(bytebuff).to_bytes(length=2, byteorder="little")
+ bytebuff
)
# TODO: Bufferlänge und dann flushen?
@@ -697,9 +705,17 @@ class RevPiNetIO(_RevPiModIO):
__slots__ = "_address"
def __init__(
self, address, autorefresh=False, monitoring=False,
syncoutputs=True, simulator=False, debug=True,
replace_io_file=None, shared_procimg=False, direct_output=False):
self,
address,
autorefresh=False,
monitoring=False,
syncoutputs=True,
simulator=False,
debug=True,
replace_io_file=None,
shared_procimg=False,
direct_output=False,
):
"""
Instantiiert die Grundfunktionen.
@@ -714,28 +730,20 @@ class RevPiNetIO(_RevPiModIO):
could be insecure for automation
:param direct_output: Deprecated, use shared_procimg
"""
check_ip = compile(
r"^(25[0-5]|(2[0-4]|[01]?\d|)\d)"
r"(\.(25[0-5]|(2[0-4]|[01]?\d|)\d)){3}$"
)
check_ip = compile(r"^(25[0-5]|(2[0-4]|[01]?\d|)\d)(\.(25[0-5]|(2[0-4]|[01]?\d|)\d)){3}$")
# Adresse verarbeiten
if isinstance(address, str):
self._address = (address, 55234)
elif isinstance(address, tuple):
if len(address) == 2 \
and isinstance(address[0], str) \
and isinstance(address[1], int):
if len(address) == 2 and isinstance(address[0], str) and isinstance(address[1], int):
# Werte prüfen
if not 0 < address[1] <= 65535:
raise ValueError("port number out of range 1 - 65535")
self._address = address
else:
raise TypeError(
"address tuple must be (<class 'str'>, <class 'int'>)"
)
raise TypeError("address tuple must be (<class 'str'>, <class 'int'>)")
else:
raise TypeError(
"parameter address must be <class 'str'> or <class 'tuple'> "
@@ -749,8 +757,7 @@ class RevPiNetIO(_RevPiModIO):
self._address = (ipv4, self._address[1])
except Exception:
raise ValueError(
"can not resolve ip address for hostname '{0}'"
"".format(self._address[0])
"can not resolve ip address for hostname '{0}'".format(self._address[0])
)
# Vererben
@@ -801,10 +808,7 @@ class RevPiNetIO(_RevPiModIO):
try:
cp.read_string(byte_buff.decode("utf-8"))
except Exception as e:
raise RuntimeError(
"replace_io_file: could not read/parse network data | {0}"
"".format(e)
)
raise RuntimeError("replace_io_file: could not read/parse network data | {0}".format(e))
return cp
def disconnect(self) -> None:
@@ -862,16 +866,12 @@ class RevPiNetIO(_RevPiModIO):
: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"
)
raise RuntimeError("can not send default values, while system is in monitoring mode")
if device is None:
self._myfh.clear_dirtybytes()
else:
dev = device if isinstance(device, Device) \
else self.device.__getitem__(device)
dev = device if isinstance(device, Device) else self.device.__getitem__(device)
mylist = [dev]
for dev in mylist:
@@ -887,16 +887,12 @@ class RevPiNetIO(_RevPiModIO):
: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"
)
raise RuntimeError("can not send default values, while system is in monitoring mode")
if device is None:
mylist = self.device
else:
dev = device if isinstance(device, Device) \
else self.device.__getitem__(device)
dev = device if isinstance(device, Device) else self.device.__getitem__(device)
mylist = [dev]
for dev in mylist:
@@ -921,13 +917,10 @@ class RevPiNetIO(_RevPiModIO):
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 += int_byte.to_bytes(length=1, byteorder="little")
# Dirtybytes an PLC Server senden
self._myfh.set_dirtybytes(
dev._offset + dev._slc_out.start, dirtybytes
)
self._myfh.set_dirtybytes(dev._offset + dev._slc_out.start, dirtybytes)
config_changed = property(get_config_changed)
reconnecting = property(get_reconnecting)
@@ -946,9 +939,18 @@ class RevPiNetIOSelected(RevPiNetIO):
__slots__ = ()
def __init__(
self, address, deviceselection, autorefresh=False,
monitoring=False, syncoutputs=True, simulator=False, debug=True,
replace_io_file=None, shared_procimg=False, direct_output=False):
self,
address,
deviceselection,
autorefresh=False,
monitoring=False,
syncoutputs=True,
simulator=False,
debug=True,
replace_io_file=None,
shared_procimg=False,
direct_output=False,
):
"""
Instantiiert nur fuer angegebene Devices die Grundfunktionen.
@@ -961,8 +963,15 @@ class RevPiNetIOSelected(RevPiNetIO):
:ref: :func:`RevPiNetIO.__init__()`
"""
super().__init__(
address, autorefresh, monitoring, syncoutputs, simulator, debug,
replace_io_file, shared_procimg, direct_output
address,
autorefresh,
monitoring,
syncoutputs,
simulator,
debug,
replace_io_file,
shared_procimg,
direct_output,
)
if type(deviceselection) is not DevSelect:
@@ -981,24 +990,19 @@ class RevPiNetIOSelected(RevPiNetIO):
if len(self.device) == 0:
if self._devselect.type:
raise DeviceNotFoundError(
"could not find ANY given {0} devices in config"
"".format(self._devselect.type)
"could not find ANY given {0} devices in config".format(self._devselect.type)
)
else:
raise DeviceNotFoundError(
"could not find ANY given devices in config"
)
elif not self._devselect.other_device_key \
and len(self.device) != len(self._devselect.values):
raise DeviceNotFoundError("could not find ANY given devices in config")
elif not self._devselect.other_device_key and len(self.device) != len(
self._devselect.values
):
if self._devselect.type:
raise DeviceNotFoundError(
"could not find ALL given {0} devices in config"
"".format(self._devselect.type)
"could not find ALL given {0} devices in config".format(self._devselect.type)
)
else:
raise DeviceNotFoundError(
"could not find ALL given devices in config"
)
raise DeviceNotFoundError("could not find ALL given devices in config")
class RevPiNetIODriver(RevPiNetIOSelected):
@@ -1014,9 +1018,16 @@ class RevPiNetIODriver(RevPiNetIOSelected):
__slots__ = ()
def __init__(
self, address, virtdev, autorefresh=False,
syncoutputs=True, debug=True, replace_io_file=None,
shared_procimg=False, direct_output=False):
self,
address,
virtdev,
autorefresh=False,
syncoutputs=True,
debug=True,
replace_io_file=None,
shared_procimg=False,
direct_output=False,
):
"""
Instantiiert die Grundfunktionen.
@@ -1032,13 +1043,20 @@ class RevPiNetIODriver(RevPiNetIOSelected):
virtdev = (virtdev,)
dev_select = DevSelect(DeviceType.VIRTUAL, "", virtdev)
super().__init__(
address, dev_select, autorefresh, False, syncoutputs, True, debug,
replace_io_file, shared_procimg, direct_output
address,
dev_select,
autorefresh,
False,
syncoutputs,
True,
debug,
replace_io_file,
shared_procimg,
direct_output,
)
def run_net_plc(
address, func, cycletime=50, replace_io_file=None, debug=True):
def run_net_plc(address, func, cycletime=50, replace_io_file=None, debug=True):
"""
Run Revoluton Pi as real plc with cycle loop and exclusive IO access.

View File

@@ -13,6 +13,7 @@ __license__ = "LGPLv2"
# - RevPiConCan_20180425_1_0.rap
# - RevPiGateCANopen_20161102_1_0.rap
class ProductType:
CON_BT = 111
CON_CAN = 109
@@ -65,6 +66,7 @@ class ProductType:
class DeviceType:
"""Module key "type" in piCtory file."""
IGNORED = ""
BASE = "BASE" # Core devices
EDGE = "EDGE" # Gateways
@@ -75,6 +77,7 @@ class DeviceType:
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
@@ -148,6 +151,7 @@ class AIO:
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
@@ -161,6 +165,7 @@ class DI:
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%
@@ -170,11 +175,13 @@ class DO:
class DIO(DI, DO):
"""Memory value mappings for RevPi DIO 1.0 (RevPiDIO_20160818_1_0.rap)."""
pass
class MIO:
"""Memory value mappings for RevPi MIO 1.0 (RevPiMIO_20200901_1_0.rap)."""
ENCODER_MODE_DISABLED = 0
ENCODER_MODE_ENABLED = 1
@@ -193,6 +200,7 @@ class MIO:
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
@@ -206,5 +214,6 @@ class COMPACT:
class FLAT:
"""Memory value mappings for RevPi Flat 1.0 (RevPiFlat_20200921_1_0.rap)."""
IN_RANGE_0_10V = 0
IN_RANGE_4_20MA = 1