Preliminary CANopen slave generation support
authoretisserant
Tue, 24 Jun 2008 19:02:11 +0200
changeset 166 121b18748de0
parent 165 f9c6c9e36725
child 167 648449a9d778
Preliminary CANopen slave generation support
plugins/canfestival/canfestival.py
plugins/canfestival/config_utils.py
--- a/plugins/canfestival/canfestival.py	Mon Jun 23 18:22:40 2008 +0200
+++ b/plugins/canfestival/canfestival.py	Tue Jun 24 19:02:11 2008 +0200
@@ -19,17 +19,10 @@
 #                    SLAVE
 #--------------------------------------------------
 
-class _NodeEdit(objdictedit):
-    " Overload some of CanFestival Node Editor methods "
-    def OnCloseFrame(self, event):
-        " Do reset _NodeListPlug.View when closed"
-        self._onclose()
-        event.Skip()
-
 class _SlavePlug(NodeManager):
     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-      <xsd:element name="CanFestivalNode">
+      <xsd:element name="CanFestivalSlaveNode">
         <xsd:complexType>
           <xsd:attribute name="CAN_Device" type="xsd:string" use="required"/>
           <xsd:attribute name="CAN_Baudrate" type="xsd:string" use="required"/>
@@ -40,7 +33,7 @@
     """
 
     def GetSlaveODPath(self):
-        os.path.join(self.PlugPath(), 'slave.od')
+        return os.path.join(self.PlugPath(), 'slave.od')
 
     def __init__(self):
         # TODO change netname when name change
@@ -65,36 +58,18 @@
                 self._View = None
             def _onsave():
                 self.GetPlugRoot().SaveProject()
-            self._View = _NodeEdit(self.GetPlugRoot().AppFrame, self)
+            self._View = objdictedit(self.GetPlugRoot().AppFrame, self)
             # TODO redefine BusId when IEC channel change
             self._View.SetBusId(self.GetCurrentLocation())
             self._View._onclose = _onclose
             self._View._onsave = _onsave
             self._View.Show()
 
-    def _ShowMasterGenerated(self, logger):
-        buildpath = self._getBuildPath()
-        # Eventually create build dir
-        if not os.path.exists(buildpath):
-            logger.write_error("Error: No PLC built\n")
-            return
-        
-        masterpath = os.path.join(buildpath, "MasterGenerated.od")
-        if not os.path.exists(masterpath):
-            logger.write_error("Error: No Master generated\n")
-            return
-        
-        new_dialog = objdictedit(None, [masterpath])
-        new_dialog.Show()
-
     PluginMethods = [
         {"bitmap" : os.path.join("images", "NetworkEdit"),
-         "name" : "Edit network", 
-         "tooltip" : "Edit CanOpen Network with NetworkEdit",
+         "name" : "Edit slave", 
+         "tooltip" : "Edit CanOpen slave with ObjdictEdit",
          "method" : "_OpenView"},
-        {"name" : "Show Master", 
-         "tooltip" : "Show Master generated by config_utils",
-         "method" : "_ShowMasterGenerated"}
     ]
 
     def OnPlugClose(self):
@@ -102,12 +77,10 @@
             self._View.Close()
 
     def PlugTestModified(self):
-        return self.ChangesToSave or self.HasChanged()
+        return self.ChangesToSave or self.OneFileHasChanged()
         
     def OnPlugSave(self):
-        self.SetRoot(self.PlugPath())
-        self.SaveProject()
-        return True
+        return self.SaveCurrentInFile(self.GetSlaveODPath())
 
     def PlugGenerate_C(self, buildpath, locations, logger):
         """
@@ -124,31 +97,23 @@
         """
         current_location = self.GetCurrentLocation()
         # define a unique name for the generated C file
-        prefix = "_".join(map(lambda x:str(x), current_location))
+        prefix = "_".join(map(str, current_location))
         Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix )
-        # Create a new copy of the model with DCF loaded with PDO mappings for desired location
-        master = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(),"OD_%s"%prefix)
-        res = gen_cfile.GenerateFile(Gen_OD_path, master)
+        # Create a new copy of the model
+        slave = self.GetCurrentNodeCopy()
+        slave.SetNodeName("OD_%s"%prefix)
+        # allow access to local OD from Slave PLC
+        pointers = config_utils.LocalODPointers(locations, current_location, slave)
+        res = gen_cfile.GenerateFile(Gen_OD_path, slave, pointers)
         if res :
             raise Exception, res
-        
-        file = open(os.path.join(buildpath, "MasterGenerated.od"), "w")
-        dump(master, file)
-        file.close()
-        
+        self.ExportCurrentToEDSFile(os.path.join(buildpath, "Slave_%s.eds"%prefix))
         return [(Gen_OD_path,canfestival_config.getCFLAGS(CanFestivalPath))],"",False
 
 #--------------------------------------------------
 #                    MASTER
 #--------------------------------------------------
 
-class _NetworkEdit(networkedit):
-    " Overload some of CanFestival Network Editor methods "
-    def OnCloseFrame(self, event):
-        " Do reset _NodeListPlug.View when closed"
-        self._onclose()
-        event.Skip()
-
 class _NodeListPlug(NodeList):
     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
@@ -176,7 +141,7 @@
                 self._View = None
             def _onsave():
                 self.GetPlugRoot().SaveProject()
-            self._View = _NetworkEdit(self.GetPlugRoot().AppFrame, self)
+            self._View = networkedit(self.GetPlugRoot().AppFrame, self)
             # TODO redefine BusId when IEC channel change
             self._View.SetBusId(self.GetCurrentLocation())
             self._View._onclose = _onclose
@@ -217,8 +182,7 @@
         
     def OnPlugSave(self):
         self.SetRoot(self.PlugPath())
-        self.SaveProject()
-        return True
+        return self.SaveProject() is not None
 
     def PlugGenerate_C(self, buildpath, locations, logger):
         """
@@ -235,10 +199,13 @@
         """
         current_location = self.GetCurrentLocation()
         # define a unique name for the generated C file
-        prefix = "_".join(map(lambda x:str(x), current_location))
+        prefix = "_".join(map(str, current_location))
         Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix )
         # Create a new copy of the model with DCF loaded with PDO mappings for desired location
         master, pointers = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(),"OD_%s"%prefix)
+        # allow access to local OD from Master PLC
+        pointers.update(config_utils.LocalODPointers(locations, current_location, master))
+        # Do generate C file.
         res = gen_cfile.GenerateFile(Gen_OD_path, master, pointers)
         if res :
             raise Exception, res
@@ -288,22 +255,29 @@
         for child in self.IECSortedChilds():
             childlocstr = "_".join(map(str,child.GetCurrentLocation()))
             nodename = "OD_%s" % childlocstr
+            
+            # Try to get Slave Node
+            child_data = getattr(child, "CanFestivalSlaveNode", None)
+            if child_data is None:
+                # Not a slave -> master
+                child_data = getattr(child, "CanFestivalNode")
+                if child_data.getSync_TPDOs():
+                    format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n    '%(nodename)
+                    format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n    '%(nodename)
 
             format_dict["nodes_includes"] += '#include "%s.h"\n'%(nodename)
             format_dict["board_decls"] += 'BOARD_DECL(%s, "%s", "%s")\n'%(
                    nodename,
-                   child.CanFestivalNode.getCAN_Device(),
-                   child.CanFestivalNode.getCAN_Baudrate())
+                   child_data.getCAN_Device(),
+                   child_data.getCAN_Baudrate())
             format_dict["nodes_declare"] += 'NODE_DECLARE(%s, %s)\n    '%(
                    nodename,
-                   child.CanFestivalNode.getNodeId())
+                   child_data.getNodeId())
             format_dict["nodes_init"] += 'NODE_INIT(%s, %s)\n    '%(
                    nodename,
-                   child.CanFestivalNode.getNodeId())
+                   child_data.getNodeId())
             format_dict["nodes_open"] += 'NODE_OPEN(%s)\n    '%(nodename)
             format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n    '%(nodename)
-            format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n    '%(nodename)
-            format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n    '%(nodename)
         
         if sys.platform == 'win32':
             if self.CanFestivalInstance.getDebug_mode() and os.path.isfile(os.path.join("%s"%(format_dict["candriver"] + '_DEBUG.dll'))):
--- a/plugins/canfestival/config_utils.py	Mon Jun 23 18:22:40 2008 +0200
+++ b/plugins/canfestival/config_utils.py	Tue Jun 24 19:02:11 2008 +0200
@@ -341,8 +341,10 @@
                 # 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):
+                if len(loc) not in (2, 3, 4):
                     raise ValueError, "Bad location size : %s"%str(loc)
+                elif len(loc) == 2:
+                    continue
                 
                 direction = location["DIR"]
                 
@@ -374,9 +376,8 @@
                     else:
                         numbit = None
                     
-                    entryinfos = node.GetSubentryInfos(index, subindex)
-                    if location["IEC_TYPE"] != "BOOL" and entryinfos["type"] != COlocationtype:
-                        raise ValueError, "Invalid type \"%s\"-> %d != %d  for location\"%s\"" % (location["IEC_TYPE"], COlocationtype, entryinfos["type"] , name)
+                    if location["IEC_TYPE"] != "BOOL" and subentry_infos["type"] != COlocationtype:
+                        raise ValueError, "Invalid type \"%s\"-> %d != %d  for location\"%s\"" % (location["IEC_TYPE"], COlocationtype, subentry_infos["type"] , name)
                     
                     typeinfos = node.GetEntryInfos(COlocationtype)
                     self.IECLocations[name] = {"type":COlocationtype, "pdotype":SlavePDOType[direction],
@@ -584,6 +585,40 @@
     dcfgenerator.GenerateDCF(locations, current_location, sync_TPDOs)
     return dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables()
 
+def LocalODPointers(locations, current_location, slave):
+    IECLocations = {}
+    pointers = {}
+    for location in locations:
+        COlocationtype = IECToCOType[location["IEC_TYPE"]]
+        name = location["NAME"]
+        if name in IECLocations:
+            if IECLocations[name] != COlocationtype:
+                raise ValueError, "Conflict type for location \"%s\"" % name 
+        else:
+            # 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 (2, 3, 4):
+                raise ValueError, "Bad location size : %s"%str(loc)
+            elif len(loc) != 2:
+                continue
+            
+            # Extract and check nodeid
+            index, subindex = loc[:2]
+            
+            # Extract and check index and subindex
+            if not slave.IsEntry(index, subindex):
+                raise ValueError, "No such index/subindex (%x,%x) (variable %s)" % (index, subindex, name)
+            
+            # Get the entry info
+            subentry_infos = slave.GetSubentryInfos(index, subindex)    
+            if subentry_infos["type"] != COlocationtype:
+                raise ValueError, "Invalid type \"%s\"-> %d != %d  for location\"%s\"" % (location["IEC_TYPE"], COlocationtype, subentry_infos["type"] , name)
+            
+            IECLocations[name] = COlocationtype
+            pointers[(index, subindex)] = name
+    return pointers
+        
 if __name__ == "__main__":
     import os, sys, getopt