# HG changeset patch # User Edouard Tisserant # Date 1607596647 -3600 # Node ID 93333d206198379df88f499ccee440243301af5f # Parent 9bd639e9124efcdc24b8a07df98828caa12767be Python Safe Globals now have more reliable triggering of OnChange call. Added "Onchange" object to accessible runtime variables that let user python code see count of changes and first and last values. diff -r 9bd639e9124e -r 93333d206198 py_ext/PythonFileCTNMixin.py --- a/py_ext/PythonFileCTNMixin.py Fri Nov 20 11:17:40 2020 +0100 +++ b/py_ext/PythonFileCTNMixin.py Thu Dec 10 11:37:27 2020 +0100 @@ -169,10 +169,14 @@ %(desc)s, %(onchange)s, %(opts)s)) -""" % varinfo for varinfo in varinfos]) +""" % varinfo + (""" +_PyOnChangeCount_%(name)s = ctypes.c_uint.in_dll(PLCBinary,"__%(name)s_onchange_count") +_PyOnChangeFirst_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_firstval") +_PyOnChangeLast_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_lastval") +""" % varinfo if varinfo["onchange"] else "") for varinfo in varinfos]) on_change_func_body = "\n".join([""" - if changes.next(): + if _PyOnChangeCount_%(name)s.value > 0: # %(name)s try:""" % varinfo + """ """ + """ @@ -215,7 +219,7 @@ import ctypes _PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s -_PySafeGetChanges_%(pyextname)s.restype = ctypes.POINTER(ctypes.c_int * %(onchange_var_count)d) +_PySafeGetChanges_%(pyextname)s.restype = None _PySafeGetChanges_%(pyextname)s.argtypes = None _%(pyextname)sGlobalsDesc = [] @@ -230,10 +234,7 @@ %(rtcalls)s def On_%(pyextname)s_Change(): - changesP = _PySafeGetChanges_%(pyextname)s() - if not changesP: - raise Exception("PySafeGetChanges returned NULL!") - changes = iter(changesP.contents) + _PySafeGetChanges_%(pyextname)s() errors = [] %(on_change_func_body)s if len(errors)>0 : @@ -274,7 +275,12 @@ """ vardeconchangefmt = """\ -int __%(name)s_rbuffer_written = 0; +unsigned int __%(name)s_rbuffer_written = 0; +IEC_%(IECtype)s __%(name)s_rbuffer_firstval; +IEC_%(IECtype)s __%(name)s_rbuffer_lastval; +unsigned int __%(name)s_onchange_count = 0; +IEC_%(IECtype)s __%(name)s_onchange_firstval; +IEC_%(IECtype)s __%(name)s_onchange_lastval; """ varretfmt = """\ @@ -297,21 +303,26 @@ if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s); if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){ + if(__%(name)s_rbuffer_written == 0); + __%(name)s_rbuffer_firstval = __%(name)s_rbuffer; + __%(name)s_rbuffer_lastval = tmp; __%(name)s_rbuffer = tmp; - /* mark variable as changed */ - __%(name)s_rbuffer_written = 1; - some_change = 1; + /* count one more change */ + __%(name)s_rbuffer_written += 1; + some_change_found = 1; } AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); } """ varcollectchangefmt = """\ - while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1)); - pysafe_changes[change_index++] = __%(name)s_rbuffer_written; + while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1)); + __%(name)s_onchange_count = __%(name)s_rbuffer_written; + __%(name)s_onchange_firstval = __%(name)s_rbuffer_firstval; + __%(name)s_onchange_lastval = __%(name)s_rbuffer_lastval; /* mark variable as unchanged */ __%(name)s_rbuffer_written = 0; - AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); + AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); """ vardec = "\n".join([(vardecfmt + vardeconchangefmt @@ -371,20 +382,20 @@ %(varret)s } +static int passing_changes_to_python = 0; void __publish_%(location_str)s(void){ - int some_change = 0; + int some_change_found = 0; %(varpub)s + passing_changes_to_python |= some_change_found; // call python part if there was at least a change - if(some_change){ + if(passing_changes_to_python){ PYTHON_POLL_body__(__%(location_str)s_notifier); + passing_changes_to_python &= !(__GET_VAR(__%(location_str)s_notifier->ACK,)); } } -static int pysafe_changes[%(onchange_var_count)d]; void* PySafeGetChanges_%(location_str)s(void){ - int change_index=0; %(varcollectchange)s - return (void*)&pysafe_changes[0]; } """ % loc_dict diff -r 9bd639e9124e -r 93333d206198 runtime/PLCObject.py --- a/runtime/PLCObject.py Fri Nov 20 11:17:40 2020 +0100 +++ b/runtime/PLCObject.py Thu Dec 10 11:37:27 2020 +0100 @@ -360,8 +360,19 @@ v = parent.python_runtime_vars["_"+name+"_pack"](t, value) parent.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v)) + class OnChangeStateClass(object): + def __getattr__(self, name): + res = object() + u = parent.python_runtime_vars["_"+name+"_unpack"] + res.count = parent.python_runtime_vars["_PyOnChangeCount_"+name].value + res.first = u(parent.python_runtime_vars["_PyOnChangeFirst_"+name]) + res.last = u(parent.python_runtime_vars["_PyOnChangeLast_"+name]) + return res + + self.python_runtime_vars.update({ "PLCGlobals": PLCSafeGlobals(), + "OnChange": OnChangeStateClass(), "WorkingDir": self.workingdir, "PLCObject": self, "PLCBinary": self.PLClibraryHandle,