# HG changeset patch # User Edouard Tisserant # Date 1607596647 -3600 # Node ID 9b50ffe6264aa588ce53891bb48575cb1b3e2f15 # Parent a70a97196654634487b5b52c0bc4a0f9f111e9bc 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 a70a97196654 -r 9b50ffe6264a py_ext/PythonFileCTNMixin.py --- a/py_ext/PythonFileCTNMixin.py Mon Dec 07 09:49:34 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 a70a97196654 -r 9b50ffe6264a runtime/PLCObject.py --- a/runtime/PLCObject.py Mon Dec 07 09:49:34 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,