py_ext/PythonFileCTNMixin.py
changeset 2719 745b64e7c695
parent 2697 93333d206198
child 3096 fc5a0b1ece10
equal deleted inserted replaced
2718:76e8ec46828a 2719:745b64e7c695
    34 
    34 
    35 from CodeFileTreeNode import CodeFile
    35 from CodeFileTreeNode import CodeFile
    36 from py_ext.PythonEditor import PythonEditor
    36 from py_ext.PythonEditor import PythonEditor
    37 
    37 
    38 
    38 
       
    39 
       
    40 
    39 class PythonFileCTNMixin(CodeFile):
    41 class PythonFileCTNMixin(CodeFile):
    40 
    42 
    41     CODEFILE_NAME = "PyFile"
    43     CODEFILE_NAME = "PyFile"
    42     SECTIONS_NAMES = [
    44     SECTIONS_NAMES = [
    43         "globals",
    45         "globals",
    93     def GetSection(self, section):
    95     def GetSection(self, section):
    94         return self.PreSectionsTexts.get(section, "") + "\n" + \
    96         return self.PreSectionsTexts.get(section, "") + "\n" + \
    95                getattr(self.CodeFile, section).getanyText() + "\n" + \
    97                getattr(self.CodeFile, section).getanyText() + "\n" + \
    96                self.PostSectionsTexts.get(section, "")
    98                self.PostSectionsTexts.get(section, "")
    97 
    99 
       
   100     def CTNGlobalInstances(self):
       
   101         variables = self.CodeFileVariables(self.CodeFile)
       
   102         ret = [(variable.getname(),
       
   103                 variable.gettype(),
       
   104                 variable.getinitial())
       
   105                for variable in variables]
       
   106         location_str = "_".join(map(str, self.GetCurrentLocation()))
       
   107         ret.append(("On_"+location_str+"_Change", "python_poll", ""))
       
   108         return ret
       
   109 
       
   110     @staticmethod
       
   111     def GetVarOnChangeContent(var):
       
   112         """
       
   113         returns given variable onchange field
       
   114         function is meant to allow customization 
       
   115         """
       
   116         return var.getonchange()
       
   117 
    98     def CTNGenerate_C(self, buildpath, locations):
   118     def CTNGenerate_C(self, buildpath, locations):
    99         # location string for that CTN
   119         # location string for that CTN
   100         location_str = "_".join(map(str, self.GetCurrentLocation()))
   120         location_str = "_".join(map(str, self.GetCurrentLocation()))
   101         configname = self.GetCTRoot().GetProjectConfigNames()[0]
   121         configname = self.GetCTRoot().GetProjectConfigNames()[0]
   102 
   122 
   103         def _onchangecode(var):
   123         def _onchangecode(var):
   104             return '"' + var.getonchange() + \
   124             result = []
   105                 "('" + var.getname() + "')\"" \
   125             for onchangecall in self.GetVarOnChangeContent(var).split(','):
   106                 if var.getonchange() else '""'
   126                 onchangecall = onchangecall.strip()
       
   127                 if onchangecall:
       
   128                     result.append(onchangecall + "('" + var.getname() + "')")
       
   129             return result
       
   130 
   107 
   131 
   108         def _onchange(var):
   132         def _onchange(var):
   109             return repr(var.getonchange()) \
   133             content = self.GetVarOnChangeContent(var)
   110                 if var.getonchange() else None
   134             return repr(content) if content else None
   111 
   135 
   112         pyextname = self.CTNName()
   136         pyextname = self.CTNName()
   113         varinfos = map(
   137         varinfos = map(
   114             lambda variable: {
   138             lambda variable: {
   115                 "name": variable.getname(),
   139                 "name": variable.getname(),
   122                 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()),
   146                 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()),
   123                 "initial": repr(variable.getinitial()),
   147                 "initial": repr(variable.getinitial()),
   124                 "pyextname": pyextname
   148                 "pyextname": pyextname
   125             },
   149             },
   126             self.CodeFile.variables.variable)
   150             self.CodeFile.variables.variable)
       
   151 
       
   152         onchange_var_count = len([None for varinfo in varinfos if varinfo["onchange"]])
       
   153 
   127         # python side PLC global variables access stub
   154         # python side PLC global variables access stub
   128         globalstubs = "\n".join([
   155         globalstubs = "\n".join([
   129             """\
   156             """\
   130 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
   157 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
   131     TypeTranslator["%(IECtype)s"]
   158     TypeTranslator["%(IECtype)s"]
   140     "%(IECtype)s",
   167     "%(IECtype)s",
   141     %(initial)s,
   168     %(initial)s,
   142     %(desc)s,
   169     %(desc)s,
   143     %(onchange)s,
   170     %(onchange)s,
   144     %(opts)s))
   171     %(opts)s))
   145 """ % varinfo for varinfo in varinfos])
   172 """ % varinfo + ("""
   146 
   173 _PyOnChangeCount_%(name)s = ctypes.c_uint.in_dll(PLCBinary,"__%(name)s_onchange_count")
       
   174 _PyOnChangeFirst_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_firstval")
       
   175 _PyOnChangeLast_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_lastval")
       
   176 """ % varinfo if varinfo["onchange"] else "") for varinfo in varinfos])
       
   177 
       
   178         on_change_func_body = "\n".join(["""
       
   179     if _PyOnChangeCount_%(name)s.value > 0:
       
   180         # %(name)s
       
   181         try:""" % varinfo + """
       
   182             """ + """
       
   183             """.join(varinfo['onchangecode'])+"""
       
   184         except Exception as e:
       
   185             errors.append("%(name)s: "+str(e))
       
   186 """ % varinfo for varinfo in varinfos if varinfo["onchange"]])
   147         # Runtime calls (start, stop, init, and cleanup)
   187         # Runtime calls (start, stop, init, and cleanup)
   148         rtcalls = ""
   188         rtcalls = ""
   149         for section in self.SECTIONS_NAMES:
   189         for section in self.SECTIONS_NAMES:
   150             if section != "globals":
   190             if section != "globals":
   151                 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
   191                 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
   161         loc_dict = {
   201         loc_dict = {
   162             "pyextname": pyextname,
   202             "pyextname": pyextname,
   163             "globalstubs": globalstubs,
   203             "globalstubs": globalstubs,
   164             "globalsection": globalsection,
   204             "globalsection": globalsection,
   165             "rtcalls": rtcalls,
   205             "rtcalls": rtcalls,
       
   206             "location_str": location_str,
       
   207             "on_change_func_body":on_change_func_body,
       
   208             "onchange_var_count": onchange_var_count
   166         }
   209         }
   167 
   210 
   168         PyFileContent = """\
   211         PyFileContent = """\
   169 #!/usr/bin/env python
   212 #!/usr/bin/env python
   170 # -*- coding: utf-8 -*-
   213 # -*- coding: utf-8 -*-
   172 ##
   215 ##
   173 
   216 
   174 ## Code for PLC global variable access
   217 ## Code for PLC global variable access
   175 from runtime.typemapping import TypeTranslator
   218 from runtime.typemapping import TypeTranslator
   176 import ctypes
   219 import ctypes
       
   220 
       
   221 _PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s
       
   222 _PySafeGetChanges_%(pyextname)s.restype = None
       
   223 _PySafeGetChanges_%(pyextname)s.argtypes = None
       
   224 
   177 _%(pyextname)sGlobalsDesc = []
   225 _%(pyextname)sGlobalsDesc = []
   178 __ext_name__ = "%(pyextname)s"
   226 __ext_name__ = "%(pyextname)s"
   179 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
   227 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
   180 %(globalstubs)s
   228 %(globalstubs)s
   181 
   229 
   182 ## User code in "global" scope
   230 ## User code in "global" scope
   183 %(globalsection)s
   231 %(globalsection)s
   184 
   232 
   185 ## Beremiz python runtime calls
   233 ## Beremiz python runtime calls
   186 %(rtcalls)s
   234 %(rtcalls)s
       
   235 
       
   236 def On_%(pyextname)s_Change():
       
   237     _PySafeGetChanges_%(pyextname)s()
       
   238     errors = []
       
   239 %(on_change_func_body)s
       
   240     if len(errors)>0 :
       
   241         raise Exception("Exception in %(pyextname)s OnChange call:\\\\n" + "\\\\n".join(errors))
   187 
   242 
   188 del __ext_name__
   243 del __ext_name__
   189 
   244 
   190 """ % loc_dict
   245 """ % loc_dict
   191 
   246 
   218 }
   273 }
   219 
   274 
   220 """
   275 """
   221 
   276 
   222         vardeconchangefmt = """\
   277         vardeconchangefmt = """\
   223 PYTHON_POLL* __%(name)s_notifier;
   278 unsigned int __%(name)s_rbuffer_written = 0;
       
   279 IEC_%(IECtype)s __%(name)s_rbuffer_firstval;
       
   280 IEC_%(IECtype)s __%(name)s_rbuffer_lastval;
       
   281 unsigned int __%(name)s_onchange_count = 0;
       
   282 IEC_%(IECtype)s __%(name)s_onchange_firstval;
       
   283 IEC_%(IECtype)s __%(name)s_onchange_lastval;
   224 """
   284 """
   225 
   285 
   226         varretfmt = """\
   286         varretfmt = """\
   227     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
   287     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
   228         if(__%(name)s_wbuffer_written == 1){
   288         if(__%(name)s_wbuffer_written == 1){
   241 
   301 
   242         varpubonchangefmt = """\
   302         varpubonchangefmt = """\
   243     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   303     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   244         IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
   304         IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
   245         if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){
   305         if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){
       
   306             if(__%(name)s_rbuffer_written == 0);
       
   307                 __%(name)s_rbuffer_firstval = __%(name)s_rbuffer;
       
   308             __%(name)s_rbuffer_lastval = tmp;
   246             __%(name)s_rbuffer = tmp;
   309             __%(name)s_rbuffer = tmp;
   247             PYTHON_POLL_body__(__%(name)s_notifier);
   310             /* count one more change */
       
   311             __%(name)s_rbuffer_written += 1;
       
   312             some_change_found = 1;
   248         }
   313         }
   249         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   314         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   250     }
   315     }
   251 """
   316 """
   252         varinitonchangefmt = """\
   317 
   253     __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE();
   318         varcollectchangefmt = """\
   254     __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
   319     while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1));
   255     __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s));
   320     __%(name)s_onchange_count = __%(name)s_rbuffer_written;
       
   321     __%(name)s_onchange_firstval = __%(name)s_rbuffer_firstval;
       
   322     __%(name)s_onchange_lastval = __%(name)s_rbuffer_lastval;
       
   323     /* mark variable as unchanged */
       
   324     __%(name)s_rbuffer_written = 0;
       
   325     AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
       
   326 
   256 """
   327 """
   257         vardec = "\n".join([(vardecfmt + vardeconchangefmt
   328         vardec = "\n".join([(vardecfmt + vardeconchangefmt
   258                              if varinfo["onchange"] else vardecfmt) % varinfo
   329                              if varinfo["onchange"] else vardecfmt) % varinfo
   259                             for varinfo in varinfos])
   330                             for varinfo in varinfos])
   260         varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
   331         varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
   261         varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
   332         varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
   262                              varpubfmt) % varinfo
   333                              varpubfmt) % varinfo
   263                             for varinfo in varinfos])
   334                             for varinfo in varinfos])
   264         varinit = "\n".join([varinitonchangefmt %
   335         varcollectchange = "\n".join([varcollectchangefmt % varinfo
   265                              dict(onchangelen=len(varinfo["onchangecode"]), **varinfo)
       
   266                              for varinfo in varinfos if varinfo["onchange"]])
   336                              for varinfo in varinfos if varinfo["onchange"]])
       
   337 
       
   338         pysafe_pypoll_code = "On_"+pyextname+"_Change()"
   267 
   339 
   268         loc_dict = {
   340         loc_dict = {
   269             "vardec": vardec,
   341             "vardec": vardec,
   270             "varinit": varinit,
       
   271             "varret": varret,
   342             "varret": varret,
   272             "varpub": varpub,
   343             "varpub": varpub,
   273             "location_str": location_str,
   344             "location_str": location_str,
       
   345             "pysafe_pypoll_code": '"'+pysafe_pypoll_code+'"',
       
   346             "pysafe_pypoll_code_len": len(pysafe_pypoll_code),
       
   347             "varcollectchange": varcollectchange,
       
   348             "onchange_var_count": onchange_var_count
   274         }
   349         }
   275 
   350 
   276         # TODO : use config name obtained from model instead of default
   351         # TODO : use config name obtained from model instead of default
   277         # "config.h". User cannot change config name, but project imported
   352         # "config.h". User cannot change config name, but project imported
   278         # or created in older beremiz vesion could use different name.
   353         # or created in older beremiz vesion could use different name.
   284 #include "iec_types_all.h"
   359 #include "iec_types_all.h"
   285 #include "POUS.h"
   360 #include "POUS.h"
   286 #include "config.h"
   361 #include "config.h"
   287 #include "beremiz.h"
   362 #include "beremiz.h"
   288 
   363 
       
   364 PYTHON_POLL* __%(location_str)s_notifier;
       
   365 
   289 /* User variables reference */
   366 /* User variables reference */
   290 %(vardec)s
   367 %(vardec)s
   291 
   368 
   292 /* Beremiz confnode functions */
   369 /* Beremiz confnode functions */
   293 int __init_%(location_str)s(int argc,char **argv){
   370 int __init_%(location_str)s(int argc,char **argv){
   294 %(varinit)s
   371     __%(location_str)s_notifier = __GET_GLOBAL_ON_%(location_str)s_CHANGE();
       
   372     __SET_VAR(__%(location_str)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
       
   373     __SET_VAR(__%(location_str)s_notifier->,CODE,,__STRING_LITERAL(%(pysafe_pypoll_code_len)d,%(pysafe_pypoll_code)s));
       
   374 
   295     return 0;
   375     return 0;
   296 }
   376 }
   297 
   377 
   298 void __cleanup_%(location_str)s(void){
   378 void __cleanup_%(location_str)s(void){
   299 }
   379 }
   300 
   380 
   301 void __retrieve_%(location_str)s(void){
   381 void __retrieve_%(location_str)s(void){
   302 %(varret)s
   382 %(varret)s
   303 }
   383 }
   304 
   384 
       
   385 static int passing_changes_to_python = 0;
   305 void __publish_%(location_str)s(void){
   386 void __publish_%(location_str)s(void){
       
   387     int some_change_found = 0;
   306 %(varpub)s
   388 %(varpub)s
   307 }
   389     passing_changes_to_python |= some_change_found;
       
   390     // call python part if there was at least a change
       
   391     if(passing_changes_to_python){
       
   392         PYTHON_POLL_body__(__%(location_str)s_notifier);
       
   393         passing_changes_to_python &= !(__GET_VAR(__%(location_str)s_notifier->ACK,));
       
   394     }
       
   395 }
       
   396 
       
   397 void* PySafeGetChanges_%(location_str)s(void){
       
   398 %(varcollectchange)s
       
   399 }
       
   400 
   308 """ % loc_dict
   401 """ % loc_dict
   309 
   402 
   310         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
   403         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
   311         pycfile = open(Gen_PyCfile_path, 'w')
   404         pycfile = open(Gen_PyCfile_path, 'w')
   312         pycfile.write(PyCFileContent)
   405         pycfile.write(PyCFileContent)