etherlab/etherlab.py
changeset 2097 58d07e039896
parent 2096 c9b0340ea0f5
child 2098 392791b5cc04
--- a/etherlab/etherlab.py	Thu Feb 07 00:59:50 2013 +0100
+++ b/etherlab/etherlab.py	Wed Feb 27 22:40:45 2013 +0100
@@ -3,12 +3,13 @@
 from xml.dom import minidom
 
 import wx
+import csv
 
 from xmlclass import *
 from POULibrary import POULibrary
 from ConfigTreeNode import ConfigTreeNode
 from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
-from ConfigEditor import NodeEditor, CIA402NodeEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
+from ConfigEditor import NodeEditor, CIA402NodeEditor, LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
 
 try:
     from MotionLibrary import Headers, AxisXSD
@@ -571,7 +572,7 @@
                         "product_code": slave["product_code"],
                         "revision_number":slave["revision_number"],
                     }
-                    device = self.GetModuleInfos(type_infos)
+                    device, alignment = self.GetModuleInfos(type_infos)
                     if device is not None:
                         if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
                             CTNType = "EthercatCIA402Slave"
@@ -651,7 +652,7 @@
         slave = self.GetSlave(slave_pos)
         if slave is not None:
             type_infos = slave.getType()
-            device = self.GetModuleInfos(type_infos)
+            device, alignement = self.GetModuleInfos(type_infos)
             if device is not None:
                 infos = type_infos.copy()
                 entries = device.GetEntriesList()
@@ -688,7 +689,7 @@
         if slave is not None:
             type_infos = slave.getType()
         
-            device = self.GetModuleInfos(type_infos)
+            device, alignement = self.GetModuleInfos(type_infos)
             if device is not None:
                 sync_managers = []
                 for sync_manager in device.getSm():
@@ -1016,7 +1017,7 @@
             slave_pos = (slave_alias, alias[slave_alias])
             
             # Extract slave device informations
-            device = self.Controler.GetModuleInfos(type_infos)
+            device, alignement = self.Controler.GetModuleInfos(type_infos)
             if device is not None:
                 
                 # Extract slaves variables to be mapped
@@ -1473,10 +1474,7 @@
                             "Type": subitem.getType(),
                             "BitSize": subitem.getBitSize(),
                             "Access": subitem_access, 
-                            "PDOMapping": subitem_pdomapping, 
-                            "PDO index": "", 
-                            "PDO name": "", 
-                            "PDO type": ""}
+                            "PDOMapping": subitem_pdomapping}
                 else:
                     entry_access = ""
                     entry_pdomapping = ""
@@ -1495,10 +1493,7 @@
                          "Type": entry_type,
                          "BitSize": object.getBitSize(),
                          "Access": entry_access,
-                         "PDOMapping": entry_pdomapping, 
-                         "PDO index": "", 
-                         "PDO name": "", 
-                         "PDO type": ""}
+                         "PDOMapping": entry_pdomapping}
         
         for TxPdo in self.getTxPdo():
             ExtractPdoInfos(TxPdo, "Transmit", entries)
@@ -1578,53 +1573,33 @@
                     "Name": ExtractName(pdo_entry.getName()),
                     "Type": entry_type.getcontent(),
                     "Access": access,
-                    "PDOMapping": pdomapping,
-                    "PDO index": pdo_index, 
-                    "PDO name": pdo_name, 
-                    "PDO type": pdo_type}
-
-class RootClass:
-    
-    CTNChildrenTypes = [("EthercatNode",_EthercatCTN,"Ethercat Master")]
-    
-    def __init__(self):
-        self.LoadModulesLibrary()
-    
-    def GetModulesLibraryPath(self):
-        library_path = os.path.join(self.CTNPath(), "modules")
-        if not os.path.exists(library_path):
-            os.mkdir(library_path)
-        return library_path
-    
-    def _ImportModuleLibrary(self):
-        dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose an XML file"), os.getcwd(), "",  _("XML files (*.xml)|*.xml|All files|*.*"), wx.OPEN)
-        if dialog.ShowModal() == wx.ID_OK:
-            filepath = dialog.GetPath()
-            if os.path.isfile(filepath):
-                shutil.copy(filepath, self.GetModulesLibraryPath())
-                self.LoadModulesLibrary()
-            else:
-                self.GetCTRoot().logger.write_error(_("No such XML file: %s\n") % filepath)
-        dialog.Destroy()  
-    
-    ConfNodeMethods = [
-        {"bitmap" : "ImportESI",
-         "name" : _("Import module library"), 
-         "tooltip" : _("Import module library"),
-         "method" : "_ImportModuleLibrary"},
-    ]
-    
-    def CTNGenerate_C(self, buildpath, locations):
-        return [],"",False
-    
-    def LoadModulesLibrary(self):
-        self.ModulesLibrary = {}
-        
-        library_path = self.GetModulesLibraryPath()
-        
-        files = os.listdir(library_path)
+                    "PDOMapping": pdomapping}
+
+DEFAULT_ALIGNMENT = 8
+
+class ModulesLibrary:
+    
+    def __init__(self, path, parent_library=None):
+        self.Path = path
+        if not os.path.exists(self.Path):
+            os.makedirs(self.Path)
+        self.ParentLibrary = parent_library
+        
+        self.LoadModules()
+        self.LoadAlignments()
+    
+    def GetPath(self):
+        return self.Path
+    
+    def GetAlignmentFilePath(self):
+        return os.path.join(self.Path, "alignments.cfg")
+    
+    def LoadModules(self):
+        self.Library = {}
+        
+        files = os.listdir(self.Path)
         for file in files:
-            filepath = os.path.join(library_path, file)
+            filepath = os.path.join(self.Path, file)
             if os.path.isfile(filepath) and os.path.splitext(filepath)[-1] == ".xml":
                 xmlfile = open(filepath, 'r')
                 xml_tree = minidom.parse(xmlfile)
@@ -1639,9 +1614,9 @@
                 if modules_infos is not None:
                     vendor = modules_infos.getVendor()
                     
-                    vendor_category = self.ModulesLibrary.setdefault(ExtractHexDecValue(vendor.getId()), 
-                                                                     {"name": ExtractName(vendor.getName(), _("Miscellaneous")), 
-                                                                      "groups": {}})
+                    vendor_category = self.Library.setdefault(ExtractHexDecValue(vendor.getId()), 
+                                                              {"name": ExtractName(vendor.getName(), _("Miscellaneous")), 
+                                                               "groups": {}})
                     
                     for group in modules_infos.getDescriptions().getGroups().getGroup():
                         group_type = group.getType()
@@ -1656,10 +1631,10 @@
                         if not vendor_category["groups"].has_key(device_group):
                             raise ValueError, "Not such group \"%\"" % device_group
                         vendor_category["groups"][device_group]["devices"].append((device.getType().getcontent(), device))
-    
+
     def GetModulesLibrary(self, profile_filter=None):
         library = []
-        for vendor_id, vendor in self.ModulesLibrary.iteritems():
+        for vendor_id, vendor in self.Library.iteritems():
             groups = []
             children_dict = {}
             for group_type, group in vendor["groups"].iteritems():
@@ -1671,12 +1646,16 @@
                 device_dict = {}
                 for device_type, device in group["devices"]:
                     if profile_filter is None or profile_filter in device.GetProfileNumbers():
+                        product_code = device.getType().getProductCode()
+                        revision_number = device.getType().getRevisionNo()
+                        alignment = self.GetAlignment(vendor_id, product_code, revision_number)
                         device_infos = {"name": ExtractName(device.getName()),
                                         "type": ETHERCAT_DEVICE,
                                         "infos": {"device_type": device_type,
                                                   "vendor": vendor_id,
-                                                  "product_code": device.getType().getProductCode(),
-                                                  "revision_number": device.getType().getRevisionNo()},
+                                                  "product_code": product_code,
+                                                  "revision_number": revision_number,
+                                                  "alignment": alignment},
                                         "children": []}
                         group_infos["children"].append(device_infos)
                         device_type_occurrences = device_dict.setdefault(device_type, [])
@@ -1698,17 +1677,115 @@
                                 "children": groups})
         library.sort(lambda x, y: cmp(x["name"], y["name"]))
         return library
-    
-    def GetModuleInfos(self, type_infos):
-        vendor = self.ModulesLibrary.get(ExtractHexDecValue(type_infos["vendor"]), None)
-        if vendor is not None:
-            for group_name, group in vendor["groups"].iteritems():
-                for device_type, device in group["devices"]:
-                    product_code = ExtractHexDecValue(device.getType().getProductCode())
-                    revision_number = ExtractHexDecValue(device.getType().getRevisionNo())
-                    if (product_code == ExtractHexDecValue(type_infos["product_code"]) and
-                        revision_number == ExtractHexDecValue(type_infos["revision_number"])):
-                        return device
-        return None
+
+    def GetModuleInfos(self, module_infos):
+        vendor = ExtractHexDecValue(module_infos["vendor"])
+        vendor_infos = self.Library.get(vendor)
+        if vendor_infos is not None:
+            for group_name, group_infos in vendor_infos["groups"].iteritems():
+                for device_type, device_infos in group_infos["devices"]:
+                    product_code = ExtractHexDecValue(device_infos.getType().getProductCode())
+                    revision_number = ExtractHexDecValue(device_infos.getType().getRevisionNo())
+                    if (product_code == ExtractHexDecValue(module_infos["product_code"]) and
+                        revision_number == ExtractHexDecValue(module_infos["revision_number"])):
+                        return device_infos, self.GetAlignment(vendor, product_code, revision_number)
+        return None, None
+    
+    def ImportModuleLibrary(self, filepath):
+        if os.path.isfile(filepath):
+            shutil.copy(filepath, self.Path)
+            self.LoadModules()
+            return True
+        return False
+    
+    def LoadAlignments(self):
+        self.Alignments = {}
+        
+        csvfile_path = self.GetAlignmentFilePath()
+        if os.path.exists(csvfile_path):
+            csvfile = open(csvfile_path, "rb")
+            sample = csvfile.read(1024)
+            csvfile.seek(0)
+            dialect = csv.Sniffer().sniff(sample)
+            has_header = csv.Sniffer().has_header(sample)
+            reader = csv.reader(csvfile, dialect)
+            for row in reader:
+                if has_header:
+                    has_header = False
+                else:
+                    try:
+                        self.Alignments[tuple(map(int, row[:3]))] = row[3]
+                    except:
+                        pass
+            csvfile.close()
+        
+    def SaveAlignments(self):
+        csvfile = open(self.GetAlignmentFilePath(), "wb")
+        writer = csv.writer(csvfile, delimiter=';')
+        writer.writerow(['Vendor', 'product_code', 'revision_number', 'alignment'])
+        for (vendor, product_code, revision_number), alignment in self.Alignments.iteritems():
+            writer.writerow([vendor, product_code, revision_number, alignment])
+        csvfile.close()
+    
+    def SetAlignment(self, vendor, product_code, revision_number, alignment):
+        vendor = ExtractHexDecValue(vendor)
+        product_code = ExtractHexDecValue(product_code)
+        revision_number = ExtractHexDecValue(revision_number)
+        
+        self.Alignments[tuple([vendor, product_code, revision_number])] = alignment
+        self.SaveAlignments()
+    
+    def GetAlignment(self, vendor, product_code, revision_number):
+        vendor = ExtractHexDecValue(vendor)
+        product_code = ExtractHexDecValue(product_code)
+        revision_number = ExtractHexDecValue(revision_number)
+        
+        alignment = self.Alignments.get(tuple([vendor, product_code, revision_number]))
+        if alignment is not None:
+            return alignment
+        
+        if self.ParentLibrary is not None:
+            return self.ParentLibrary.GetAlignment(vendor, product_code, revision_number)
+        return DEFAULT_ALIGNMENT
+
+USERDATA_DIR = wx.StandardPaths.Get().GetUserDataDir()
+if wx.Platform != '__WXMSW__':
+    USERDATA_DIR += '_files'
+
+ModulesDatabase = ModulesLibrary(
+    os.path.join(USERDATA_DIR, "ethercat_modules"))
+
+class RootClass:
+    
+    CTNChildrenTypes = [("EthercatNode",_EthercatCTN,"Ethercat Master")]
+    EditorType = LibraryEditor
+    
+    def __init__(self):
+        self.ModulesLibrary = None
+        self.LoadModulesLibrary()
+    
+    def GetModulesLibraryPath(self):
+        return os.path.join(self.CTNPath(), "modules") 
+    
+    def CTNGenerate_C(self, buildpath, locations):
+        return [],"",False
+    
+    def LoadModulesLibrary(self):
+        if self.ModulesLibrary is None:
+            self.ModulesLibrary = ModulesLibrary(self.GetModulesLibraryPath(), ModulesDatabase)
+        else:
+            self.ModulesLibrary.LoadModulesLibrary()
+    
+    def GetModulesDatabaseInstance(self):
+        return ModulesDatabase
+    
+    def GetModulesLibraryInstance(self):
+        return self.ModulesLibrary
+    
+    def GetModulesLibrary(self, profile_filter=None):
+        return self.ModulesLibrary.GetModulesLibrary(profile_filter)
+    
+    def GetModuleInfos(self, module_infos):
+        return self.ModulesLibrary.GetModuleInfos(module_infos)