--- a/py_ext/PythonFileCTNMixin.py Fri Jul 03 14:02:04 2020 +0200
+++ b/py_ext/PythonFileCTNMixin.py Fri Jul 10 11:15:41 2020 +0200
@@ -101,9 +101,8 @@
for variable in variables]
- ret.extend([("On"+variable.getname()+"Change", "python_poll", "")
- for variable in variables
- if variable.getonchange()])
+ location_str = "_".join(map(str, self.GetCurrentLocation())) + ret.append(("On_"+location_str+"_Change", "python_poll", "")) def CTNGenerate_C(self, buildpath, locations):
@@ -112,9 +111,7 @@
configname = self.GetCTRoot().GetProjectConfigNames()[0]
- return '"' + var.getonchange() + \
- "('" + var.getname() + "')\"" \
- if var.getonchange() else '""'
+ return var.getonchange() + "('" + var.getname() + "')" return repr(var.getonchange()) \
@@ -135,6 +132,9 @@
self.CodeFile.variables.variable)
+ onchange_var_count = len([None for varinfo in varinfos if varinfo["onchange"]]) # python side PLC global variables access stub
globalstubs = "\n".join([
@@ -155,6 +155,14 @@
""" % varinfo for varinfo in varinfos])
+ on_change_func_body = "\n".join([""" + errors.append("%(name)s: "+str(e)) +""" % varinfo for varinfo in varinfos if varinfo["onchange"]]) # Runtime calls (start, stop, init, and cleanup)
for section in self.SECTIONS_NAMES:
@@ -174,6 +182,9 @@
"globalstubs": globalstubs,
"globalsection": globalsection,
+ "location_str": location_str, + "on_change_func_body":on_change_func_body, + "onchange_var_count": onchange_var_count @@ -185,6 +196,11 @@
## Code for PLC global variable access
from runtime.typemapping import TypeTranslator
+_PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s +_PySafeGetChanges_%(pyextname)s.restype = ctypes.POINTER(ctypes.c_int * %(onchange_var_count)d) +_PySafeGetChanges_%(pyextname)s.argtypes = None _%(pyextname)sGlobalsDesc = []
__ext_name__ = "%(pyextname)s"
PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
@@ -196,6 +212,16 @@
## Beremiz python runtime calls
+def On_%(pyextname)s_Change(): + changesP = _PySafeGetChanges_%(pyextname)s() + raise Exception("PySafeGetChanges returned NULL!") + changes = iter(changesP.contents) + raise Exception("Exception in %(pyextname)s OnChange call:\\\\n" + "\\\\n".join(errors)) @@ -231,7 +257,7 @@
-PYTHON_POLL* __%(name)s_notifier;
+int __%(name)s_rbuffer_written = 0; @@ -255,15 +281,21 @@
IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){
__%(name)s_rbuffer = tmp;
- PYTHON_POLL_body__(__%(name)s_notifier);
+ /* mark variable as changed */ + __%(name)s_rbuffer_written = 1; AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
- varinitonchangefmt = """\
- __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE();
- __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
- __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s));
+ varcollectchangefmt = """\ + while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1)); + pysafe_changes[change_index++] = __%(name)s_rbuffer_written; + /* mark variable as unchanged */ + __%(name)s_rbuffer_written = 0; + AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); vardec = "\n".join([(vardecfmt + vardeconchangefmt
if varinfo["onchange"] else vardecfmt) % varinfo
@@ -272,16 +304,20 @@
varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
for varinfo in varinfos])
- varinit = "\n".join([varinitonchangefmt %
- dict(onchangelen=len(varinfo["onchangecode"]), **varinfo)
+ varcollectchange = "\n".join([varcollectchangefmt % varinfo for varinfo in varinfos if varinfo["onchange"]])
+ pysafe_pypoll_code = "On_"+pyextname+"_Change()"
"location_str": location_str,
+ "pysafe_pypoll_code": '"'+pysafe_pypoll_code+'"', + "pysafe_pypoll_code_len": len(pysafe_pypoll_code), + "varcollectchange": varcollectchange, + "onchange_var_count": onchange_var_count # TODO : use config name obtained from model instead of default
@@ -297,12 +333,17 @@
+PYTHON_POLL* __%(location_str)s_notifier; /* User variables reference */
/* Beremiz confnode functions */
int __init_%(location_str)s(int argc,char **argv){
+ __%(location_str)s_notifier = __GET_GLOBAL_ON_%(location_str)s_CHANGE(); + __SET_VAR(__%(location_str)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); + __SET_VAR(__%(location_str)s_notifier->,CODE,,__STRING_LITERAL(%(pysafe_pypoll_code_len)d,%(pysafe_pypoll_code)s)); @@ -314,8 +355,21 @@
void __publish_%(location_str)s(void){
+ // call python part if there was at least a change + PYTHON_POLL_body__(__%(location_str)s_notifier); +static int pysafe_changes[%(onchange_var_count)d]; +void* PySafeGetChanges_%(location_str)s(void){ + return (void*)&pysafe_changes[0]; Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)