py_ext/PythonFileCTNMixin.py
changeset 2692 d4bede6cd3f1
parent 2681 abc2088aac4e
child 2693 7ab2b5a18e65
equal deleted inserted replaced
2691:6e4a95621080 2692:d4bede6cd3f1
    93     def GetSection(self, section):
    93     def GetSection(self, section):
    94         return self.PreSectionsTexts.get(section, "") + "\n" + \
    94         return self.PreSectionsTexts.get(section, "") + "\n" + \
    95                getattr(self.CodeFile, section).getanyText() + "\n" + \
    95                getattr(self.CodeFile, section).getanyText() + "\n" + \
    96                self.PostSectionsTexts.get(section, "")
    96                self.PostSectionsTexts.get(section, "")
    97 
    97 
       
    98     def CTNGlobalInstances(self):
       
    99         variables = self.CodeFileVariables(self.CodeFile)
       
   100         ret = [(variable.getname(),
       
   101                 variable.gettype(),
       
   102                 variable.getinitial())
       
   103                for variable in variables]
       
   104         location_str = "_".join(map(str, self.GetCurrentLocation()))
       
   105         ret.append(("On_"+location_str+"_Change", "python_poll", ""))
       
   106         return ret
       
   107 
    98     def CTNGenerate_C(self, buildpath, locations):
   108     def CTNGenerate_C(self, buildpath, locations):
    99         # location string for that CTN
   109         # location string for that CTN
   100         location_str = "_".join(map(str, self.GetCurrentLocation()))
   110         location_str = "_".join(map(str, self.GetCurrentLocation()))
   101         configname = self.GetCTRoot().GetProjectConfigNames()[0]
   111         configname = self.GetCTRoot().GetProjectConfigNames()[0]
   102 
   112 
   103         def _onchangecode(var):
   113         def _onchangecode(var):
   104             return '"' + var.getonchange() + \
   114             return [onchangecall.strip() + "('" + var.getname() + "')"
   105                 "('" + var.getname() + "')\"" \
   115                     for onchangecall in var.getonchange().split(',')]
   106                 if var.getonchange() else '""'
       
   107 
   116 
   108         def _onchange(var):
   117         def _onchange(var):
   109             return repr(var.getonchange()) \
   118             return repr(var.getonchange()) \
   110                 if var.getonchange() else None
   119                 if var.getonchange() else None
   111 
   120 
   122                 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()),
   131                 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()),
   123                 "initial": repr(variable.getinitial()),
   132                 "initial": repr(variable.getinitial()),
   124                 "pyextname": pyextname
   133                 "pyextname": pyextname
   125             },
   134             },
   126             self.CodeFile.variables.variable)
   135             self.CodeFile.variables.variable)
       
   136 
       
   137         onchange_var_count = len([None for varinfo in varinfos if varinfo["onchange"]])
       
   138 
   127         # python side PLC global variables access stub
   139         # python side PLC global variables access stub
   128         globalstubs = "\n".join([
   140         globalstubs = "\n".join([
   129             """\
   141             """\
   130 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
   142 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
   131     TypeTranslator["%(IECtype)s"]
   143     TypeTranslator["%(IECtype)s"]
   142     %(desc)s,
   154     %(desc)s,
   143     %(onchange)s,
   155     %(onchange)s,
   144     %(opts)s))
   156     %(opts)s))
   145 """ % varinfo for varinfo in varinfos])
   157 """ % varinfo for varinfo in varinfos])
   146 
   158 
       
   159         on_change_func_body = "\n".join(["""
       
   160     if changes.next():
       
   161         # %(name)s
       
   162         try:
       
   163 """ % varinfo + """ 
       
   164             """ + """
       
   165             """.join(varinfo['onchangecode'])+"""
       
   166         except Exception as e:
       
   167             errors.append("%(name)s: "+str(e))
       
   168 """ % varinfo for varinfo in varinfos if varinfo["onchange"]])
   147         # Runtime calls (start, stop, init, and cleanup)
   169         # Runtime calls (start, stop, init, and cleanup)
   148         rtcalls = ""
   170         rtcalls = ""
   149         for section in self.SECTIONS_NAMES:
   171         for section in self.SECTIONS_NAMES:
   150             if section != "globals":
   172             if section != "globals":
   151                 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
   173                 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
   161         loc_dict = {
   183         loc_dict = {
   162             "pyextname": pyextname,
   184             "pyextname": pyextname,
   163             "globalstubs": globalstubs,
   185             "globalstubs": globalstubs,
   164             "globalsection": globalsection,
   186             "globalsection": globalsection,
   165             "rtcalls": rtcalls,
   187             "rtcalls": rtcalls,
       
   188             "location_str": location_str,
       
   189             "on_change_func_body":on_change_func_body,
       
   190             "onchange_var_count": onchange_var_count
   166         }
   191         }
   167 
   192 
   168         PyFileContent = """\
   193         PyFileContent = """\
   169 #!/usr/bin/env python
   194 #!/usr/bin/env python
   170 # -*- coding: utf-8 -*-
   195 # -*- coding: utf-8 -*-
   172 ##
   197 ##
   173 
   198 
   174 ## Code for PLC global variable access
   199 ## Code for PLC global variable access
   175 from runtime.typemapping import TypeTranslator
   200 from runtime.typemapping import TypeTranslator
   176 import ctypes
   201 import ctypes
       
   202 
       
   203 _PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s
       
   204 _PySafeGetChanges_%(pyextname)s.restype = ctypes.POINTER(ctypes.c_int * %(onchange_var_count)d)
       
   205 _PySafeGetChanges_%(pyextname)s.argtypes = None
       
   206 
   177 _%(pyextname)sGlobalsDesc = []
   207 _%(pyextname)sGlobalsDesc = []
   178 __ext_name__ = "%(pyextname)s"
   208 __ext_name__ = "%(pyextname)s"
   179 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
   209 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
   180 %(globalstubs)s
   210 %(globalstubs)s
   181 
   211 
   182 ## User code in "global" scope
   212 ## User code in "global" scope
   183 %(globalsection)s
   213 %(globalsection)s
   184 
   214 
   185 ## Beremiz python runtime calls
   215 ## Beremiz python runtime calls
   186 %(rtcalls)s
   216 %(rtcalls)s
       
   217 
       
   218 def On_%(pyextname)s_Change():
       
   219     changesP = _PySafeGetChanges_%(pyextname)s()
       
   220     if not changesP:
       
   221         raise Exception("PySafeGetChanges returned NULL!")
       
   222     changes = iter(changesP.contents)
       
   223     errors = []
       
   224 %(on_change_func_body)s
       
   225     if len(errors)>0 :
       
   226         raise Exception("Exception in %(pyextname)s OnChange call:\\\\n" + "\\\\n".join(errors))
   187 
   227 
   188 del __ext_name__
   228 del __ext_name__
   189 
   229 
   190 """ % loc_dict
   230 """ % loc_dict
   191 
   231 
   218 }
   258 }
   219 
   259 
   220 """
   260 """
   221 
   261 
   222         vardeconchangefmt = """\
   262         vardeconchangefmt = """\
   223 PYTHON_POLL* __%(name)s_notifier;
   263 int __%(name)s_rbuffer_written = 0;
   224 """
   264 """
   225 
   265 
   226         varretfmt = """\
   266         varretfmt = """\
   227     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
   267     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
   228         if(__%(name)s_wbuffer_written == 1){
   268         if(__%(name)s_wbuffer_written == 1){
   242         varpubonchangefmt = """\
   282         varpubonchangefmt = """\
   243     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   283     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   244         IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
   284         IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
   245         if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){
   285         if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){
   246             __%(name)s_rbuffer = tmp;
   286             __%(name)s_rbuffer = tmp;
   247             PYTHON_POLL_body__(__%(name)s_notifier);
   287             /* mark variable as changed */
       
   288             __%(name)s_rbuffer_written = 1;
       
   289             some_change = 1;
   248         }
   290         }
   249         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   291         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   250     }
   292     }
   251 """
   293 """
   252         varinitonchangefmt = """\
   294 
   253     __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE();
   295         varcollectchangefmt = """\
   254     __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
   296     while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1));
   255     __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s));
   297     pysafe_changes[change_index++] = __%(name)s_rbuffer_written;
       
   298     /* mark variable as unchanged */
       
   299     __%(name)s_rbuffer_written = 0;
       
   300     AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
       
   301 
   256 """
   302 """
   257         vardec = "\n".join([(vardecfmt + vardeconchangefmt
   303         vardec = "\n".join([(vardecfmt + vardeconchangefmt
   258                              if varinfo["onchange"] else vardecfmt) % varinfo
   304                              if varinfo["onchange"] else vardecfmt) % varinfo
   259                             for varinfo in varinfos])
   305                             for varinfo in varinfos])
   260         varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
   306         varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
   261         varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
   307         varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
   262                              varpubfmt) % varinfo
   308                              varpubfmt) % varinfo
   263                             for varinfo in varinfos])
   309                             for varinfo in varinfos])
   264         varinit = "\n".join([varinitonchangefmt %
   310         varcollectchange = "\n".join([varcollectchangefmt % varinfo
   265                              dict(onchangelen=len(varinfo["onchangecode"]), **varinfo)
       
   266                              for varinfo in varinfos if varinfo["onchange"]])
   311                              for varinfo in varinfos if varinfo["onchange"]])
       
   312 
       
   313         pysafe_pypoll_code = "On_"+pyextname+"_Change()"
   267 
   314 
   268         loc_dict = {
   315         loc_dict = {
   269             "vardec": vardec,
   316             "vardec": vardec,
   270             "varinit": varinit,
       
   271             "varret": varret,
   317             "varret": varret,
   272             "varpub": varpub,
   318             "varpub": varpub,
   273             "location_str": location_str,
   319             "location_str": location_str,
       
   320             "pysafe_pypoll_code": '"'+pysafe_pypoll_code+'"',
       
   321             "pysafe_pypoll_code_len": len(pysafe_pypoll_code),
       
   322             "varcollectchange": varcollectchange,
       
   323             "onchange_var_count": onchange_var_count
   274         }
   324         }
   275 
   325 
   276         # TODO : use config name obtained from model instead of default
   326         # TODO : use config name obtained from model instead of default
   277         # "config.h". User cannot change config name, but project imported
   327         # "config.h". User cannot change config name, but project imported
   278         # or created in older beremiz vesion could use different name.
   328         # or created in older beremiz vesion could use different name.
   284 #include "iec_types_all.h"
   334 #include "iec_types_all.h"
   285 #include "POUS.h"
   335 #include "POUS.h"
   286 #include "config.h"
   336 #include "config.h"
   287 #include "beremiz.h"
   337 #include "beremiz.h"
   288 
   338 
       
   339 PYTHON_POLL* __%(location_str)s_notifier;
       
   340 
   289 /* User variables reference */
   341 /* User variables reference */
   290 %(vardec)s
   342 %(vardec)s
   291 
   343 
   292 /* Beremiz confnode functions */
   344 /* Beremiz confnode functions */
   293 int __init_%(location_str)s(int argc,char **argv){
   345 int __init_%(location_str)s(int argc,char **argv){
   294 %(varinit)s
   346     __%(location_str)s_notifier = __GET_GLOBAL_ON_%(location_str)s_CHANGE();
       
   347     __SET_VAR(__%(location_str)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
       
   348     __SET_VAR(__%(location_str)s_notifier->,CODE,,__STRING_LITERAL(%(pysafe_pypoll_code_len)d,%(pysafe_pypoll_code)s));
       
   349 
   295     return 0;
   350     return 0;
   296 }
   351 }
   297 
   352 
   298 void __cleanup_%(location_str)s(void){
   353 void __cleanup_%(location_str)s(void){
   299 }
   354 }
   301 void __retrieve_%(location_str)s(void){
   356 void __retrieve_%(location_str)s(void){
   302 %(varret)s
   357 %(varret)s
   303 }
   358 }
   304 
   359 
   305 void __publish_%(location_str)s(void){
   360 void __publish_%(location_str)s(void){
       
   361     int some_change = 0;
   306 %(varpub)s
   362 %(varpub)s
   307 }
   363     // call python part if there was at least a change
       
   364     if(some_change){
       
   365         PYTHON_POLL_body__(__%(location_str)s_notifier);
       
   366     }
       
   367 }
       
   368 
       
   369 static int pysafe_changes[%(onchange_var_count)d];
       
   370 void* PySafeGetChanges_%(location_str)s(void){
       
   371     int change_index=0;
       
   372 %(varcollectchange)s
       
   373     return (void*)&pysafe_changes[0];
       
   374 }
       
   375 
   308 """ % loc_dict
   376 """ % loc_dict
   309 
   377 
   310         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
   378         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
   311         pycfile = open(Gen_PyCfile_path, 'w')
   379         pycfile = open(Gen_PyCfile_path, 'w')
   312         pycfile.write(PyCFileContent)
   380         pycfile.write(PyCFileContent)