plugins/python/python.py
changeset 366 cd90e4c10261
child 367 a76ee5307bb7
equal deleted inserted replaced
365:a7f58414dea0 366:cd90e4c10261
       
     1 import wx
       
     2 import os
       
     3 import modules
       
     4 from plugger import PlugTemplate, opjimg
       
     5 from PythonEditor import PythonEditorFrame
       
     6 
       
     7 from xml.dom import minidom
       
     8 from xmlclass import *
       
     9 import cPickle
       
    10 
       
    11 PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "python_xsd.xsd")) 
       
    12 
       
    13 #-------------------------------------------------------------------------------
       
    14 #                         Undo Buffer for PythonCode
       
    15 #-------------------------------------------------------------------------------
       
    16 
       
    17 # Length of the buffer
       
    18 UNDO_BUFFER_LENGTH = 20
       
    19 
       
    20 """
       
    21 Class implementing a buffer of changes made on the current editing model
       
    22 """
       
    23 class UndoBuffer:
       
    24 
       
    25     # Constructor initialising buffer
       
    26     def __init__(self, currentstate, issaved = False):
       
    27         self.Buffer = []
       
    28         self.CurrentIndex = -1
       
    29         self.MinIndex = -1
       
    30         self.MaxIndex = -1
       
    31         # if current state is defined
       
    32         if currentstate:
       
    33             self.CurrentIndex = 0
       
    34             self.MinIndex = 0
       
    35             self.MaxIndex = 0
       
    36         # Initialising buffer with currentstate at the first place
       
    37         for i in xrange(UNDO_BUFFER_LENGTH):
       
    38             if i == 0:
       
    39                 self.Buffer.append(currentstate)
       
    40             else:
       
    41                 self.Buffer.append(None)
       
    42         # Initialising index of state saved
       
    43         if issaved:
       
    44             self.LastSave = 0
       
    45         else:
       
    46             self.LastSave = -1
       
    47     
       
    48     # Add a new state in buffer
       
    49     def Buffering(self, currentstate):
       
    50         self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
       
    51         self.Buffer[self.CurrentIndex] = currentstate
       
    52         # Actualising buffer limits
       
    53         self.MaxIndex = self.CurrentIndex
       
    54         if self.MinIndex == self.CurrentIndex:
       
    55             # If the removed state was the state saved, there is no state saved in the buffer
       
    56             if self.LastSave == self.MinIndex:
       
    57                 self.LastSave = -1
       
    58             self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH
       
    59         self.MinIndex = max(self.MinIndex, 0)
       
    60     
       
    61     # Return current state of buffer
       
    62     def Current(self):
       
    63         return self.Buffer[self.CurrentIndex]
       
    64     
       
    65     # Change current state to previous in buffer and return new current state
       
    66     def Previous(self):
       
    67         if self.CurrentIndex != self.MinIndex:
       
    68             self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH
       
    69             return self.Buffer[self.CurrentIndex]
       
    70         return None
       
    71     
       
    72     # Change current state to next in buffer and return new current state
       
    73     def Next(self):
       
    74         if self.CurrentIndex != self.MaxIndex:
       
    75             self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
       
    76             return self.Buffer[self.CurrentIndex]
       
    77         return None
       
    78     
       
    79     # Return True if current state is the first in buffer
       
    80     def IsFirst(self):
       
    81         return self.CurrentIndex == self.MinIndex
       
    82     
       
    83     # Return True if current state is the last in buffer
       
    84     def IsLast(self):
       
    85         return self.CurrentIndex == self.MaxIndex
       
    86 
       
    87     # Note that current state is saved
       
    88     def CurrentSaved(self):
       
    89         self.LastSave = self.CurrentIndex
       
    90         
       
    91     # Return True if current state is saved
       
    92     def IsCurrentSaved(self):
       
    93         return self.LastSave == self.CurrentIndex
       
    94 
       
    95 class PythonCodeTemplate:
       
    96     
       
    97     def __init__(self):
       
    98         
       
    99         self.PluginMethods.insert(0, 
       
   100                 {"bitmap" : opjimg("editPYTHONcode"),
       
   101                  "name" : _("Edit Python File"), 
       
   102                  "tooltip" : _("Edit Python File"),
       
   103                  "method" : "_OpenView"},
       
   104         )
       
   105 
       
   106         filepath = self.PythonFileName()
       
   107         
       
   108         self.Buffering = False
       
   109         self.PythonCode = PythonClasses["Python"]()
       
   110         self.PythonBuffer = UndoBuffer(self.Copy(self.PythonCode), False)
       
   111         if os.path.isfile(filepath):
       
   112             xmlfile = open(filepath, 'r')
       
   113             tree = minidom.parse(xmlfile)
       
   114             xmlfile.close()
       
   115             
       
   116             for child in tree.childNodes:
       
   117                 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python":
       
   118                     self.PythonCode.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
       
   119                     self.PythonBuffer = UndoBuffer(self.Copy(self.PythonCode), True)
       
   120         else:
       
   121             self.OnPlugSave()
       
   122 
       
   123     def PluginPath(self):
       
   124         return os.path.join(self.PlugParent.PluginPath(), "modules", self.PlugType)
       
   125 
       
   126     def PythonFileName(self):
       
   127         return os.path.join(self.PlugPath(), "python.xml")
       
   128 
       
   129     def GetFilename(self):
       
   130         if self.PythonBuffer.IsCurrentSaved():
       
   131             return "python"
       
   132         else:
       
   133             return "~python~"
       
   134 
       
   135     def SetPythonCode(self, text):
       
   136         self.PythonCode.settext(text)
       
   137         
       
   138     def GetPythonCode(self):
       
   139         return self.PythonCode.gettext()
       
   140     
       
   141     _View = None
       
   142     def _OpenView(self):
       
   143         if not self._View:
       
   144             def _onclose():
       
   145                 self._View = None
       
   146             def _onsave():
       
   147                 self.GetPlugRoot().SaveProject()
       
   148             self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self)
       
   149             self._View._onclose = _onclose
       
   150             self._View._onsave = _onsave
       
   151             self._View.Show()
       
   152     
       
   153     def OnPlugSave(self):
       
   154         filepath = self.PythonFileName()
       
   155         
       
   156         text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
       
   157         extras = {"xmlns":"http://www.w3.org/2001/XMLSchema",
       
   158                   "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
       
   159                   "xsi:schemaLocation" : "python_xsd.xsd"}
       
   160         text += self.PythonCode.generateXMLText("Python", 0, extras)
       
   161 
       
   162         xmlfile = open(filepath,"w")
       
   163         xmlfile.write(text)
       
   164         xmlfile.close()
       
   165         
       
   166         self.PythonBuffer.CurrentSaved()
       
   167         return True
       
   168         
       
   169 #-------------------------------------------------------------------------------
       
   170 #                      Current Buffering Management Functions
       
   171 #-------------------------------------------------------------------------------
       
   172 
       
   173     """
       
   174     Return a copy of the project
       
   175     """
       
   176     def Copy(self, model):
       
   177         return cPickle.loads(cPickle.dumps(model))
       
   178 
       
   179     def BufferPython(self):
       
   180         self.PythonBuffer.Buffering(self.Copy(self.PythonCode))
       
   181     
       
   182     def StartBuffering(self):
       
   183         self.PythonBuffer.Buffering(self.PythonCode)
       
   184         self.Buffering = True
       
   185         
       
   186     def EndBuffering(self):
       
   187         if self.Buffering:
       
   188             self.PythonCode = self.Copy(self.PythonCode)
       
   189             self.Buffering = False
       
   190     
       
   191     def PythonCodeIsSaved(self):
       
   192         if self.PythonBuffer:
       
   193             return self.PythonBuffer.IsCurrentSaved()
       
   194         else:
       
   195             return True
       
   196 
       
   197     def LoadPrevious(self):
       
   198         self.PythonCode = self.Copy(self.PythonBuffer.Previous())
       
   199     
       
   200     def LoadNext(self):
       
   201         self.PythonCode = self.Copy(self.PythonBuffer.Next())
       
   202     
       
   203     def GetBufferState(self):
       
   204         first = self.PythonBuffer.IsFirst()
       
   205         last = self.PythonBuffer.IsLast()
       
   206         return not first, not last
       
   207 
       
   208 def _GetClassFunction(name):
       
   209     def GetRootClass():
       
   210         __import__("plugins.python.modules." + name)
       
   211         return getattr(modules, name).RootClass
       
   212     return GetRootClass
       
   213 
       
   214 class RootClass(PythonCodeTemplate):
       
   215 
       
   216     # For root object, available Childs Types are modules of the modules packages.
       
   217     PlugChildsTypes = [(name, _GetClassFunction(name), help) for name, help in zip(modules.__all__,modules.helps)]
       
   218     
       
   219     def PluginPath(self):
       
   220         return os.path.join(self.PlugParent.PluginPath(), self.PlugType)
       
   221     
       
   222     def PlugGenerate_C(self, buildpath, locations):
       
   223         """
       
   224         Generate C code
       
   225         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   226         @param locations: List of complete variables locations \
       
   227             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   228             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   229             "DIR" : direction "Q","I" or "M"
       
   230             "SIZE" : size "X", "B", "W", "D", "L"
       
   231             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   232             }, ...]
       
   233         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   234         """
       
   235         current_location = self.GetCurrentLocation()
       
   236         # define a unique name for the generated C file
       
   237         location_str = "_".join(map(lambda x:str(x), current_location))
       
   238         
       
   239         plugin_root = self.GetPlugRoot()
       
   240         plugin_root.GetIECProgramsAndVariables()
       
   241         
       
   242         plc_python_filepath = os.path.join(os.path.split(__file__)[0], "plc_python.c")
       
   243         plc_python_file = open(plc_python_filepath, 'r')
       
   244         plc_python_code = plc_python_file.read()
       
   245         plc_python_file.close()
       
   246         python_eval_fb_list = []
       
   247         for v in plugin_root._VariablesList :
       
   248             if v["vartype"] == "FB" and v["type"] in ["PYTHON_EVAL","PYTHON_POLL"]:
       
   249                 python_eval_fb_list.append(v)
       
   250         python_eval_fb_count = max(1, len(python_eval_fb_list))
       
   251         
       
   252         # prepare python code
       
   253         plc_python_code = plc_python_code % {
       
   254            "python_eval_fb_count": python_eval_fb_count,
       
   255            "location": location_str}
       
   256         
       
   257         Gen_Pythonfile_path = os.path.join(buildpath, "python_%s.c"%location_str)
       
   258         pythonfile = open(Gen_Pythonfile_path,'w')
       
   259         pythonfile.write(plc_python_code)
       
   260         pythonfile.close()
       
   261         
       
   262         runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str)
       
   263         runtimefile = open(runtimefile_path, 'w')
       
   264         runtimefile.write(self.GetPythonCode())
       
   265         runtimefile.write("""
       
   266 def _runtime_%(location)s_begin():
       
   267     print "runtime_begin"
       
   268 
       
   269 def _runtime_%(location)s_cleanup():
       
   270     print "runtime_cleanup"
       
   271 
       
   272 """ % {"location": location_str})
       
   273         runtimefile.close()
       
   274         
       
   275         if wx.Platform == '__WXMSW__':
       
   276             matiec_flags = " -I../../matiec/lib"
       
   277         else:
       
   278             matiec_flags = " -I../matiec/lib"
       
   279         
       
   280         return [(Gen_Pythonfile_path, matiec_flags)], "", True, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))