py_ext/PythonFileCTNMixin.py
changeset 1475 de4ee16f7c6c
parent 1474 28e9d479aa65
parent 1452 7a2b344de8cf
child 1503 3a238c0c5993
equal deleted inserted replaced
1474:28e9d479aa65 1475:de4ee16f7c6c
     5 
     5 
     6 from CodeFileTreeNode import CodeFile
     6 from CodeFileTreeNode import CodeFile
     7 from PythonEditor import PythonEditor
     7 from PythonEditor import PythonEditor
     8 
     8 
     9 class PythonFileCTNMixin(CodeFile):
     9 class PythonFileCTNMixin(CodeFile):
    10     
    10 
    11     CODEFILE_NAME = "PyFile"
    11     CODEFILE_NAME = "PyFile"
    12     SECTIONS_NAMES = [
    12     SECTIONS_NAMES = [
    13         "globals",
    13         "globals",
    14         "init",
    14         "init",
    15         "cleanup",
    15         "cleanup",
    16         "start",
    16         "start",
    17         "stop"]
    17         "stop"]
    18     EditorType = PythonEditor
    18     EditorType = PythonEditor
    19     
    19 
    20     def __init__(self):
    20     def __init__(self):
    21         CodeFile.__init__(self)
    21         CodeFile.__init__(self)
    22         
    22 
    23         filepath = self.PythonFileName()
    23         filepath = self.PythonFileName()
    24         
    24 
    25         if os.path.isfile(filepath):
    25         if os.path.isfile(filepath):
    26             PythonParser = GenerateParserFromXSD(
    26             PythonParser = GenerateParserFromXSD(
    27                 os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) 
    27                 os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd"))
    28             
    28 
    29             xmlfile = open(filepath, 'r')
    29             xmlfile = open(filepath, 'r')
    30             pythonfile_xml = xmlfile.read()
    30             pythonfile_xml = xmlfile.read()
    31             xmlfile.close()
    31             xmlfile.close()
    32             
    32 
    33             pythonfile_xml = pythonfile_xml.replace(
    33             pythonfile_xml = pythonfile_xml.replace(
    34                 'xmlns="http://www.w3.org/2001/XMLSchema"', 
    34                 'xmlns="http://www.w3.org/2001/XMLSchema"',
    35                 'xmlns:xhtml="http://www.w3.org/1999/xhtml"')
    35                 'xmlns:xhtml="http://www.w3.org/1999/xhtml"')
    36             for cre, repl in [
    36             for cre, repl in [
    37                 (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
    37                 (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
    38                 (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
    38                 (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
    39                 pythonfile_xml = cre.sub(repl, pythonfile_xml)
    39                 pythonfile_xml = cre.sub(repl, pythonfile_xml)
    40             
    40 
    41             try:
    41             try:
    42                 python_code, error = PythonParser.LoadXMLString(pythonfile_xml)
    42                 python_code, error = PythonParser.LoadXMLString(pythonfile_xml)
    43                 if error is None:    
    43                 if error is None:
    44                     self.CodeFile.globals.setanyText(python_code.getanyText())
    44                     self.CodeFile.globals.setanyText(python_code.getanyText())
    45                     os.remove(filepath)
    45                     os.remove(filepath)
    46                     self.CreateCodeFileBuffer(False)
    46                     self.CreateCodeFileBuffer(False)
    47                     self.OnCTNSave()
    47                     self.OnCTNSave()
    48             except Exception, exc:
    48             except Exception, exc:
    49                 error = unicode(exc)
    49                 error = unicode(exc)
    50             
    50 
    51             if error is not None:
    51             if error is not None:
    52                 self.GetCTRoot().logger.write_error(
    52                 self.GetCTRoot().logger.write_error(
    53                     _("Couldn't import old %s file.") % CTNName)
    53                     _("Couldn't import old %s file.") % self.CTNName())
    54     
    54 
    55     def CodeFileName(self):
    55     def CodeFileName(self):
    56         return os.path.join(self.CTNPath(), "pyfile.xml")
    56         return os.path.join(self.CTNPath(), "pyfile.xml")
    57     
    57 
    58     def PythonFileName(self):
    58     def PythonFileName(self):
    59         return os.path.join(self.CTNPath(), "py_ext.xml")
    59         return os.path.join(self.CTNPath(), "py_ext.xml")
    60 
    60 
    61     PreSectionsTexts = {}
    61     PreSectionsTexts = {}
    62     PostSectionsTexts = {}
    62     PostSectionsTexts = {}
    63     def GetSection(self,section):
    63     def GetSection(self,section):
    64         return self.PreSectionsTexts.get(section,"") + "\n" + \
    64         return self.PreSectionsTexts.get(section,"") + "\n" + \
    65                getattr(self.CodeFile, section).getanyText() + "\n" + \
    65                getattr(self.CodeFile, section).getanyText() + "\n" + \
    66                self.PostSectionsTexts.get(section,"")
    66                self.PostSectionsTexts.get(section,"")
    67         
       
    68 
    67 
    69     def CTNGenerate_C(self, buildpath, locations):
    68     def CTNGenerate_C(self, buildpath, locations):
    70         # location string for that CTN 
    69         # location string for that CTN
    71         location_str = "_".join(map(lambda x:str(x), 
    70         location_str = "_".join(map(lambda x:str(x),
    72                                 self.GetCurrentLocation()))
    71                                 self.GetCurrentLocation()))
    73         configname = self.GetCTRoot().GetProjectConfigNames()[0]
    72         configname = self.GetCTRoot().GetProjectConfigNames()[0]
    74         
    73 
    75         
    74         pyextname = self.CTNName()
       
    75         varinfos = map(lambda variable : {
       
    76                     "name": variable.getname(),
       
    77                     "desc" : repr(variable.getdesc()),
       
    78                     "onchangecode" : '"'+variable.getonchange()+\
       
    79                                          "('"+variable.getname()+"')\"" \
       
    80                                      if variable.getonchange() else '""',
       
    81                     "onchange" : repr(variable.getonchange()) \
       
    82                                  if variable.getonchange() else None,
       
    83                     "opts" : repr(variable.getopts()),
       
    84                     "configname" : configname.upper(),
       
    85                     "uppername" : variable.getname().upper(),
       
    86                     "IECtype" : variable.gettype(),
       
    87                     "pyextname" :pyextname},
       
    88                     self.CodeFile.variables.variable)
    76         # python side PLC global variables access stub
    89         # python side PLC global variables access stub
    77         globalstubs = "\n".join(["""\
    90         globalstubs = "\n".join(["""\
    78 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
    91 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
    79     TypeTranslator["%(IECtype)s"]
    92     TypeTranslator["%(IECtype)s"]
    80 _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s
    93 _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s
    81 _PySafeGetPLCGlob_%(name)s.restype = None
    94 _PySafeGetPLCGlob_%(name)s.restype = None
    82 _PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] 
    95 _PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
    83 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
    96 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
    84 _PySafeSetPLCGlob_%(name)s.restype = None
    97 _PySafeSetPLCGlob_%(name)s.restype = None
    85 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
    98 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
    86 """ % { "name": variable.getname(),
    99 _%(pyextname)sGlobalsDesc.append((
    87         "configname": configname.upper(),
   100     "%(name)s",
    88         "uppername": variable.getname().upper(),
   101     "%(IECtype)s",
    89         "IECtype": variable.gettype()}
   102     %(desc)s,
    90             for variable in self.CodeFile.variables.variable])
   103     %(onchange)s,
       
   104     %(opts)s))
       
   105 """ % varinfo
       
   106       for varinfo in varinfos])
    91 
   107 
    92         # Runtime calls (start, stop, init, and cleanup)
   108         # Runtime calls (start, stop, init, and cleanup)
    93         rtcalls = ""
   109         rtcalls = ""
    94         for section in self.SECTIONS_NAMES:
   110         for section in self.SECTIONS_NAMES:
    95             if section != "globals":
   111             if section != "globals":
    99                     rtcalls += '    ' + \
   115                     rtcalls += '    ' + \
   100                         sectiontext.replace('\n', '\n    ')+"\n\n"
   116                         sectiontext.replace('\n', '\n    ')+"\n\n"
   101                 else:
   117                 else:
   102                     rtcalls += "    pass\n\n"
   118                     rtcalls += "    pass\n\n"
   103 
   119 
   104         globalsection = self.GetSection("globals")      
   120         globalsection = self.GetSection("globals")
   105 
   121 
   106         PyFileContent = """\
   122         PyFileContent = """\
   107 #!/usr/bin/env python
   123 #!/usr/bin/env python
   108 # -*- coding: utf-8 -*-
   124 # -*- coding: utf-8 -*-
   109 ## Code generated by Beremiz python mixin confnode
   125 ## Code generated by Beremiz python mixin confnode
   110 ##        
   126 ##
   111         
   127 
   112 ## Code for PLC global variable access
   128 ## Code for PLC global variable access
   113 from targets.typemapping import TypeTranslator
   129 from targets.typemapping import TypeTranslator
   114 import ctypes 
   130 import ctypes
       
   131 _%(pyextname)sGlobalsDesc = []
       
   132 __ext_name__ = "%(pyextname)s"
       
   133 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
   115 %(globalstubs)s
   134 %(globalstubs)s
   116         
   135 
   117 ## User code in "global" scope
   136 ## User code in "global" scope
   118 %(globalsection)s
   137 %(globalsection)s
   119 
   138 
   120 ## Beremiz python runtime calls
   139 ## Beremiz python runtime calls
   121 %(rtcalls)s
   140 %(rtcalls)s
   122 
   141 
       
   142 del __ext_name__
       
   143 
   123 """ % locals()
   144 """ % locals()
   124 
   145 
   125         # write generated content to python file
   146         # write generated content to python file
   126         runtimefile_path = os.path.join(buildpath, 
   147         runtimefile_path = os.path.join(buildpath,
   127             "runtime_%s.py"%location_str)
   148             "runtime_%s.py"%location_str)
   128         runtimefile = open(runtimefile_path, 'w')
   149         runtimefile = open(runtimefile_path, 'w')
   129         runtimefile.write(PyFileContent.encode('utf-8'))
   150         runtimefile.write(PyFileContent.encode('utf-8'))
   130         runtimefile.close()
   151         runtimefile.close()
   131 
   152 
   132         # C code for safe global variables access
   153         # C code for safe global variables access
   133         
   154 
   134         vardecfmt = """\
   155         vardecfmt = """\
   135 extern  __IEC_%(IECtype)s_t %(configname)s__%(uppername)s;
   156 extern  __IEC_%(IECtype)s_t %(configname)s__%(uppername)s;
   136 IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s;
   157 IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s;
   137 IEC_%(IECtype)s __%(name)s_wbuffer;
   158 IEC_%(IECtype)s __%(name)s_wbuffer;
   138 long __%(name)s_rlock = 0;
   159 long __%(name)s_rlock = 0;
   149     __%(name)s_wbuffer_written = 1;
   170     __%(name)s_wbuffer_written = 1;
   150     AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
   171     AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
   151 }
   172 }
   152 
   173 
   153 """
   174 """
       
   175 
       
   176         vardeconchangefmt = """\
       
   177 PYTHON_POLL* __%(name)s_notifier;
       
   178 """
       
   179 
   154         varretfmt = """\
   180         varretfmt = """\
   155     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
   181     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
   156         if(__%(name)s_wbuffer_written == 1){
   182         if(__%(name)s_wbuffer_written == 1){
   157             %(configname)s__%(uppername)s.value = __%(name)s_wbuffer;
   183             %(configname)s__%(uppername)s.value = __%(name)s_wbuffer;
   158             __%(name)s_wbuffer_written = 0;
   184             __%(name)s_wbuffer_written = 0;
   159         }
   185         }
   160         AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
   186         AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
   161     }
   187     }
   162 """ 
   188 """
   163         varpubfmt = """\
   189         varpubfmt = """\
   164     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   190     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   165         __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
   191         __%(name)s_rbuffer = __GET_VAR(%(configname)s__%(uppername)s);
   166         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   192         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   167     }
   193     }
   168 """ 
   194 """
   169 
   195 
   170         var_str = map("\n".join, zip(*[
   196         varpubonchangefmt = """\
   171             map(lambda f : f % varinfo,
   197     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
   172                 (vardecfmt, varretfmt, varpubfmt))
   198         IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
   173                 for varinfo in map(lambda variable : {
   199         if(__%(name)s_rbuffer != tmp){
   174                     "name": variable.getname(),
   200             __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
   175                     "configname": configname.upper(),
   201             PYTHON_POLL_body__(__%(name)s_notifier);
   176                     "uppername": variable.getname().upper(),
   202         }
   177                     "IECtype": variable.gettype()},
   203         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
   178                     self.CodeFile.variables.variable)]))
   204     }
   179         if len(var_str) > 0:
   205 """
   180             vardec, varret, varpub = var_str
   206         varinitonchangefmt = """\
   181         else:
   207     __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE();
   182             vardec = varret = varpub = ""
   208     __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
   183         
   209     __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s));
       
   210 """
       
   211         vardec = "\n".join([(vardecfmt + vardeconchangefmt
       
   212                              if varinfo["onchange"] else vardecfmt)% varinfo
       
   213                             for varinfo in varinfos])
       
   214         varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
       
   215         varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
       
   216                              varpubfmt) % varinfo
       
   217                             for varinfo in varinfos])
       
   218         varinit = "\n".join([varinitonchangefmt % dict(
       
   219                                 onchangelen = len(varinfo["onchangecode"]),**varinfo)
       
   220                             for varinfo in varinfos if varinfo["onchange"]])
       
   221 
       
   222         # TODO : use config name obtained from model instead of default
       
   223         # "config.h". User cannot change config name, but project imported
       
   224         # or created in older beremiz vesion could use different name.
   184         PyCFileContent = """\
   225         PyCFileContent = """\
   185 /* 
   226 /*
   186  * Code generated by Beremiz py_ext confnode 
   227  * Code generated by Beremiz py_ext confnode
   187  * for safe global variables access
   228  * for safe global variables access
   188  */
   229  */
   189 #include "iec_types_all.h"
   230 #include "iec_types_all.h"
       
   231 #include "POUS.h"
       
   232 #include "config.h"
   190 #include "beremiz.h"
   233 #include "beremiz.h"
   191 
   234 
   192 /* User variables reference */
   235 /* User variables reference */
   193 %(vardec)s
   236 %(vardec)s
   194 
   237 
   195 /* Beremiz confnode functions */
   238 /* Beremiz confnode functions */
   196 int __init_%(location_str)s(int argc,char **argv){
   239 int __init_%(location_str)s(int argc,char **argv){
       
   240 %(varinit)s
   197     return 0;
   241     return 0;
   198 }
   242 }
   199 
   243 
   200 void __cleanup_%(location_str)s(void){
   244 void __cleanup_%(location_str)s(void){
   201 }
   245 }
   206 
   250 
   207 void __publish_%(location_str)s(void){
   251 void __publish_%(location_str)s(void){
   208 %(varpub)s
   252 %(varpub)s
   209 }
   253 }
   210 """ % locals()
   254 """ % locals()
   211         
   255 
   212         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str)
   256         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str)
   213         pycfile = open(Gen_PyCfile_path,'w')
   257         pycfile = open(Gen_PyCfile_path,'w')
   214         pycfile.write(PyCFileContent)
   258         pycfile.write(PyCFileContent)
   215         pycfile.close()
   259         pycfile.close()
   216         
   260 
   217         matiec_flags = '"-l -p -I%s"'%os.path.abspath(
   261         matiec_flags = '"-l -p -I%s"'%os.path.abspath(
   218             self.GetCTRoot().GetIECLibPath())
   262             self.GetCTRoot().GetIECLibPath())
   219         
   263 
   220         return ([(Gen_PyCfile_path, matiec_flags)],
   264         return ([(Gen_PyCfile_path, matiec_flags)],
   221                 "",
   265                 "",
   222                 True,
   266                 True,
   223                 ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")))
   267                 ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")))
   224 
   268