py_ext/PythonFileCTNMixin.py
branch1.1 Korean release
changeset 1280 72a826dfcfbb
parent 1160 e9e9d3193894
child 1315 ff14a66bbd12
equal deleted inserted replaced
977:c8e008b8cefe 1280:72a826dfcfbb
     1 import os
     1 import os
     2 from PLCControler import UndoBuffer
     2 from PLCControler import UndoBuffer
     3 from PythonEditor import PythonEditor
     3 from PythonEditor import PythonEditor
     4 
     4 
     5 from xml.dom import minidom
     5 from xml.dom import minidom
     6 from xmlclass import *
     6 from xmlclass import GenerateClassesFromXSD
     7 import cPickle
     7 import cPickle
     8 
     8 
       
     9 from CodeFileTreeNode import CodeFile
       
    10 
     9 PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) 
    11 PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) 
    10 
    12 
    11 class PythonFileCTNMixin:
    13 class PythonFileCTNMixin(CodeFile):
    12     
    14     
       
    15     CODEFILE_NAME = "PyFile"
       
    16     SECTIONS_NAMES = [
       
    17         "globals",
       
    18         "init",
       
    19         "cleanup",
       
    20         "start",
       
    21         "stop"]
    13     EditorType = PythonEditor
    22     EditorType = PythonEditor
    14     
    23     
    15     def __init__(self):
    24     def __init__(self):
       
    25         CodeFile.__init__(self)
    16         
    26         
    17         filepath = self.PythonFileName()
    27         filepath = self.PythonFileName()
    18         
    28         
    19         self.PythonCode = PythonClasses["Python"]()
    29         python_code = PythonClasses["Python"]()
    20         if os.path.isfile(filepath):
    30         if os.path.isfile(filepath):
    21             xmlfile = open(filepath, 'r')
    31             xmlfile = open(filepath, 'r')
    22             tree = minidom.parse(xmlfile)
    32             tree = minidom.parse(xmlfile)
    23             xmlfile.close()
    33             xmlfile.close()
    24             
    34             
    25             for child in tree.childNodes:
    35             for child in tree.childNodes:
    26                 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python":
    36                 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python":
    27                     self.PythonCode.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
    37                     python_code.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
    28                     self.CreatePythonBuffer(True)
    38                     self.CodeFile.globals.settext(python_code.gettext())
    29         else:
    39                     os.remove(filepath)
    30             self.CreatePythonBuffer(False)
    40                     self.CreateCodeFileBuffer(False)
    31             self.OnCTNSave()
    41                     self.OnCTNSave()
    32 
    42     
       
    43     def CodeFileName(self):
       
    44         return os.path.join(self.CTNPath(), "pyfile.xml")
       
    45     
    33     def PythonFileName(self):
    46     def PythonFileName(self):
    34         return os.path.join(self.CTNPath(), "py_ext.xml")
    47         return os.path.join(self.CTNPath(), "py_ext.xml")
    35 
    48 
    36     def GetFilename(self):
    49     PreSectionsTexts = {}
    37         if self.PythonBuffer.IsCurrentSaved():
    50     PostSectionsTexts = {}
    38             return "py_ext"
    51     def GetSection(self,section):
       
    52         return self.PreSectionsTexts.get(section,"") + "\n" + \
       
    53                getattr(self.CodeFile, section).gettext() + "\n" + \
       
    54                self.PostSectionsTexts.get(section,"")
       
    55         
       
    56 
       
    57     def CTNGenerate_C(self, buildpath, locations):
       
    58         # location string for that CTN 
       
    59         location_str = "_".join(map(lambda x:str(x), 
       
    60                                 self.GetCurrentLocation()))
       
    61         configname = self.GetCTRoot().GetProjectConfigNames()[0]
       
    62         
       
    63         
       
    64         # python side PLC global variables access stub
       
    65         globalstubs = "\n".join(["""\
       
    66 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
       
    67     TypeTranslator["%(IECtype)s"]
       
    68 _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s
       
    69 _PySafeGetPLCGlob_%(name)s.restype = None
       
    70 _PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] 
       
    71 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
       
    72 _PySafeSetPLCGlob_%(name)s.restype = None
       
    73 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
       
    74 """ % { "name": variable.getname(),
       
    75         "configname": configname.upper(),
       
    76         "uppername": variable.getname().upper(),
       
    77         "IECtype": variable.gettype()}
       
    78             for variable in self.CodeFile.variables.variable])
       
    79 
       
    80         # Runtime calls (start, stop, init, and cleanup)
       
    81         rtcalls = ""
       
    82         for section in self.SECTIONS_NAMES:
       
    83             if section != "globals":
       
    84                 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
       
    85                 sectiontext = self.GetSection(section).strip()
       
    86                 if sectiontext:
       
    87                     rtcalls += '    ' + \
       
    88                         sectiontext.replace('\n', '\n    ')+"\n\n"
       
    89                 else:
       
    90                     rtcalls += "    pass\n\n"
       
    91 
       
    92         globalsection = self.GetSection("globals")      
       
    93 
       
    94         PyFileContent = """\
       
    95 #!/usr/bin/env python
       
    96 # -*- coding: utf-8 -*-
       
    97 ## Code generated by Beremiz python mixin confnode
       
    98 ##        
       
    99         
       
   100 ## Code for PLC global variable access
       
   101 from targets.typemapping import TypeTranslator
       
   102 import ctypes 
       
   103 %(globalstubs)s
       
   104         
       
   105 ## User code in "global" scope
       
   106 %(globalsection)s
       
   107 
       
   108 ## Beremiz python runtime calls
       
   109 %(rtcalls)s
       
   110 
       
   111 """ % locals()
       
   112 
       
   113         # write generated content to python file
       
   114         runtimefile_path = os.path.join(buildpath, 
       
   115             "runtime_%s.py"%location_str)
       
   116         runtimefile = open(runtimefile_path, 'w')
       
   117         runtimefile.write(PyFileContent.encode('utf-8'))
       
   118         runtimefile.close()
       
   119 
       
   120         # C code for safe global variables access
       
   121         
       
   122         vardecfmt = """\
       
   123 extern  __IEC_%(IECtype)s_t %(configname)s__%(uppername)s;
       
   124 IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s;
       
   125 IEC_%(IECtype)s __%(name)s_wbuffer;
       
   126 long __%(name)s_rlock = 0;
       
   127 long __%(name)s_wlock = 0;
       
   128 int __%(name)s_wbuffer_written = 0;
       
   129 void __SafeGetPLCGlob_%(name)s(IEC_%(IECtype)s *pvalue){
       
   130     while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1));
       
   131     *pvalue = __%(name)s_rbuffer;
       
   132     AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
       
   133 }
       
   134 void __SafeSetPLCGlob_%(name)s(IEC_%(IECtype)s *value){
       
   135     while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1));
       
   136     __%(name)s_wbuffer = *value;
       
   137     __%(name)s_wbuffer_written = 1;
       
   138     AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
       
   139 }
       
   140 
       
   141 """
       
   142         varretfmt = """\
       
   143     if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
       
   144         if(__%(name)s_wbuffer_written == 1){
       
   145             %(configname)s__%(uppername)s.value = __%(name)s_wbuffer;
       
   146             __%(name)s_wbuffer_written = 0;
       
   147         }
       
   148         AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
       
   149     }
       
   150 """ 
       
   151         varpubfmt = """\
       
   152     if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
       
   153         __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
       
   154         AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
       
   155     }
       
   156 """ 
       
   157 
       
   158         var_str = map("\n".join, zip(*[
       
   159             map(lambda f : f % varinfo,
       
   160                 (vardecfmt, varretfmt, varpubfmt))
       
   161                 for varinfo in map(lambda variable : {
       
   162                     "name": variable.getname(),
       
   163                     "configname": configname.upper(),
       
   164                     "uppername": variable.getname().upper(),
       
   165                     "IECtype": variable.gettype()},
       
   166                     self.CodeFile.variables.variable)]))
       
   167         if len(var_str) > 0:
       
   168             vardec, varret, varpub = var_str
    39         else:
   169         else:
    40             return "~py_ext~"
   170             vardec = varret = varpub = ""
    41 
   171         
    42     def SetPythonCode(self, text):
   172         PyCFileContent = """\
    43         self.PythonCode.settext(text)
   173 /* 
    44         
   174  * Code generated by Beremiz py_ext confnode 
    45     def GetPythonCode(self):
   175  * for safe global variables access
    46         return self.PythonCode.gettext()
   176  */
    47     
   177 #include "iec_types_all.h"
    48     def CTNTestModified(self):
   178 #include "beremiz.h"
    49         return self.ChangesToSave or not self.PythonIsSaved()
   179 
    50     
   180 /* User variables reference */
    51     def OnCTNSave(self):
   181 %(vardec)s
    52         filepath = self.PythonFileName()
   182 
    53         
   183 /* Beremiz confnode functions */
    54         text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
   184 int __init_%(location_str)s(int argc,char **argv){
    55         extras = {"xmlns":"http://www.w3.org/2001/XMLSchema",
   185     return 0;
    56                   "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
   186 }
    57                   "xsi:schemaLocation" : "py_ext_xsd.xsd"}
   187 
    58         text += self.PythonCode.generateXMLText("Python", 0, extras)
   188 void __cleanup_%(location_str)s(void){
    59 
   189 }
    60         xmlfile = open(filepath,"w")
   190 
    61         xmlfile.write(text.encode("utf-8"))
   191 void __retrieve_%(location_str)s(void){
    62         xmlfile.close()
   192 %(varret)s
    63         
   193 }
    64         self.MarkPythonAsSaved()
   194 
    65         return True
   195 void __publish_%(location_str)s(void){
    66         
   196 %(varpub)s
    67 #-------------------------------------------------------------------------------
   197 }
    68 #                      Current Buffering Management Functions
   198 """ % locals()
    69 #-------------------------------------------------------------------------------
   199         
    70 
   200         Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str)
    71     """
   201         pycfile = open(Gen_PyCfile_path,'w')
    72     Return a copy of the project
   202         pycfile.write(PyCFileContent)
    73     """
   203         pycfile.close()
    74     def Copy(self, model):
   204         
    75         return cPickle.loads(cPickle.dumps(model))
   205         matiec_flags = '"-I%s"'%os.path.abspath(
    76 
   206             self.GetCTRoot().GetIECLibPath())
    77     def CreatePythonBuffer(self, saved):
   207         
    78         self.Buffering = False
   208         return ([(Gen_PyCfile_path, matiec_flags)],
    79         self.PythonBuffer = UndoBuffer(cPickle.dumps(self.PythonCode), saved)
   209                 "",
    80 
   210                 True,
    81     def BufferPython(self):
   211                 ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")))
    82         self.PythonBuffer.Buffering(cPickle.dumps(self.PythonCode))
   212 
    83     
       
    84     def StartBuffering(self):
       
    85         self.Buffering = True
       
    86         
       
    87     def EndBuffering(self):
       
    88         if self.Buffering:
       
    89             self.PythonBuffer.Buffering(cPickle.dumps(self.PythonCode))
       
    90             self.Buffering = False
       
    91     
       
    92     def MarkPythonAsSaved(self):
       
    93         self.EndBuffering()
       
    94         self.PythonBuffer.CurrentSaved()
       
    95     
       
    96     def PythonIsSaved(self):
       
    97         return self.PythonBuffer.IsCurrentSaved() and not self.Buffering
       
    98         
       
    99     def LoadPrevious(self):
       
   100         self.EndBuffering()
       
   101         self.PythonCode = cPickle.loads(self.PythonBuffer.Previous())
       
   102     
       
   103     def LoadNext(self):
       
   104         self.PythonCode = cPickle.loads(self.PythonBuffer.Next())
       
   105     
       
   106     def GetBufferState(self):
       
   107         first = self.PythonBuffer.IsFirst() and not self.Buffering
       
   108         last = self.PythonBuffer.IsLast()
       
   109         return not first, not last
       
   110