Sync write of outputs with autorefresh/cycle or .writeprocimg, if shared_procimg=True

If shared_procimg=True, ModIO wrote the new set values directly to the process
image without cycle sync mechanisms. As a result, the IOs could change status
several times within the cycleloop, which is wrong.
This commit is contained in:
2021-01-31 13:31:05 +01:00
parent 57c56ce207
commit 6608bd965c
5 changed files with 119 additions and 68 deletions

View File

@@ -129,6 +129,7 @@ class Device(object):
"_offset", "_position", "_producttype", "_selfupdate", \ "_offset", "_position", "_producttype", "_selfupdate", \
"_slc_devoff", "_slc_inp", "_slc_inpoff", "_slc_mem", \ "_slc_devoff", "_slc_inp", "_slc_inpoff", "_slc_mem", \
"_slc_memoff", "_slc_out", "_slc_outoff", "_shared_procimg", \ "_slc_memoff", "_slc_out", "_slc_outoff", "_shared_procimg", \
"_shared_write", \
"bmk", "catalognr", "comment", "extend", \ "bmk", "catalognr", "comment", "extend", \
"guid", "id", "inpvariant", "outvariant", "type" "guid", "id", "inpvariant", "outvariant", "type"
@@ -148,6 +149,7 @@ class Device(object):
self.__my_io_list = [] self.__my_io_list = []
self._selfupdate = False self._selfupdate = False
self._shared_procimg = parentmodio._shared_procimg self._shared_procimg = parentmodio._shared_procimg
self._shared_write = []
# Wertzuweisung aus dict_device # Wertzuweisung aus dict_device
self._name = dict_device.get("name") self._name = dict_device.get("name")
@@ -511,6 +513,8 @@ class Device(object):
:param activate: Set True to activate process image sharing :param activate: Set True to activate process image sharing
""" """
with self._filelock:
self._shared_write.clear()
self._shared_procimg = True if activate else False self._shared_procimg = True if activate else False
def syncoutputs(self) -> bool: def syncoutputs(self) -> bool:

View File

@@ -536,7 +536,20 @@ class ProcimgWriter(Thread):
for dev in self._modio._lst_refresh: for dev in self._modio._lst_refresh:
with dev._filelock: with dev._filelock:
if self._modio._monitoring or dev._shared_procimg: if self._modio._monitoring:
# 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:
self.__check_change(dev)
elif dev._shared_procimg:
for io in dev._shared_write:
if not io._write_to_procimg():
raise IOError("error on _write_to_procimg")
dev._shared_write.clear()
# Inputs und Outputs in Puffer # Inputs und Outputs in Puffer
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff] dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
if self.__eventwork \ if self.__eventwork \

View File

@@ -532,6 +532,80 @@ class IOBase(object):
raise ValueError("Value must be <class 'bool'>") raise ValueError("Value must be <class 'bool'>")
self._export = 2 + int(value) self._export = 2 + int(value)
def _write_to_procimg(self) -> bool:
"""
Write value of io directly to the process image.
:return: True after successful write operation
"""
if not self._parentdevice._shared_procimg:
raise RuntimeError("device is not marked for shared_procimg")
# note: Will not be removed from _shared_write on direct call
if self._bitshift:
# Write single bit to process image
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:
try:
# Set value durchführen (Funktion K+16)
ioctl(
self._parentdevice._modio._myfh,
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
)
except Exception as e:
self._parentdevice._modio._gotioerror("ioset", e)
return False
elif hasattr(self._parentdevice._modio._myfh, "ioctl"):
# IOCTL über Netzwerk
with self._parentdevice._modio._myfh_lck:
try:
self._parentdevice._modio._myfh.ioctl(
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
)
except Exception as e:
self._parentdevice._modio._gotioerror(
"net_ioset", e)
return False
else:
# IOCTL in Datei simulieren
try:
# Set value durchführen (Funktion K+16)
self._parentdevice._modio._simulate_ioctl(
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
)
except Exception as e:
self._parentdevice._modio._gotioerror("file_ioset", e)
return False
else:
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.write(value)
if self._parentdevice._modio._buffedwrite:
self._parentdevice._modio._myfh.flush()
except IOError as e:
self._parentdevice._modio._gotioerror("ioset", e)
return False
return True
def get_defaultvalue(self): def get_defaultvalue(self):
""" """
Gibt die Defaultvalue von piCtory zurueck. Gibt die Defaultvalue von piCtory zurueck.
@@ -624,54 +698,14 @@ class IOBase(object):
# Versuchen egal welchen Typ in Bool zu konvertieren # Versuchen egal welchen Typ in Bool zu konvertieren
value = bool(value) value = bool(value)
if self._parentdevice._shared_procimg:
# Direktes Schreiben der Outputs
if self._parentdevice._modio._run_on_pi:
# IOCTL auf dem RevPi
with self._parentdevice._modio._myfh_lck:
try:
# Set value durchführen (Funktion K+16)
ioctl(
self._parentdevice._modio._myfh,
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
)
except Exception as e:
self._parentdevice._modio._gotioerror("ioset", e)
return
elif hasattr(self._parentdevice._modio._myfh, "ioctl"):
# IOCTL über Netzwerk
with self._parentdevice._modio._myfh_lck:
try:
self._parentdevice._modio._myfh.ioctl(
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
)
except Exception as e:
self._parentdevice._modio._gotioerror(
"net_ioset", e)
return
else:
# IOCTL in Datei simulieren
try:
# Set value durchführen (Funktion K+16)
self._parentdevice._modio._simulate_ioctl(
19216,
self.__bit_ioctl_on if value
else self.__bit_ioctl_off
)
except Exception as e:
self._parentdevice._modio._gotioerror("file_ioset", e)
return
# Für Bitoperationen sperren # Für Bitoperationen sperren
self._parentdevice._filelock.acquire() self._parentdevice._filelock.acquire()
if self._parentdevice._shared_procimg \
and self not in self._parentdevice._shared_write:
# Mark this IO for write operations
self._parentdevice._shared_write.append(self)
# Hier gibt es immer nur ein byte, als int holen # Hier gibt es immer nur ein byte, als int holen
int_byte = self._parentdevice._ba_devdata[self._slc_address.start] int_byte = self._parentdevice._ba_devdata[self._slc_address.start]
@@ -704,18 +738,11 @@ class IOBase(object):
) )
) )
if self._parentdevice._shared_procimg: if self._parentdevice._shared_procimg \
with self._parentdevice._modio._myfh_lck: and self not in self._parentdevice._shared_write:
try: with self._parentdevice._filelock:
self._parentdevice._modio._myfh.seek( # Mark this IO as changed
self._get_address() self._parentdevice._shared_write.append(self)
)
self._parentdevice._modio._myfh.write(value)
if self._parentdevice._modio._buffedwrite:
self._parentdevice._modio._myfh.flush()
except IOError as e:
self._parentdevice._modio._gotioerror("ioset", e)
return
self._parentdevice._ba_devdata[self._slc_address] = value self._parentdevice._ba_devdata[self._slc_address] = value

View File

@@ -1227,13 +1227,21 @@ class RevPiModIO(object):
mylist = [dev] mylist = [dev]
global_ex = None global_ex = None
workokay = True
for dev in mylist: for dev in mylist:
if dev._shared_procimg: if dev._selfupdate:
# Do not update this device
continue continue
elif not dev._selfupdate:
dev._filelock.acquire()
dev._filelock.acquire()
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"
)
dev._shared_write.clear()
else:
# Outpus auf Bus schreiben # Outpus auf Bus schreiben
self._myfh_lck.acquire() self._myfh_lck.acquire()
try: try:
@@ -1241,23 +1249,22 @@ class RevPiModIO(object):
self._myfh.write(dev._ba_devdata[dev._slc_out]) self._myfh.write(dev._ba_devdata[dev._slc_out])
except IOError as e: except IOError as e:
global_ex = e global_ex = e
workokay = False
finally: finally:
self._myfh_lck.release() self._myfh_lck.release()
dev._filelock.release() dev._filelock.release()
if self._buffedwrite: if self._buffedwrite:
try: try:
self._myfh.flush() self._myfh.flush()
except IOError as e: except IOError as e:
global_ex = e global_ex = e
workokay = False
if not workokay: if global_ex:
self._gotioerror("writeprocimg", global_ex) self._gotioerror("writeprocimg", global_ex)
return False
return workokay return True
debug = property(_get_debug, _set_debug) debug = property(_get_debug, _set_debug)
configrsc = property(_get_configrsc) configrsc = property(_get_configrsc)

View File

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