Added stub code and declarations for bidirectional access to PLC globals from python code (untested)
authorEdouard Tisserant
Wed, 15 May 2013 17:13:49 +0900
changeset 1144 21475ee0e688
parent 1143 59818c488ead
child 1145 203f4eff3313
child 1146 510d1ea1f6c1
Added stub code and declarations for bidirectional access to PLC globals from python code (untested)
py_ext/PythonFileCTNMixin.py
runtime/PLCObject.py
--- a/py_ext/PythonFileCTNMixin.py	Wed May 15 08:20:17 2013 +0200
+++ b/py_ext/PythonFileCTNMixin.py	Wed May 15 17:13:49 2013 +0900
@@ -55,21 +55,28 @@
         
 
     def CTNGenerate_C(self, buildpath, locations):
-        current_location = self.GetCurrentLocation()
-        # define a unique name for the generated C file
-        location_str = "_".join(map(lambda x:str(x), current_location))
-        
-        
-        # Generate Beremiz python runtime variables code
-        config = self.GetCTRoot().GetProjectConfigNames()[0]
-        variables_str = ""
-        for variable in self.CodeFile.variables.variable:
-            global_name = "%s_%s" % (config.upper(), variable.getname().upper())
-            variables_str += "# global_var:%s python_var:%s type:%s initial:%s\n" % (
-                global_name,
-                variable.getname(),
-                variable.gettype(),
-                str(variable.getinitial()))
+        # location string for that CTN 
+        location_str = "_".join(map(lambda x:str(x), 
+                                self.GetCurrentLocation()))
+        configname = self.GetCTRoot().GetProjectConfigNames()[0]
+        
+        
+        # python side PLC global variables access stub
+        globalstubs = "\n".join(["""\
+_%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
+    TypeTranslator["%(IECtype)s"]
+_PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s
+_PySafeGetPLCGlob_%(name)s.restype = _%(name)s_ctype 
+_PySafeGetPLCGlob_%(name)s.argtypes = []
+_PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
+_PySafeSetPLCGlob_%(name)s.restype = None
+_PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
+""" % { "name": variable.getname(),
+        "configname": configname.upper(),
+        "uppername": variable.getname().upper(),
+        "IECtype": variable.gettype(),
+        "initial" : str(variable.getinitial())}
+            for variable in self.CodeFile.variables.variable])
 
         # Runtime calls (start, stop, init, and cleanup)
         rtcalls = ""
@@ -82,76 +89,117 @@
                         sectiontext.strip().replace('\n', '\n    ')+"\n"
                 else:
                     rtcalls += "    pass\n\n"
-        
-        text = """\
+
+        globalsection = self.GetSection("globals")      
+
+        PyFileContent = """\
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 ## Code generated by Beremiz python mixin confnode
 ##        
         
 ## Code for PLC global variable access
-%s
+from targets.typemapping import TypeTranslator
+import ctypes 
+%(globalstubs)s
         
 ## User code in "global" scope
-%s
+%(globalsection)s
 
 ## Beremiz python runtime calls
-%s
-
-"""%(   # variables
-        variables_str,
-        # globals
-        self.GetSection("globals"),
-        # Beremiz python runtime functions
-        rtcalls)
-
+%(rtcalls)s
+
+""" % locals()
+
+        # write generated content to python file
         runtimefile_path = os.path.join(buildpath, 
             "runtime_%s.py"%location_str)
         runtimefile = open(runtimefile_path, 'w')
-        runtimefile.write(text.encode('utf-8'))
+        runtimefile.write(PyFileContent.encode('utf-8'))
         runtimefile.close()
 
-        text = """\
+        # C code for safe global variables access
+        
+        vardecfmt = """\
+extern  __%(IECtype)s_t %(configname)s__%(uppername)s;
+%(IECtype)s __%(name)s_rbuffer = %(initial)s;
+%(IECtype)s __%(name)s_wbuffer;
+long __%(name)s_rlock = 0;
+long __%(name)s_wlock = 0;
+int __%(name)s_wbuffer_written = 0;
+%(IECtype)s __SafeGetPLCGlob_%(name)s(){
+    %(IECtype)s res;
+    while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1));
+    res = __%(name)s_rbuffer;
+    AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
+    return res;
+}
+__SafeSetPLCGlob_%(name)s(%(IECtype)s *value){
+    while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1));
+    __%(name)s_wbuffer = *value;
+    __%(name)s_wbuffer_written = 1;
+    AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
+}
+
+"""
+        varretfmt = """\
+    if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
+        if(__%(name)s_wbuffer_written == 1){
+            %(configname)s__%(uppername)s.value = __%(name)s_wbuffer;
+            __%(name)s_wbuffer_written = 0;
+        }
+        AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
+    }
+""" 
+        varpubfmt = """\
+    if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
+        __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
+        AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
+    }
+""" 
+
+        vardec, varret, varpub = map("\n".join, zip(*[
+            map(lambda f : f % varinfo,
+                (vardecfmt, varretfmt, varpubfmt))
+                for varinfo in map(lambda variable : {
+                    "name": variable.getname(),
+                    "configname": configname.upper(),
+                    "uppername": variable.getname().upper(),
+                    "IECtype": "IEC_%s"%variable.gettype(),
+                    "initial" : str(variable.getinitial())},
+                    self.CodeFile.variables.variable)]))
+        
+        PyCFileContent = """\
 /* 
  * Code generated by Beremiz py_ext confnode 
  * for safe global variables access
  */
 #include "iec_types_all.h"
-"""
-        
-        # Adding variables
-        text += "/* User variables reference */\n"
-        for variable in self.CodeFile.variables.variable:
-            var_infos = {
-                "name": variable.getname(),
-                "global": "%s__%s" % (config.upper(),
-                                      variable.getname().upper()),
-                "type": "__IEC_%s_t" % variable.gettype()}
-            text += "extern %(type)s %(global)s;\n" % var_infos
-            text += "%(type)s __buffer_%(name)s;\n" % var_infos
-        text += "\n"
-        
-        # Adding Beremiz confnode functions
-        text += "/* Beremiz confnode functions */\n"
-        text += "int __init_%s(int argc,char **argv)\n{\n"%location_str
-        text += "/*TODO*/\n"
-        text += "  return 0;\n}\n\n"
-        
-        text += "void __cleanup_%s(void)\n{\n"%location_str
-        text += "/*TODO*/\n"
-        text += "\n}\n\n"
-        
-        text += "void __retrieve_%s(void)\n{\n"%location_str
-        text += "/*TODO*/\n"
-        text += "\n}\n\n"
-        
-        text += "void __publish_%s(void)\n{\n"%location_str
-        text += "/*TODO*/\n"
-        text += "\n}\n\n"
+#include "beremiz.h"
+
+/* User variables reference */
+%(vardec)s
+
+/* Beremiz confnode functions */
+int __init_%(location_str)s(int argc,char **argv){
+    return 0;
+}
+
+void __cleanup_%(location_str)s(void){
+}
+
+void __retrieve_%(location_str)s(void){
+%(varret)s
+}
+
+void __publish_%(location_str)s(void){
+%(varpub)s
+}
+""" % locals()
         
         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str)
         pycfile = open(Gen_PyCfile_path,'w')
-        pycfile.write(text)
+        pycfile.write(PyCFileContent)
         pycfile.close()
         
         matiec_flags = '"-I%s"'%os.path.abspath(
--- a/runtime/PLCObject.py	Wed May 15 08:20:17 2013 +0200
+++ b/runtime/PLCObject.py	Wed May 15 17:13:49 2013 +0900
@@ -264,6 +264,14 @@
             self.python_runtime_vars["_runtime_%s"%methodname] = []
         self.python_runtime_vars["PLCObject"] = self
         self.python_runtime_vars["PLCBinary"] = self.PLClibraryHandle
+        class PLCSafeGlobals:
+            def __getattr__(self, name):
+                r = globals()["_PySafeGetPLCGlob_"+name]()
+                return globals()["_"+name+"_unpack"](r)
+            def __setattr__(self, name, value):
+                v = globals()["_"+name+"_pack"](c_type,value)
+                globals()["_PySafeSetPLCGlob_"+name](ctypes.byref(v))
+        self.python_runtime_vars["PLCGlobals"] = PLCSafeGlobals()
         try:
             for filename in os.listdir(self.workingdir):
                 name, ext = os.path.splitext(filename)