Pleliminary build process -- C code generation
authoretisserant
Fri, 07 Sep 2007 10:19:04 +0200
changeset 22 9a0c535c3272
parent 21 bded6d31365c
child 23 e007d9d466d7
Pleliminary build process -- C code generation
Beremiz.py
plugger.py
plugins/canfestival/canfestival.py
plugins/canfestival/config_utils.py
--- a/Beremiz.py	Fri Sep 07 09:11:18 2007 +0200
+++ b/Beremiz.py	Fri Sep 07 10:19:04 2007 +0200
@@ -47,23 +47,18 @@
     def writelines(self, l):
         map(self.write, l)
 
-    def write(self, s):
-        if self.default_style != self.black_white: 
-            self.output.SetDefaultStyle(self.black_white)
-            self.default_style = self.black_white
+    def write(self, s, style = None):
+        if not style : style=self.black_white
+        if self.default_style != style: 
+            self.output.SetDefaultStyle(style)
+            self.default_style = style
         self.output.AppendText(s) 
 
     def write_warning(self, s):
-        if self.default_style != self.red_white: 
-            self.output.SetDefaultStyle(self.red_white)
-            self.default_style = self.red_white
-        self.output.AppendText(s) 
+        self.write(s,self.red_white)
 
     def write_error(self, s):
-        if self.default_style != self.red_yellow: 
-            self.output.SetDefaultStyle(self.red_yellow)
-            self.default_style = self.red_yellow
-        self.output.AppendText(s) 
+        self.write(s,self.red_yellow)
 
     def flush(self):
         self.output.SetValue("")
@@ -570,7 +565,8 @@
                         choicectrl.Append(choice)
                     callback = self.GetChoiceCallBackFunction(choicectrl, element_path)
                 choicectrl.Bind(wx.EVT_CHOICE, callback, id=id)
-                choicectrl.SetStringSelection(element_infos["value"])
+                if element_infos["value"]:
+                    choicectrl.SetStringSelection(element_infos["value"])
             elif isinstance(element_infos["type"], types.DictType):
                 boxsizer = wx.BoxSizer(wx.HORIZONTAL)
                 if first:
@@ -582,7 +578,8 @@
                     pos=wx.Point(0, 0), size=wx.Size(100, 17), style=0)
                 boxsizer.AddWindow(statictext, 0, border=0, flag=wx.TOP|wx.LEFT|wx.BOTTOM)
                 id = wx.NewId()
-                min = max = -1
+                min = -sys.maxint-1
+                max = sys.maxint
                 if "min" in element_infos["type"]:
                     min = element_infos["type"]["min"]
                 if "max" in element_infos["type"]:
@@ -636,7 +633,7 @@
     
     def OnNewProjectMenu(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:
--- a/plugger.py	Fri Sep 07 09:11:18 2007 +0200
+++ b/plugger.py	Fri Sep 07 10:19:04 2007 +0200
@@ -7,6 +7,7 @@
 import types
 import shutil
 from xml.dom import minidom
+import wx
 
 #Quick hack to be able to find Beremiz IEC tools. Should be config params.
 base_folder = os.path.split(sys.path[0])[0]
@@ -136,11 +137,15 @@
         Generate C code
         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
         @param locations: List of complete variables locations \
-            [(IEC_loc, IEC_Direction, IEC_Type, Name)]\
-            ex: [((0,0,4,5),'I','STRING','__IX_0_0_4_5'),...]
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
         """
-        logger.write_warning(".".join(map(lambda x:str(x), current_location)) + " -> Nothing yo do")
+        logger.write_warning(".".join(map(lambda x:str(x), current_location)) + " -> Nothing yo do\n")
         return [],""
     
     def _Generate_C(self, buildpath, current_location, locations, logger):
@@ -158,7 +163,7 @@
                     # but update location (add curent IEC channel at the end)
                     new_location,
                     # filter locations that start with current IEC location
-                    [ (l,d,t,n) for l,d,t,n in locations if l[0:len(new_location)] == new_location ],
+                    [loc for loc in locations if loc["LOC"][0:len(new_location)] == new_location ],
                     #propagete logger
                     logger)
             # stack the result
@@ -377,6 +382,7 @@
 from PLCOpenEditor import PLCOpenEditor, ProjectDialog
 from TextViewer import TextViewer
 from plcopen.structures import IEC_KEYWORDS
+import re
 
 class PluginsRoot(PlugTemplate):
     """
@@ -504,8 +510,8 @@
         
         # Create Controler for PLCOpen program
         self.PLCManager = PLCControler()
-        self.PLCManager.CreateNewProject(PLCParams.pop("projectName"))
-        self.PLCManager.SetProjectProperties(properties = PLCParams)
+        self.PLCManager.CreateNewProject(values.pop("projectName"))
+        self.PLCManager.SetProjectProperties(properties = values)
         # Change XSD into class members
         self._AddParamsMembers()
         self.PluggedChilds = {}
@@ -587,15 +593,15 @@
         result = self.PLCManager.GenerateProgram(plc_file)
         if not result:
             # Failed !
-            logger.write_error("Error : ST/IL/SFC code generator returned %d"%result)
+            logger.write_error("Error : ST/IL/SFC code generator returned %d\n"%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))
+        status, result, err_result = logger.LogCommand("%s %s -I %s %s"%(iec2cc_path, plc_file, ieclib_path, buildpath))
         if status:
             # Failed !
-            logger.write_error("Error : IEC to C compiler returned %d"%status)
+            logger.write_error("Error : IEC to C compiler returned %d\n"%status)
             return False
         # Now extract C files of stdout
         C_files = result.splitlines()
@@ -641,14 +647,14 @@
         if not os.path.exists(buildpath):
             os.mkdir(buildpath)
         
-        logger.write("Start build in %s" % buildpath)
+        logger.write("Start build in %s\n" % buildpath)
         
         # Generate SoftPLC code
         if not self._Generate_SoftPLC(logger):
-            logger.write_error("SoftPLC code generation failed !")
+            logger.write_error("SoftPLC code generation failed !\n")
             return False
 
-        logger.write("SoftPLC code generation successfull")
+        logger.write("SoftPLC code generation successfull\n")
         
         # Generate C code and compilation params from plugin hierarchy
         try:
@@ -658,18 +664,18 @@
                 self.PLCGeneratedLocatedVars,
                 logger)
         except Exception, msg:
-            logger.write_error("Plugins code generation Failed !")
+            logger.write_error("Plugins code generation Failed !\n")
             logger.write_error(str(msg))
             return False
 
-        logger.write_error("Plugins code generation successfull")
+        logger.write("Plugins code generation successfull\n")
 
         # Compile the resulting code into object files.
         for CFile, CFLAG in CFilesAndCFLAGS:
-            print CFile,CFLAG
+            logger.write(str((CFile,CFLAG)))
         
         # Link object files into something that can be executed on target
-        print LDFLAGS
+        logger.write(LDFLAGS)
 
     def _showIECcode(self, logger):
         plc_file = self._getIECcodepath()
@@ -687,12 +693,18 @@
 
     def _EditPLC(self, logger):
         if not self.PLCEditor:
-            self.PLCEditor = PLCOpenEditor(self, self.PLCManager)
+            self.PLCEditor = PLCOpenEditor(self.AppFrame, 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)]
-    
+    def _Clean(self, logger):
+        logger.write_error("Not impl\n")
+    
+    def _Run(self, logger):
+        logger.write_error("Not impl\n")
+
+    PluginMethods = [("EditPLC",_EditPLC), ("Build",_build), ("Clean",_Clean), ("Run",_Run), ("Show IEC code",_showIECcode)]
+    
--- a/plugins/canfestival/canfestival.py	Fri Sep 07 09:11:18 2007 +0200
+++ b/plugins/canfestival/canfestival.py	Fri Sep 07 10:19:04 2007 +0200
@@ -56,11 +56,18 @@
         Generate C code
         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
         @param locations: List of complete variables locations \
-            [(IEC_loc, IEC_Direction IEC_Type, Name)]\
-            ex: [((0,0,4,5),'I','X','__IX_0_0_4_5'),...]
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
         """
+        # define a unique name for the generated C file
         prefix = "_".join(map(lambda x:str(x), current_location))
         Gen_OD_path = os.path.join(buildpath, prefix + "_OD.c" )
+        # Create a new copy of the model with DCF loaded with PDO mappings for desired location
         master = config_utils.GenerateConciseDCF(locations, current_location, self)
         res = gen_cfile.GenerateFile(Gen_OD_path, master)
         if res :
@@ -82,25 +89,6 @@
     PlugChildsTypes = [("CanOpenNode",_NodeListPlug)]
     
     def PlugGenerate_C(self, buildpath, current_location, locations):
-        """
-        Generate C code
-        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
-        @param locations: List of complete variables locations \
-            [(IEC_loc, IEC_Direction IEC_Type, Name)]\
-            ex: [((0,0,4,5),'I','X','__IX_0_0_4_5'),...]
-        """
-        prefix = "_".join(map(lambda x:str(x), current_location))
-        Gen_OD_path = os.path.join(buildpath, prefix + "_OD.c" )
-        master = config_utils.GenerateConciseDCF(locations, self)
-        res = gen_cfile.GenerateFile(Gen_OD_path, master)
-        if not res:
-             s = str(self.BaseParams.BusId)+"_IN(){}\n"
-             s += "CanOpen(str(\""+self.CanFestivalNode.CAN_Device+"\")"
-             f = file(filepath, 'a')
-             f.write(s)
-        else:
-             pass # error
-         
         return [],""
 
 
--- a/plugins/canfestival/config_utils.py	Fri Sep 07 09:11:18 2007 +0200
+++ b/plugins/canfestival/config_utils.py	Fri Sep 07 10:19:04 2007 +0200
@@ -139,7 +139,20 @@
     return ListCobIDAvailable.pop(0)
         
         
-def GenerateConciseDCF(locations, busname, nodelist):
+def GenerateConciseDCF(locations, current_location, nodelist):
+    """
+    Fills a CanFestival network editor model, with DCF with requested PDO mappings.
+    @param locations: List of complete variables locations \
+        [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+        "NAME" : name of the variable (generally "__IW0_1_2" style)
+        "DIR" : direction "Q","I" or "M"
+        "SIZE" : size "X", "B", "W", "D", "L"
+        "LOC" : tuple of interger for IEC location (0,1,2,...)
+        }, ...]
+    @param nodelist: CanFestival network editor model
+    @return: a modified copy of the given CanFestival network editor model
+    """
+    
     global DictLocations, DictCobID, DictLocationsNotMapped, ListCobIDAvailable, SlavesPdoNumber, DefaultTransmitTypeSlave
 
     DictLocations = {}
@@ -187,45 +200,46 @@
                 ListCobIDAvailable.remove(pdo_cobid)
     
     # Get list of locations check if exists and mappables -> put them in DictLocations
-    for locationtype, name in locations:    
-        if name in DictLocations.keys():
+    for location in locations:
+        locationtype = location["IEC_TYPE"]
+        name = location["NAME"]
+        if name in DictLocations:
             if DictLocations[name]["type"] != DicoTypes[locationtype]:
                 raise ValueError, "Conflict type for location \"%s\"" % name 
         else:
-            loc = [i for i in name.split("_") if len(i) > 0]
-            if len(loc) not in (4, 5):
-                continue
-            
-            prefix = loc[0][0]
-            
-            # Extract and check busname
-            if loc[0][1].isdigit():
-                sizelocation = ""
-                busnamelocation = int(loc[0][1:])
-            else:
-                sizelocation = loc[0][1]
-                busnamelocation = int(loc[0][2:])
-            if busnamelocation != busname:
-                continue # A ne pas remplacer par un message d'erreur
+            #get only the part of the location that concern this node
+            loc = location["LOC"][len(current_location):]
+            # loc correspond to (ID, INDEX, SUBINDEX [,BIT])
+            if len(loc) not in (3, 4):
+                raise ValueError, "Bad location size"
+            
+            direction = location["DIR"]
+            
+            sizelocation = location["SIZE"]
             
             # Extract and check nodeid
-            nodeid = int(loc[1])
+            nodeid, index, subindex = loc[:3]
+            
+            # Check Id is in slave node list
             if nodeid not in nodelist.SlaveNodes.keys():
-                continue
+                raise ValueError, "Non existing node ID : %d (variable %s)" % (nodeid,name)
+            
+            # Get the model for this node (made from EDS)
             node = nodelist.SlaveNodes[nodeid]["Node"]
             
             # Extract and check index and subindex
-            index = int(loc[2])
-            subindex = int(loc[3])
             if not node.IsEntry(index, subindex):
-                continue
+                raise ValueError, "No such index/subindex (%x,%x) in ID : %d (variable %s)" % (index,subindex,nodeid,name)
+            
+            #Get the entry info
             subentry_infos = node.GetSubentryInfos(index, subindex)
             
+            # If a PDO mappable
             if subentry_infos and subentry_infos["pdo"]:
-                if sizelocation == "X" and len(loc) > 4:
+                if sizelocation == "X" and len(loc) > 3:
                     numbit = loc[4]
-                elif sizelocation != "X" and len(loc) > 4:
-                    continue
+                elif sizelocation != "X" and len(loc) > 3:
+                    raise ValueError, "Cannot set bit offset for non bool '%s' variable (ID:%d,Idx:%x,sIdx:%x))" % (name,nodeid,index,subindex)
                 else:
                     numbit = None
                 
@@ -235,10 +249,12 @@
                     raise ValueError, "Invalid type for location \"%s\"" % name
                 
                 typeinfos = node.GetEntryInfos(locationtype)
-                DictLocations[name] = {"type":locationtype, "pdotype":SlavePDOType[prefix],
+                DictLocations[name] = {"type":locationtype, "pdotype":SlavePDOType[direction],
                                        "nodeid": nodeid, "index": index,"subindex": subindex, 
                                        "bit": numbit, "size": typeinfos["size"], "busname": busname, "sizelocation": sizelocation}
-                  
+            else:
+                raise ValueError, "Not PDO mappable variable : '%s' (ID:%d,Idx:%x,sIdx:%x))" % (name,nodeid,index,subindex)
+                
     # Create DictCobID with variables already mapped and add them in DictValidLocations
     for name, locationinfos in DictLocations.items():
         node = nodelist.SlaveNodes[locationinfos["nodeid"]]["Node"]