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: greg@427: open_pyeditor = True greg@427: has_permissions = self.GetPlugRoot().CheckProjectPathPerm() greg@427: if not has_permissions: greg@427: dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame, greg@427: _("You don't have write permissions.\nOpen PythonEditor anyway ?"), greg@427: _("Open PythonEditor"), greg@427: wx.YES_NO|wx.ICON_QUESTION) greg@427: open_pyeditor = dialog.ShowModal() == wx.ID_YES greg@427: dialog.Destroy() greg@427: if open_pyeditor: greg@427: def _onclose(): greg@427: self._View = None greg@427: if has_permissions: greg@427: def _onsave(): greg@427: self.GetPlugRoot().SaveProject() greg@427: else: greg@427: def _onsave(): greg@427: pass greg@427: self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self) greg@427: self._View._onclose = _onclose greg@427: self._View._onsave = _onsave greg@427: self._View.Show() laurent@366: laurent@366: def OnPlugSave(self): laurent@366: filepath = self.PythonFileName() laurent@366: laurent@366: text = "\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@430: xmlfile.write(text.encode("utf-8")) 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: greg@418: matiec_flags = '"-I%s"'%os.path.abspath(self.GetPlugRoot().GetIECLibPath()) laurent@366: laurent@366: return [(Gen_Pythonfile_path, matiec_flags)], "", True, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))