Beremiz plugins definitions.
authoretisserant
Tue, 04 Sep 2007 17:58:29 +0200
changeset 20 d3cb5020997b
parent 19 73257cea38bd
child 21 bded6d31365c
Beremiz plugins definitions.
Beremiz.py
plugger.py
plugins/canfestival/canfestival.py
plugins/svgui/svgui.py
--- a/Beremiz.py	Tue Sep 04 17:16:42 2007 +0200
+++ b/Beremiz.py	Tue Sep 04 17:58:29 2007 +0200
@@ -26,22 +26,10 @@
 
 import wx
 
-from time import localtime
-from datetime import datetime
 import types
 
 import os, re, platform, sys, time, traceback, getopt, commands
-base_folder = os.path.split(sys.path[0])[0]
-sys.path.append(os.path.join(base_folder, "plcopeneditor"))
-sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
-sys.path.append(os.path.join(base_folder, "wxsvg", "svgui", "defeditor"))
-
-iec2cc_path = os.path.join(base_folder, "matiec", "iec2cc")
-ieclib_path = os.path.join(base_folder, "matiec", "lib")
-
-from PLCOpenEditor import PLCOpenEditor, ProjectDialog
-from TextViewer import TextViewer
-from plcopen.structures import IEC_KEYWORDS#, AddPlugin
+
 from plugger import PluginsRoot
 
 class LogPseudoFile:
@@ -80,6 +68,41 @@
     def isatty(self):
         return false
 
+    def LogCommand(self, Command, sz_limit = 100):
+
+        import os, popen2, fcntl, select, signal
+        
+        child = popen2.Popen3(Command, 1) # capture stdout and stderr from command
+        child.tochild.close()             # don't need to talk to child
+        outfile = child.fromchild 
+        outfd = outfile.fileno()
+        errfile = child.childerr
+        errfd = errfile.fileno()
+        outdata = errdata = ''
+        outeof = erreof = 0
+        outlen = errlen = 0
+        while 1:
+            ready = select.select([outfd,errfd],[],[]) # wait for input
+            if outfd in ready[0]:
+                outchunk = outfile.readline()
+                if outchunk == '': outeof = 1 
+                else : outlen += 1
+                outdata += outchunk
+                self.write(outchunk)
+            if errfd in ready[0]:
+                errchunk = errfile.readline()
+                if errchunk == '': erreof = 1 
+                else : errlen += 1
+                errdata += errchunk
+                self.write_warning(errchunk)
+            if outeof and erreof : break
+            if errlen > sz_limit or outlen > sz_limit : 
+                os.kill(child.pid, signal.SIGTERM)
+                self.write_error("Output size reached limit -- killed\n")
+                break
+        err = child.wait()
+        return (err, outdata, errdata)
+
 [ID_BEREMIZ, ID_BEREMIZMAINSPLITTER, 
  ID_BEREMIZSECONDSPLITTER, ID_BEREMIZLEFTPANEL, 
  ID_BEREMIZPARAMSPANEL, ID_BEREMIZLOGCONSOLE, 
@@ -299,14 +322,12 @@
         
         self.Log = LogPseudoFile(self.LogConsole)
         
-        self.PluginRoot = PluginsRoot()
+        self.PluginRoot = PluginsRoot(self)
         
         if projectOpen:
             self.PluginRoot.LoadProject(projectOpen)
             self.RefreshPluginTree()
         
-        self.PLCEditor = None
-        
         self.RefreshPluginParams()
         self.RefreshButtons()
         self.RefreshMainMenu()
@@ -588,123 +609,6 @@
                     textctrl.SetValue(str(element_infos["value"]))
             first = False
     
-    def UpdateAttributesTreeParts(self, tree, new_tree):
-        tree_leafs = [(element_infos["name"], element_infos["type"]) for element_infos in tree["children"]]
-        new_tree_leafs = [(element_infos["name"], element_infos["type"]) for element_infos in new_tree["children"]]
-        if tree_leafs != new_tree_leafs:
-            tree["children"] = new_tree["children"]
-            for child in tree["children"]:
-                self.PrepareAttributesTree(child)
-        else:
-            for idx, new_element_infos in enumerate(new_tree["children"]):
-                tree["children"][idx]["value"] = new_element_infos["value"]
-                if len(new_element_infos["children"]) > 0:
-                    self.UpdateAttributesTreeParts(tree["children"][idx], new_element_infos)
-    
-    def PrepareAttributesTree(self, tree):
-        if len(tree["children"]) > 0:
-            tree["open"] = False
-            for child in tree["children"]:
-                self.PrepareAttributesTree(child)
-    
-    def GenerateTable(self, data, tree, path, indent):
-        if path:
-            tree_path = "%s.%s"%(path, tree["name"])
-            infos = {"Attribute" : "   " * indent + tree["name"], "Value" : tree["value"], "Type" : tree["type"], "Open" : "", "Path" : tree_path}
-            data.append(infos)
-            indent += 1
-        else:
-            tree_path = tree["name"]
-        if len(tree["children"]) > 0:
-            if tree["open"] or not path:
-                if path:
-                    infos["Open"] = "v"
-                for child in tree["children"]:
-                    self.GenerateTable(data, child, tree_path, indent)
-            elif path:
-                infos["Open"] = ">"
-    
-    def RefreshAttributesGrid(self):
-        plugin = self.GetSelectedPlugin()
-        if not plugin:
-            self.AttributesTree = []
-            self.Table.Empty()
-        else:
-            new_params = plugin.GetParamsAttributes()
-            for idx, child in enumerate(new_params):
-                if len(self.AttributesTree) > idx:
-                    if self.AttributesTree[idx]["name"] == child["name"]:
-                        self.UpdateAttributesTreeParts(self.AttributesTree[idx], child)
-                    else:
-                        self.AttributesTree[idx] = child
-                        self.PrepareAttributesTree(child)
-                else:
-                    self.AttributesTree.append(child)
-                    self.PrepareAttributesTree(child)
-            while len(self.AttributesTree) > len(new_params):
-                self.AttributesTree.pop(-1)
-            data = []
-            for child in self.AttributesTree:
-                self.GenerateTable(data, child, None, 0)
-            self.Table.SetData(data)
-        self.Table.ResetView(self.AttributesGrid)
-    
-    def OpenClose(self, tree, path):
-        parts = path.split(".", 1)
-        for child in tree["children"]:
-            if child["name"] == parts[0]:
-                if len(parts) > 1:
-                    return self.OpenClose(child, parts[1])
-                elif len(child["children"]) > 0:
-                    child["open"] = not child["open"]
-                    return True
-        return False
-    
-    def OpenCloseAttribute(self):
-        if self.AttributesGrid.GetGridCursorCol() == 0:
-            row = self.AttributesGrid.GetGridCursorRow()
-            path = self.Table.GetValueByName(row, "Path")
-            parts = path.split(".", 1)
-            for child in self.AttributesTree:
-                if child["name"] == parts[0] and len(parts) > 1:
-                    result = self.OpenClose(child, parts[1])
-                    if result:
-                        self.RefreshAttributesGrid()
-    
-    def OnParamsEnableChanged(self, event):
-        plugin = self.GetSelectedPlugin()
-        if plugin and plugin != self.PluginRoot:
-            plugin.BaseParams.setEnabled(event.Checked())
-        event.Skip()
-    
-    def OnParamsIECChannelChanged(self, event):
-        plugin = self.GetSelectedPlugin()
-        if plugin and plugin != self.PluginRoot:
-            plugin.BaseParams.setIEC_Channel(self.ParamsIECChannel.GetValue())
-        event.Skip()
-    
-    def OnParamsTargetTypeChanged(self, event):
-        plugin = self.GetSelectedPlugin()
-        if plugin and plugin == self.PluginRoot:
-            self.PluginRoot.ChangeTargetType(self.ParamsTargetType.GetStringSelection())
-            self.RefreshAttributesGrid()
-        event.Skip()
-    
-    def OnAttributesGridCellChange(self, event):
-        row = event.GetRow()
-        plugin = self.GetSelectedPlugin()
-        if plugin:
-            path = self.Table.GetValueByName(row, "Path")
-            value = self.Table.GetValueByName(row, "Value")
-            plugin.SetParamsAttribute(path, value)
-            print plugin.GetParamsAttributes(path)
-            self.RefreshAttributesGrid()
-        event.Skip()
-    
-    def OnAttributesGridCellLeftClick(self, event):
-        wx.CallAfter(self.OpenCloseAttribute)
-        event.Skip()
-    
     def OnNewProjectMenu(self, event):
         defaultpath = self.PluginRoot.GetProjectPath()
         if defaultpath == "":
@@ -713,25 +617,20 @@
         if dialog.ShowModal() == wx.ID_OK:
             projectpath = dialog.GetPath()
             dialog.Destroy()
-            if os.path.isdir(projectpath) and len(os.listdir(projectpath)) == 0:
-                dialog = ProjectDialog(self)
-                if dialog.ShowModal() == wx.ID_OK:
-                    values = dialog.GetValues()
-                    values["creationDateTime"] = datetime(*localtime()[:6])
-                    self.PluginRoot.NewProject(projectpath, values)
-                    self.RefreshPluginTree()
-                    self.RefreshButtons()
-                    self.RefreshMainMenu()
-                dialog.Destroy()
+            res = self.PluginRoot.NewProject(projectpath)
+            if not res :
+                self.RefreshPluginTree()
+                self.RefreshButtons()
+                self.RefreshMainMenu()
             else:
-                message = wx.MessageDialog(self, "Folder choosen isn't empty. You can't use it for a new project!", "ERROR", wx.OK|wx.ICON_ERROR)
+                message = wx.MessageDialog(self, res, "ERROR", wx.OK|wx.ICON_ERROR)
                 message.ShowModal()
                 message.Destroy()
         event.Skip()
     
     def OnOpenProjectMenu(self, event):
         defaultpath = self.PluginRoot.GetProjectPath()
-        if defaultpath == "":
+        if not defaultpath:
             defaultpath = os.getcwd()
         dialog = wx.DirDialog(self , "Choose a project", defaultpath, wx.DD_NEW_DIR_BUTTON)
         if dialog.ShowModal() == wx.ID_OK:
@@ -785,7 +684,7 @@
         event.Skip()
 
     def OnBuildMenu(self, event):
-        self.BuildAutom()
+        #self.BuildAutom()
         event.Skip()
 
     def OnSimulateMenu(self, event):
@@ -828,50 +727,6 @@
     
     def DeletePlugin(self):
         pass
-    
-    def EditPLC(self):
-        if not self.PLCEditor:
-            self.PLCEditor = PLCOpenEditor(self, self.PluginRoot.PLCManager)
-            self.PLCEditor.RefreshProjectTree()
-            self.PLCEditor.RefreshFileMenu()
-            self.PLCEditor.RefreshEditMenu()
-            self.PLCEditor.RefreshToolBar()
-            self.PLCEditor.Show()
-
-    def LogCommand(self, Command, sz_limit = 100):
-
-        import os, popen2, fcntl, select, signal
-        
-        child = popen2.Popen3(Command, 1) # capture stdout and stderr from command
-        child.tochild.close()             # don't need to talk to child
-        outfile = child.fromchild 
-        outfd = outfile.fileno()
-        errfile = child.childerr
-        errfd = errfile.fileno()
-        outdata = errdata = ''
-        outeof = erreof = 0
-        outlen = errlen = 0
-        while 1:
-            ready = select.select([outfd,errfd],[],[]) # wait for input
-            if outfd in ready[0]:
-                outchunk = outfile.readline()
-                if outchunk == '': outeof = 1 
-                else : outlen += 1
-                outdata += outchunk
-                self.Log.write(outchunk)
-            if errfd in ready[0]:
-                errchunk = errfile.readline()
-                if errchunk == '': erreof = 1 
-                else : errlen += 1
-                errdata += errchunk
-                self.Log.write_warning(errchunk)
-            if outeof and erreof : break
-            if errlen > sz_limit or outlen > sz_limit : 
-                os.kill(child.pid, signal.SIGTERM)
-                self.Log.write_error("Output size reached limit -- killed\n")
-                break
-        err = child.wait()
-        return (err, outdata, errdata)
 
 #-------------------------------------------------------------------------------
 #                             Add Bus Dialog
--- a/plugger.py	Tue Sep 04 17:16:42 2007 +0200
+++ b/plugger.py	Tue Sep 04 17:58:29 2007 +0200
@@ -2,15 +2,18 @@
 Base definitions for beremiz plugins
 """
 
-import os
+import os,sys
 import plugins
 import types
 import shutil
 from xml.dom import minidom
+
+#Quick hack to be able to find Beremiz IEC tools. Should be config params.
+base_folder = os.path.split(sys.path[0])[0]
+sys.path.append(os.path.join(base_folder, "plcopeneditor"))
+
 from xmlclass import GenerateClassesFromXSDstring
 
-from PLCControler import PLCControler
-
 _BaseParamsClass = GenerateClassesFromXSDstring("""<?xml version="1.0" encoding="ISO-8859-1" ?>
         <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
           <xsd:element name="BaseParams">
@@ -66,6 +69,7 @@
         return False
         
     def OnPlugSave(self):
+        #Default, do nothing and return success
         return True
 
     def GetParamsAttributes(self, path = None):
@@ -98,17 +102,15 @@
             os.mkdir(plugpath)
 
         # generate XML for base XML parameters controller of the plugin
-        basexmlfilepath = self.PluginBaseXmlFilePath()
-        if basexmlfilepath:
-            BaseXMLFile = open(basexmlfilepath,'w')
+        if self.MandatoryParams:
+            BaseXMLFile = open(self.PluginBaseXmlFilePath(),'w')
             BaseXMLFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
             BaseXMLFile.write(self.MandatoryParams[1].generateXMLText(self.MandatoryParams[0], 0))
             BaseXMLFile.close()
         
         # generate XML for XML parameters controller of the plugin
-        xmlfilepath = self.PluginXmlFilePath()
-        if xmlfilepath:
-            XMLFile = open(xmlfilepath,'w')
+        if self.PlugParams:
+            XMLFile = open(self.PluginXmlFilePath(),'w')
             XMLFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
             XMLFile.write(self.PlugParams[1].generateXMLText(self.PlugParams[0], 0))
             XMLFile.close()
@@ -292,8 +294,14 @@
                 if os.path.isdir(_self.PlugPath(PlugName)) and os.path.isfile(_self.PluginXmlFilePath(PlugName)):
                     #Load the plugin.xml file into parameters members
                     _self.LoadXMLParams(PlugName)
+                    # Basic check. Better to fail immediately.
+                    if (_self.BaseParams.getName() != PlugName):
+                        raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(PlugName, _self.BaseParams.getName())
+
+                    # Now, self.PlugPath() should be OK
+                    
                     # Check that IEC_Channel is not already in use.
-                    self.FindNewIEC_Channel(self.BaseParams.getIEC_Channel())
+                    _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel())
                     # Call the plugin real __init__
                     if getattr(PlugClass, "__init__", None):
                         PlugClass.__init__(_self)
@@ -319,31 +327,21 @@
         return newPluginOpj
             
 
-    def LoadXMLParams(self, PlugName = None, test = True):
+    def LoadXMLParams(self, PlugName = None):
         # Get the base xml tree
-        basexmlfilepath = self.PluginBaseXmlFilePath(PlugName)
-        if basexmlfilepath:
-            basexmlfile = open(basexmlfilepath, 'r')
+        if self.MandatoryParams:
+            basexmlfile = open(self.PluginBaseXmlFilePath(PlugName), 'r')
             basetree = minidom.parse(basexmlfile)
             self.MandatoryParams[1].loadXMLTree(basetree.childNodes[0])
             basexmlfile.close()
         
         # Get the xml tree
-        xmlfilepath = self.PluginXmlFilePath(PlugName)
-        if xmlfilepath:
-            xmlfile = open(xmlfilepath, 'r')
+        if self.PlugParams:
+            xmlfile = open(self.PluginXmlFilePath(PlugName), 'r')
             tree = minidom.parse(xmlfile)
             self.PlugParams[1].loadXMLTree(tree.childNodes[0])
             xmlfile.close()
         
-        if test:
-            # Basic check. Better to fail immediately.
-            if not PlugName:
-                PlugName = os.path.split(self.PlugPath())[1].split(NameTypeSeparator)[0]
-            if (self.BaseParams.getName() != PlugName):
-                raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(PlugName, self.BaseParams.getName())
-            # Now, self.PlugPath() should be OK
-
     def LoadChilds(self):
         # Iterate over all PlugName@PlugType in plugin directory, and try to open them
         for PlugDir in os.listdir(self.PlugPath()):
@@ -359,7 +357,38 @@
         return getattr(__import__("plugins." + name), name).RootClass
     return GetRootClass
 
+
+####################################################################################
+####################################################################################
+####################################################################################
+###################################   ROOT    ######################################
+####################################################################################
+####################################################################################
+####################################################################################
+
+iec2cc_path = os.path.join(base_folder, "matiec", "iec2cc")
+ieclib_path = os.path.join(base_folder, "matiec", "lib")
+
+# import for project creation timestamping
+from time import localtime
+from datetime import datetime
+# import necessary stuff from PLCOpenEditor
+from PLCControler import PLCControler
+from PLCOpenEditor import PLCOpenEditor, ProjectDialog
+from TextViewer import TextViewer
+from plcopen.structures import IEC_KEYWORDS
+
 class PluginsRoot(PlugTemplate):
+    """
+    This class define Root object of the plugin tree. 
+    It is responsible of :
+    - Managing project directory
+    - Building project
+    - Handling PLCOpenEditor controler and view
+    - Loading user plugins and instanciante them as childs
+    - ...
+    
+    """
 
     # For root object, available Childs Types are modules of the plugin packages.
     PlugChildsTypes = [(name, _GetClassFunction(name)) for name in plugins.__all__]
@@ -418,26 +447,43 @@
     </xsd:schema>
     """
 
-    def __init__(self):
-        PlugTemplate.__init__(self)
+    def __init__(self, frame):
+
+        self.MandatoryParams = None
+        self.AppFrame = frame
+        
+        """
+        This method are not called here... but in NewProject and OpenProject
+        self._AddParamsMembers()
+        self.PluggedChilds = {}
+        """
+
         # self is the parent
         self.PlugParent = None
         # Keep track of the plugin type name
         self.PlugType = "Beremiz"
         
-        self.ProjectPath = ""
+        # After __init__ root plugin is not valid
+        self.ProjectPath = None
         self.PLCManager = None
+        self.PLCEditor = None
     
     def HasProjectOpened(self):
         """
         Return if a project is actually opened
         """
-        return self.ProjectPath != ""
+        return self.ProjectPath != None
     
     def GetProjectPath(self):
         return self.ProjectPath
     
-    def NewProject(self, ProjectPath, PLCParams):
+    def GetPlugInfos(self):
+        childs = []
+        for child in self.IterChilds():
+            childs.append(child.GetPlugInfos())
+        return {"name" : os.path.split(self.ProjectPath)[1], "type" : None, "values" : childs}
+    
+    def NewProject(self, ProjectPath):
         """
         Create a new project in an empty folder
         @param ProjectPath: path of the folder where project have to be created
@@ -446,6 +492,16 @@
         # Verify that choosen folder is empty
         if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0:
             return "Folder choosen isn't empty. You can't use it for a new project!"
+        
+        dialog = ProjectDialog(self.AppFrame)
+        if dialog.ShowModal() == wx.ID_OK:
+            values = dialog.GetValues()
+            values["creationDateTime"] = datetime(*localtime()[:6])
+            dialog.Destroy()
+        else:
+            dialog.Destroy()
+            return "Project not created"
+        
         # Create Controler for PLCOpen program
         self.PLCManager = PLCControler()
         self.PLCManager.CreateNewProject(PLCParams.pop("projectName"))
@@ -453,11 +509,8 @@
         # Change XSD into class members
         self._AddParamsMembers()
         self.PluggedChilds = {}
-        # No IEC channel, name, etc...
-        self.MandatoryParams = []
         # Keep track of the root plugin (i.e. project path)
         self.ProjectPath = ProjectPath
-        self.BaseParams.setName(os.path.split(ProjectPath)[1])
         return None
         
     def LoadProject(self, ProjectPath):
@@ -478,19 +531,16 @@
         # Change XSD into class members
         self._AddParamsMembers()
         self.PluggedChilds = {}
-        # No IEC channel, name, etc...
-        self.MandatoryParams = None
         # Keep track of the root plugin (i.e. project path)
         self.ProjectPath = ProjectPath
         # If dir have already be made, and file exist
         if os.path.isdir(self.PlugPath()) and os.path.isfile(self.PluginXmlFilePath()):
             #Load the plugin.xml file into parameters members
-            result = self.LoadXMLParams(test = False)
+            result = self.LoadXMLParams()
             if result:
                 return result
             #Load and init all the childs
             self.LoadChilds()
-        self.BaseParams.setName(os.path.split(ProjectPath)[1])
         return None
     
     def SaveProject(self):
@@ -501,9 +551,6 @@
     def PlugPath(self, PlugName=None):
         return self.ProjectPath
     
-    def PluginBaseXmlFilePath(self, PlugName=None):
-        return None
-    
     def PluginXmlFilePath(self, PlugName=None):
         return os.path.join(self.PlugPath(PlugName), "beremiz.xml")
 
@@ -517,61 +564,93 @@
         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
         """
         return [(C_file_name, "") for C_file_name in self.PLCGeneratedCFiles ] , ""
-        
-    def _Generate_SoftPLC(self, buildpath, logger):
-
+    
+    def _getBuildPath(self):
+        return os.path.join(self.ProjectPath, "build")
+    
+    def _getIECcodepath(self):
+        # define name for IEC code file
+        return os.path.join(self._getBuildPath(), "plc.st")
+    
+    def _Generate_SoftPLC(self, logger):
+        """
+        Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2CC
+        @param buildpath: path where files should be created
+        @param logger: the log pseudo file
+        """
+
+        logger.write("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")
+        buildpath = self._getBuildPath()
+        # define name for IEC code file
+        plc_file = self._getIECcodepath()
+        # ask PLCOpenEditor controller to write ST/IL/SFC code file
+        result = self.PLCManager.GenerateProgram(plc_file)
+        if not result:
+            # Failed !
+            logger.write_error("Error : ST/IL/SFC code generator returned %d"%result)
+            return False
+        logger.write("Compiling ST Program in to C Program...\n")
+        # Now compile IEC code into many C files
+        # files are listed to stdout, and errors to stderr. 
+        status, result, err_result = logger.LogCommand("%s %s -I %s %s"%(iec2cc_path, plc_file, ieclib_path, self.TargetDir))
+        if status:
+            # Failed !
+            logger.write_error("Error : IEC to C compiler returned %d"%status)
+            return False
+        # Now extract C files of stdout
+        C_files = result.splitlines()
+        # remove those that are not to be compiled because included by others
+        C_files.remove("POUS.c")
+        C_files.remove("LOCATED_VARIABLES.h")
+        # transform those base names to full names with path
+        C_files = map(lambda filename:os.path.join(self.TargetDir, filename), C_files)
+        logger.write("Extracting Located Variables...\n")
+        # IEC2CC compiler generate a list of located variables : LOCATED_VARIABLES.h
+        location_file = open(os.path.join(buildpath,"LOCATED_VARIABLES.h"))
+        locations = []
+        # each line of LOCATED_VARIABLES.h declares a located variable
+        lines = [line.strip() for line in location_file.readlines()]
+        # This regular expression parses the lines genereated by IEC2CC
         LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWD]))?,(?P<LOC>[,0-9]*)\)")
-        
-        if self.PLCManager:
-            logger.write("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")
-            plc_file = os.path.join(self.TargetDir, "plc.st")
-            result = self.PLCManager.GenerateProgram(plc_file)
-            if not result:
-                logger.write_error("Error : ST/IL/SFC code generator returned %d"%result)
-                return False
-            logger.write("Compiling ST Program in to C Program...\n")
-            status, result, err_result = self.LogCommand("%s %s -I %s %s"%(iec2cc_path, plc_file, ieclib_path, self.TargetDir))
-            if status:
-                new_dialog = wx.Frame(None)
-                ST_viewer = TextViewer(new_dialog, None, None)
-                #ST_viewer.Enable(False)
-                ST_viewer.SetKeywords(IEC_KEYWORDS)
-                ST_viewer.SetText(file(plc_file).read())
-                new_dialog.Show()
-                raise Exception, "Error : IEC to C compiler returned %d"%status
-            C_files = result.splitlines()
-            C_files.remove("POUS.c")
-            C_files = map(lambda filename:os.path.join(self.TargetDir, filename), C_files)
-            logger.write("Extracting Located Variables...\n")
-            location_file = open(os.path.join(self.TargetDir,"LOCATED_VARIABLES.h"))
-            locations = []
-            lines = [line.strip() for line in location_file.readlines()]
-            for line in lines:
-                result = LOCATED_MODEL.match(line)
-                if result:
-                    resdict = result.groupdict()
-                    # rewrite location as a tuple of integers
-                    resdict['LOC'] = tuple(map(int,resdict['LOC'].split(',')))
-                    if not resdict['SIZE']:
-                        resdict['SIZE'] = 'X'
-                    locations.append(resdict)
+        for line in lines:
+            # If line match RE, 
+            result = LOCATED_MODEL.match(line)
+            if result:
+                # Get the resulting dict
+                resdict = result.groupdict()
+                # rewrite string for variadic location as a tuple of integers
+                resdict['LOC'] = tuple(map(int,resdict['LOC'].split(',')))
+                # set located size to 'X' if not given 
+                if not resdict['SIZE']:
+                    resdict['SIZE'] = 'X'
+                # finally store into located variable list
+                locations.append(resdict)
+        # Keep track of generated C files for later use by self.PlugGenerate_C
         self.PLCGeneratedCFiles = C_files
+        # Keep track of generated located variables for later use by self._Generate_C
         self.PLCGeneratedLocatedVars = locations
         return True
 
     def _build(self, logger):
-        buildpath = os.path.join(self.ProjectPath, "build")
+        """
+        Method called by user to (re)build SoftPLC and plugin tree
+        """
+        buildpath = self._getBuildPath()
+
+        # Eventually create build dir
         if not os.path.exists(buildpath):
             os.mkdir(buildpath)
         
         logger.write("Start build in %s" % buildpath)
         
-        if not self._Generate_SoftPLC(buildpath, logger):
-            logger.write("SoftPLC code generation failed !")
-            return
-    
+        # Generate SoftPLC code
+        if not self._Generate_SoftPLC(logger):
+            logger.write_error("SoftPLC code generation failed !")
+            return False
+
         logger.write("SoftPLC code generation successfull")
         
+        # Generate C code and compilation params from plugin hierarchy
         try:
             CFilesAndCFLAGS, LDFLAGS = self._Generate_C(
                 buildpath, 
@@ -581,14 +660,39 @@
         except Exception, msg:
             logger.write_error("Plugins code generation Failed !")
             logger.write_error(str(msg))
-            return
+            return False
 
         logger.write_error("Plugins code generation successfull")
-        
+
+        # Compile the resulting code into object files.
         for CFile, CFLAG in CFilesAndCFLAGS:
             print CFile,CFLAG
         
-        LDFLAGS
-
-    PluginMethods = [("Build",_build), ("Clean",None), ("Run",None), ("EditPLC",None), ("Simulate",None)]
-    
+        # Link object files into something that can be executed on target
+        print LDFLAGS
+
+    def _showIECcode(self, logger):
+        plc_file = self._getIECcodepath()
+        new_dialog = wx.Frame(None)
+        ST_viewer = TextViewer(new_dialog, None, None)
+        #ST_viewer.Enable(False)
+        ST_viewer.SetKeywords(IEC_KEYWORDS)
+        try:
+            text = file(plc_file).read()
+        except:
+            text = '(* No IEC code have been generated at that time ! *)'
+        ST_viewer.SetText(text)
+            
+        new_dialog.Show()
+
+    def _EditPLC(self, logger):
+        if not self.PLCEditor:
+            self.PLCEditor = PLCOpenEditor(self, self.PLCManager)
+            self.PLCEditor.RefreshProjectTree()
+            self.PLCEditor.RefreshFileMenu()
+            self.PLCEditor.RefreshEditMenu()
+            self.PLCEditor.RefreshToolBar()
+            self.PLCEditor.Show()
+
+    PluginMethods = [("Build",_build), ("Clean",None), ("Run",None), ("EditPLC",None), ("Show IEC code",_showIECcode)]
+    
--- a/plugins/canfestival/canfestival.py	Tue Sep 04 17:16:42 2007 +0200
+++ b/plugins/canfestival/canfestival.py	Tue Sep 04 17:58:29 2007 +0200
@@ -1,4 +1,7 @@
-import os
+import os, sys
+base_folder = os.path.split(sys.path[0])[0]
+sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
+
 from nodelist import NodeList
 from nodemanager import NodeManager
 import config_utils, gen_cfile
--- a/plugins/svgui/svgui.py	Tue Sep 04 17:16:42 2007 +0200
+++ b/plugins/svgui/svgui.py	Tue Sep 04 17:58:29 2007 +0200
@@ -1,4 +1,7 @@
-import os
+import os, sys
+base_folder = os.path.split(sys.path[0])[0]
+sys.path.append(os.path.join(base_folder, "wxsvg", "defeditor"))
+
 from DEFControler import DEFControler
 from defeditor import EditorFrame