laurent@366: import wx
laurent@366: import os
laurent@366: import modules
laurent@366: from plugger import PlugTemplate, opjimg
laurent@366: from PythonEditor import PythonEditorFrame
laurent@366: 
laurent@366: from xml.dom import minidom
laurent@366: from xmlclass import *
laurent@366: import cPickle
laurent@366: 
laurent@366: PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "python_xsd.xsd")) 
laurent@366: 
laurent@366: #-------------------------------------------------------------------------------
laurent@366: #                         Undo Buffer for PythonCode
laurent@366: #-------------------------------------------------------------------------------
laurent@366: 
laurent@366: # Length of the buffer
laurent@366: UNDO_BUFFER_LENGTH = 20
laurent@366: 
laurent@366: """
laurent@366: Class implementing a buffer of changes made on the current editing model
laurent@366: """
laurent@366: class UndoBuffer:
laurent@366: 
laurent@366:     # Constructor initialising buffer
laurent@366:     def __init__(self, currentstate, issaved = False):
laurent@366:         self.Buffer = []
laurent@366:         self.CurrentIndex = -1
laurent@366:         self.MinIndex = -1
laurent@366:         self.MaxIndex = -1
laurent@366:         # if current state is defined
laurent@366:         if currentstate:
laurent@366:             self.CurrentIndex = 0
laurent@366:             self.MinIndex = 0
laurent@366:             self.MaxIndex = 0
laurent@366:         # Initialising buffer with currentstate at the first place
laurent@366:         for i in xrange(UNDO_BUFFER_LENGTH):
laurent@366:             if i == 0:
laurent@366:                 self.Buffer.append(currentstate)
laurent@366:             else:
laurent@366:                 self.Buffer.append(None)
laurent@366:         # Initialising index of state saved
laurent@366:         if issaved:
laurent@366:             self.LastSave = 0
laurent@366:         else:
laurent@366:             self.LastSave = -1
laurent@366:     
laurent@366:     # Add a new state in buffer
laurent@366:     def Buffering(self, currentstate):
laurent@366:         self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
laurent@366:         self.Buffer[self.CurrentIndex] = currentstate
laurent@366:         # Actualising buffer limits
laurent@366:         self.MaxIndex = self.CurrentIndex
laurent@366:         if self.MinIndex == self.CurrentIndex:
laurent@366:             # If the removed state was the state saved, there is no state saved in the buffer
laurent@366:             if self.LastSave == self.MinIndex:
laurent@366:                 self.LastSave = -1
laurent@366:             self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH
laurent@366:         self.MinIndex = max(self.MinIndex, 0)
laurent@366:     
laurent@366:     # Return current state of buffer
laurent@366:     def Current(self):
laurent@366:         return self.Buffer[self.CurrentIndex]
laurent@366:     
laurent@366:     # Change current state to previous in buffer and return new current state
laurent@366:     def Previous(self):
laurent@366:         if self.CurrentIndex != self.MinIndex:
laurent@366:             self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH
laurent@366:             return self.Buffer[self.CurrentIndex]
laurent@366:         return None
laurent@366:     
laurent@366:     # Change current state to next in buffer and return new current state
laurent@366:     def Next(self):
laurent@366:         if self.CurrentIndex != self.MaxIndex:
laurent@366:             self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
laurent@366:             return self.Buffer[self.CurrentIndex]
laurent@366:         return None
laurent@366:     
laurent@366:     # Return True if current state is the first in buffer
laurent@366:     def IsFirst(self):
laurent@366:         return self.CurrentIndex == self.MinIndex
laurent@366:     
laurent@366:     # Return True if current state is the last in buffer
laurent@366:     def IsLast(self):
laurent@366:         return self.CurrentIndex == self.MaxIndex
laurent@366: 
laurent@366:     # Note that current state is saved
laurent@366:     def CurrentSaved(self):
laurent@366:         self.LastSave = self.CurrentIndex
laurent@366:         
laurent@366:     # Return True if current state is saved
laurent@366:     def IsCurrentSaved(self):
laurent@366:         return self.LastSave == self.CurrentIndex
laurent@366: 
laurent@366: class PythonCodeTemplate:
laurent@366:     
laurent@366:     def __init__(self):
laurent@366:         
laurent@366:         self.PluginMethods.insert(0, 
laurent@366:                 {"bitmap" : opjimg("editPYTHONcode"),
laurent@366:                  "name" : _("Edit Python File"), 
laurent@366:                  "tooltip" : _("Edit Python File"),
laurent@366:                  "method" : "_OpenView"},
laurent@366:         )
laurent@366: 
laurent@366:         filepath = self.PythonFileName()
laurent@366:         
laurent@366:         self.Buffering = False
laurent@366:         self.PythonCode = PythonClasses["Python"]()
laurent@366:         self.PythonBuffer = UndoBuffer(self.Copy(self.PythonCode), False)
laurent@366:         if os.path.isfile(filepath):
laurent@366:             xmlfile = open(filepath, 'r')
laurent@366:             tree = minidom.parse(xmlfile)
laurent@366:             xmlfile.close()
laurent@366:             
laurent@366:             for child in tree.childNodes:
laurent@366:                 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python":
laurent@366:                     self.PythonCode.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"])
laurent@366:                     self.PythonBuffer = UndoBuffer(self.Copy(self.PythonCode), True)
laurent@366:         else:
laurent@366:             self.OnPlugSave()
laurent@366: 
laurent@366:     def PluginPath(self):
laurent@366:         return os.path.join(self.PlugParent.PluginPath(), "modules", self.PlugType)
laurent@366: 
laurent@366:     def PythonFileName(self):
laurent@366:         return os.path.join(self.PlugPath(), "python.xml")
laurent@366: 
laurent@366:     def GetFilename(self):
laurent@366:         if self.PythonBuffer.IsCurrentSaved():
laurent@366:             return "python"
laurent@366:         else:
laurent@366:             return "~python~"
laurent@366: 
laurent@366:     def SetPythonCode(self, text):
laurent@366:         self.PythonCode.settext(text)
laurent@366:         
laurent@366:     def GetPythonCode(self):
laurent@366:         return self.PythonCode.gettext()
laurent@366:     
laurent@366:     _View = None
laurent@366:     def _OpenView(self):
laurent@366:         if not self._View:
laurent@366:             def _onclose():
laurent@366:                 self._View = None
laurent@366:             def _onsave():
laurent@366:                 self.GetPlugRoot().SaveProject()
laurent@366:             self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self)
laurent@366:             self._View._onclose = _onclose
laurent@366:             self._View._onsave = _onsave
laurent@366:             self._View.Show()
laurent@366:     
laurent@366:     def OnPlugSave(self):
laurent@366:         filepath = self.PythonFileName()
laurent@366:         
laurent@366:         text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
laurent@366:         extras = {"xmlns":"http://www.w3.org/2001/XMLSchema",
laurent@366:                   "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
laurent@366:                   "xsi:schemaLocation" : "python_xsd.xsd"}
laurent@366:         text += self.PythonCode.generateXMLText("Python", 0, extras)
laurent@366: 
laurent@366:         xmlfile = open(filepath,"w")
laurent@366:         xmlfile.write(text)
laurent@366:         xmlfile.close()
laurent@366:         
laurent@366:         self.PythonBuffer.CurrentSaved()
laurent@366:         return True
laurent@366:         
laurent@366: #-------------------------------------------------------------------------------
laurent@366: #                      Current Buffering Management Functions
laurent@366: #-------------------------------------------------------------------------------
laurent@366: 
laurent@366:     """
laurent@366:     Return a copy of the project
laurent@366:     """
laurent@366:     def Copy(self, model):
laurent@366:         return cPickle.loads(cPickle.dumps(model))
laurent@366: 
laurent@366:     def BufferPython(self):
laurent@366:         self.PythonBuffer.Buffering(self.Copy(self.PythonCode))
laurent@366:     
laurent@366:     def StartBuffering(self):
laurent@366:         self.PythonBuffer.Buffering(self.PythonCode)
laurent@366:         self.Buffering = True
laurent@366:         
laurent@366:     def EndBuffering(self):
laurent@366:         if self.Buffering:
laurent@366:             self.PythonCode = self.Copy(self.PythonCode)
laurent@366:             self.Buffering = False
laurent@366:     
laurent@366:     def PythonCodeIsSaved(self):
laurent@366:         if self.PythonBuffer:
laurent@366:             return self.PythonBuffer.IsCurrentSaved()
laurent@366:         else:
laurent@366:             return True
laurent@366: 
laurent@366:     def LoadPrevious(self):
laurent@366:         self.PythonCode = self.Copy(self.PythonBuffer.Previous())
laurent@366:     
laurent@366:     def LoadNext(self):
laurent@366:         self.PythonCode = self.Copy(self.PythonBuffer.Next())
laurent@366:     
laurent@366:     def GetBufferState(self):
laurent@366:         first = self.PythonBuffer.IsFirst()
laurent@366:         last = self.PythonBuffer.IsLast()
laurent@366:         return not first, not last
laurent@366: 
laurent@366: def _GetClassFunction(name):
laurent@366:     def GetRootClass():
laurent@366:         __import__("plugins.python.modules." + name)
laurent@366:         return getattr(modules, name).RootClass
laurent@366:     return GetRootClass
laurent@366: 
laurent@366: class RootClass(PythonCodeTemplate):
laurent@366: 
laurent@366:     # For root object, available Childs Types are modules of the modules packages.
laurent@366:     PlugChildsTypes = [(name, _GetClassFunction(name), help) for name, help in zip(modules.__all__,modules.helps)]
laurent@366:     
laurent@366:     def PluginPath(self):
laurent@366:         return os.path.join(self.PlugParent.PluginPath(), self.PlugType)
laurent@366:     
laurent@366:     def PlugGenerate_C(self, buildpath, locations):
laurent@366:         """
laurent@366:         Generate C code
laurent@366:         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
laurent@366:         @param locations: List of complete variables locations \
laurent@366:             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
laurent@366:             "NAME" : name of the variable (generally "__IW0_1_2" style)
laurent@366:             "DIR" : direction "Q","I" or "M"
laurent@366:             "SIZE" : size "X", "B", "W", "D", "L"
laurent@366:             "LOC" : tuple of interger for IEC location (0,1,2,...)
laurent@366:             }, ...]
laurent@366:         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
laurent@366:         """
laurent@366:         current_location = self.GetCurrentLocation()
laurent@366:         # define a unique name for the generated C file
laurent@366:         location_str = "_".join(map(lambda x:str(x), current_location))
laurent@366:         
laurent@366:         plugin_root = self.GetPlugRoot()
laurent@366:         plugin_root.GetIECProgramsAndVariables()
laurent@366:         
laurent@366:         plc_python_filepath = os.path.join(os.path.split(__file__)[0], "plc_python.c")
laurent@366:         plc_python_file = open(plc_python_filepath, 'r')
laurent@366:         plc_python_code = plc_python_file.read()
laurent@366:         plc_python_file.close()
laurent@366:         python_eval_fb_list = []
laurent@367:         for v in plugin_root._VariablesList:
laurent@366:             if v["vartype"] == "FB" and v["type"] in ["PYTHON_EVAL","PYTHON_POLL"]:
laurent@366:                 python_eval_fb_list.append(v)
laurent@366:         python_eval_fb_count = max(1, len(python_eval_fb_list))
laurent@366:         
laurent@366:         # prepare python code
laurent@366:         plc_python_code = plc_python_code % {
laurent@366:            "python_eval_fb_count": python_eval_fb_count,
laurent@366:            "location": location_str}
laurent@366:         
laurent@366:         Gen_Pythonfile_path = os.path.join(buildpath, "python_%s.c"%location_str)
laurent@366:         pythonfile = open(Gen_Pythonfile_path,'w')
laurent@366:         pythonfile.write(plc_python_code)
laurent@366:         pythonfile.close()
laurent@366:         
laurent@366:         runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str)
laurent@366:         runtimefile = open(runtimefile_path, 'w')
laurent@366:         runtimefile.write(self.GetPythonCode())
laurent@366:         runtimefile.close()
laurent@366:         
laurent@366:         if wx.Platform == '__WXMSW__':
laurent@366:             matiec_flags = " -I../../matiec/lib"
laurent@366:         else:
laurent@366:             matiec_flags = " -I../matiec/lib"
laurent@366:         
laurent@366:         return [(Gen_Pythonfile_path, matiec_flags)], "", True, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))