etherlab/ConfigEditor.py
changeset 2041 ce3727171207
parent 2040 d676082c1d2f
child 2043 27d4cb689a79
--- a/etherlab/ConfigEditor.py	Sun Mar 11 21:57:00 2012 +0100
+++ b/etherlab/ConfigEditor.py	Sun Mar 18 23:57:32 2012 +0100
@@ -4,32 +4,6 @@
 
 from controls import CustomGrid, CustomTable, EditorPanel
 
-SCAN_COMMAND = """
-import commands
-result = commands.getoutput("ethercat slaves")
-slaves = []
-for slave_line in result.splitlines():
-    chunks = slave_line.split()
-    idx, pos, state, flag = chunks[:4]
-    name = " ".join(chunks[4:])
-    alias, position = pos.split(":")
-    slave = {"idx": int(idx),
-             "alias": int(alias),
-             "position": int(position),
-             "name": name}
-    details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"])
-    for details_line in details.splitlines():
-        details_line = details_line.strip()
-        for header, param in [("Vendor Id:", "vendor_id"),
-                              ("Product code:", "product_code"),
-                              ("Revision number:", "revision_number")]:
-            if details_line.startswith(header):
-                slave[param] = int(details_line.split()[-1], 16)
-                break
-    slaves.append(slave)
-returnVal = slaves
-"""
-
 [ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE] = range(3)
 
 def AppendMenu(parent, help, id, kind, text):
@@ -38,91 +12,6 @@
     else:
         parent.Append(helpString=help, id=id, kind=kind, item=text)
 
-[ID_SLAVETYPECHOICEDIALOG, ID_SLAVETYPECHOICEDIALOGSTATICTEXT1,
- ID_SLAVETYPECHOICEDIALOGSLAVETYPESLIBRARY
-] = [wx.NewId() for _init_ctrls in range(3)]
-
-class SlaveTypeChoiceDialog(wx.Dialog):
-    
-    if wx.VERSION < (2, 6, 0):
-        def Bind(self, event, function, id = None):
-            if id is not None:
-                event(self, id, function)
-            else:
-                event(self, function)
-    
-    def _init_coll_flexGridSizer1_Items(self, parent):
-        parent.AddWindow(self.staticText1, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
-        parent.AddWindow(self.SlaveTypesLibrary, 0, border=20, flag=wx.GROW|wx.LEFT|wx.RIGHT)
-        parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
-    
-    def _init_coll_flexGridSizer1_Growables(self, parent):
-        parent.AddGrowableCol(0)
-        parent.AddGrowableRow(1)
-    
-    def _init_sizers(self):
-        self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
-        
-        self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
-        self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
-        
-        self.SetSizer(self.flexGridSizer1)
-
-    def _init_ctrls(self, prnt):
-        wx.Dialog.__init__(self, id=ID_SLAVETYPECHOICEDIALOG,
-              name='SlaveTypeChoiceDialog', parent=prnt,
-              size=wx.Size(600, 400), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
-              title=_('Browse slave types library'))
-        self.SetClientSize(wx.Size(600, 400))
-
-        self.staticText1 = wx.StaticText(id=ID_SLAVETYPECHOICEDIALOGSTATICTEXT1,
-              label=_('Choose a slave type:'), name='staticText1', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.SlaveTypesLibrary = wx.TreeCtrl(id=ID_SLAVETYPECHOICEDIALOGSLAVETYPESLIBRARY,
-              name='TypeTree', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 0), style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT)
-        
-        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
-        if wx.VERSION >= (2, 5, 0):
-            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
-        else:
-            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
-        
-        self._init_sizers()
-
-    def __init__(self, parent, controler, default=None):
-        self._init_ctrls(parent)
-        
-        slaves_types = controler.GetSlaveTypesLibrary()
-        
-        root = self.SlaveTypesLibrary.AddRoot("")
-        self.GenerateSlaveTypesLibraryTreeBranch(root, slaves_types, default)
-
-    def GenerateSlaveTypesLibraryTreeBranch(self, root, children, default):
-        for infos in children:
-            item = self.SlaveTypesLibrary.AppendItem(root, infos["name"])
-            if infos["type"] == ETHERCAT_DEVICE:
-                self.SlaveTypesLibrary.SetPyData(item, infos["infos"])
-                if infos["infos"] == default:
-                    self.SlaveTypesLibrary.SelectItem(item)
-                    self.SlaveTypesLibrary.EnsureVisible(item)
-            else:
-                self.GenerateSlaveTypesLibraryTreeBranch(item, infos["children"], default)
-
-    def GetType(self):
-        selected = self.SlaveTypesLibrary.GetSelection()
-        return self.SlaveTypesLibrary.GetPyData(selected)
-
-    def OnOK(self, event):
-        selected = self.SlaveTypesLibrary.GetSelection()
-        if not selected.IsOk() or self.SlaveTypesLibrary.GetPyData(selected) is None:
-            message = wx.MessageDialog(self, _("No valid slave type selected!"), _("Error"), wx.OK|wx.ICON_ERROR)
-            message.ShowModal()
-            message.Destroy()
-        else:
-            self.EndModal(wx.ID_OK)
-
 def GetSyncManagersTableColnames():
     _ = lambda x : x
     return ["#", _("Name"), _("Start Address"), _("Default Size"), _("Control Byte"), _("Enable")]
@@ -139,23 +28,18 @@
     _ = lambda x : x
     return ["#", _("Name"), _("Index"), _("SubIndex"), _("Type"), _("PDO index"), _("PDO name"), _("PDO type")]
 
-[ID_SLAVEINFOSPANEL, ID_SLAVEINFOSPANELVENDORLABEL, 
- ID_SLAVEINFOSPANELVENDOR, ID_SLAVEINFOSPANELPRODUCTCODELABEL, 
- ID_SLAVEINFOSPANELPRODUCTCODE, ID_SLAVEINFOSPANELREVISIONNUMBERLABEL, 
- ID_SLAVEINFOSPANELREVISIONNUMBER, ID_SLAVEINFOSPANELPHYSICSLABEL, 
- ID_SLAVEINFOSPANELPHYSICS, ID_SLAVEINFOSPANELSYNCMANAGERSLABEL, 
- ID_SLAVEINFOSPANELSYNCMANAGERSGRID, ID_SLAVEINFOSPANELVARIABLESLABEL, 
- ID_SLAVEINFOSPANELVARIABLESGRID, 
+[ID_NODEEDITOR, ID_NODEEDITORVENDORLABEL, 
+ ID_NODEEDITORVENDOR, ID_NODEEDITORPRODUCTCODELABEL, 
+ ID_NODEEDITORPRODUCTCODE, ID_NODEEDITORREVISIONNUMBERLABEL, 
+ ID_NODEEDITORREVISIONNUMBER, ID_NODEEDITORPHYSICSLABEL, 
+ ID_NODEEDITORPHYSICS, ID_NODEEDITORSYNCMANAGERSLABEL, 
+ ID_NODEEDITORSYNCMANAGERSGRID, ID_NODEEDITORVARIABLESLABEL, 
+ ID_NODEEDITORVARIABLESGRID, 
 ] = [wx.NewId() for _init_ctrls in range(13)]
 
-class SlaveInfosPanel(wx.Panel):
-    
-    if wx.VERSION < (2, 6, 0):
-        def Bind(self, event, function, id = None):
-            if id is not None:
-                event(self, id, function)
-            else:
-                event(self, function)
+class NodeEditor(EditorPanel):
+    
+    ID = ID_NODEEDITOR
     
     def _init_coll_MainSizer_Items(self, parent):
         parent.AddSizer(self.SlaveInfosDetailsSizer, 0, border=5, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
@@ -192,58 +76,58 @@
         self._init_coll_SlaveInfosDetailsSizer_Growables(self.SlaveInfosDetailsSizer)
         self._init_coll_SlaveInfosDetailsSizer_Items(self.SlaveInfosDetailsSizer)
         
-        self.SetSizer(self.MainSizer)
-
-    def _init_ctrls(self, prnt):
-        wx.Panel.__init__(self, id=ID_SLAVEINFOSPANEL, name='SlavePanel', parent=prnt,
+        self.Editor.SetSizer(self.MainSizer)
+
+    def _init_Editor(self, prnt):
+        self.Editor = wx.Panel(id=-1, name='SlavePanel', parent=prnt,
               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
         
-        self.VendorLabel = wx.StaticText(id=ID_SLAVEINFOSPANELVENDORLABEL,
-              label=_('Vendor:'), name='VendorLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.Vendor = wx.TextCtrl(id=ID_SLAVEINFOSPANELVENDOR, value='',
-              name='Vendor', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.TE_READONLY)
-        
-        self.ProductCodeLabel = wx.StaticText(id=ID_SLAVEINFOSPANELPRODUCTCODELABEL,
-              label=_('Product code:'), name='ProductCodeLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.ProductCode = wx.TextCtrl(id=ID_SLAVEINFOSPANELPRODUCTCODE, value='',
-              name='ProductCode', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.TE_READONLY)
-        
-        self.RevisionNumberLabel = wx.StaticText(id=ID_SLAVEINFOSPANELREVISIONNUMBERLABEL,
-              label=_('Revision number:'), name='RevisionNumberLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.RevisionNumber = wx.TextCtrl(id=ID_SLAVEINFOSPANELREVISIONNUMBER, value='',
-              name='RevisionNumber', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.TE_READONLY)
-        
-        self.PhysicsLabel = wx.StaticText(id=ID_SLAVEINFOSPANELPHYSICSLABEL,
-              label=_('Physics:'), name='PhysicsLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.Physics = wx.TextCtrl(id=ID_SLAVEINFOSPANELPHYSICS, value='',
-              name='Physics', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.TE_READONLY)
-        
-        self.SyncManagersLabel =  wx.StaticText(id=ID_SLAVEINFOSPANELSYNCMANAGERSLABEL,
-              label=_('Sync managers:'), name='SyncManagersLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.SyncManagersGrid = CustomGrid(id=ID_SLAVEINFOSPANELSYNCMANAGERSGRID,
-              name='SyncManagersGrid', parent=self, pos=wx.Point(0, 0), 
+        self.VendorLabel = wx.StaticText(id=ID_NODEEDITORVENDORLABEL,
+              label=_('Vendor:'), name='VendorLabel', parent=self.Editor,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        
+        self.Vendor = wx.TextCtrl(id=ID_NODEEDITORVENDOR, value='',
+              name='Vendor', parent=self.Editor, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TE_READONLY)
+        
+        self.ProductCodeLabel = wx.StaticText(id=ID_NODEEDITORPRODUCTCODELABEL,
+              label=_('Product code:'), name='ProductCodeLabel', parent=self.Editor,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        
+        self.ProductCode = wx.TextCtrl(id=ID_NODEEDITORPRODUCTCODE, value='',
+              name='ProductCode', parent=self.Editor, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TE_READONLY)
+        
+        self.RevisionNumberLabel = wx.StaticText(id=ID_NODEEDITORREVISIONNUMBERLABEL,
+              label=_('Revision number:'), name='RevisionNumberLabel', parent=self.Editor,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        
+        self.RevisionNumber = wx.TextCtrl(id=ID_NODEEDITORREVISIONNUMBER, value='',
+              name='RevisionNumber', parent=self.Editor, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TE_READONLY)
+        
+        self.PhysicsLabel = wx.StaticText(id=ID_NODEEDITORPHYSICSLABEL,
+              label=_('Physics:'), name='PhysicsLabel', parent=self.Editor,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        
+        self.Physics = wx.TextCtrl(id=ID_NODEEDITORPHYSICS, value='',
+              name='Physics', parent=self.Editor, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TE_READONLY)
+        
+        self.SyncManagersLabel =  wx.StaticText(id=ID_NODEEDITORSYNCMANAGERSLABEL,
+              label=_('Sync managers:'), name='SyncManagersLabel', parent=self.Editor,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        
+        self.SyncManagersGrid = CustomGrid(id=ID_NODEEDITORSYNCMANAGERSGRID,
+              name='SyncManagersGrid', parent=self.Editor, pos=wx.Point(0, 0), 
               size=wx.Size(0, 0), style=wx.VSCROLL)
         
-        self.VariablesLabel =  wx.StaticText(id=ID_SLAVEINFOSPANELVARIABLESLABEL,
+        self.VariablesLabel =  wx.StaticText(id=ID_NODEEDITORVARIABLESLABEL,
               label=_('Variable entries:'), name='VariablesLabel', parent=self,
               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
         
-        self.VariablesGrid = wx.gizmos.TreeListCtrl(id=ID_SLAVEINFOSPANELVARIABLESGRID,
-              name='VariablesGrid', parent=self, pos=wx.Point(0, 0), 
+        self.VariablesGrid = wx.gizmos.TreeListCtrl(id=ID_NODEEDITORVARIABLESGRID,
+              name='VariablesGrid', parent=self.Editor, pos=wx.Point(0, 0), 
               size=wx.Size(0, 0), style=wx.TR_DEFAULT_STYLE |
                                         wx.TR_ROW_LINES |
                                         wx.TR_COLUMN_LINES |
@@ -252,14 +136,10 @@
         self.VariablesGrid.GetMainWindow().Bind(wx.EVT_LEFT_DOWN, self.OnVariablesGridLeftClick)
                 
         self._init_sizers()
-        
-    def __init__(self, parent, controler):
-        self._init_ctrls(parent)
-        
-        self.Controler = controler
-        self.Slave = None
-        self.Type = None
-        
+    
+    def __init__(self, parent, controler, window):
+        EditorPanel.__init__(self, parent, "", window, controler)
+    
         self.SyncManagersTable = SyncManagersTable(self, [], GetSyncManagersTableColnames())
         self.SyncManagersGrid.SetTable(self.SyncManagersTable)
         self.SyncManagersGridColAlignements = [wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, 
@@ -281,10 +161,21 @@
             self.VariablesGrid.AddColumn(colname, colsize, colalign)
         self.VariablesGrid.SetMainColumn(1)
         
-    def SetSlaveInfos(self, slave_pos, slave_infos):
-        self.Slave = slave_pos
+        img = wx.Bitmap(self.Controler.GetIconPath("Cfile.png"), wx.BITMAP_TYPE_PNG).ConvertToImage()
+        self.SetIcon(wx.BitmapFromImage(img.Rescale(16, 16)))
+    
+    def __del__(self):
+        self.Controler.OnCloseEditor(self)
+    
+    def GetTitle(self):
+        return self.Controler.PlugFullName()
+    
+    def GetBufferState(self):
+        return False, False
+        
+    def RefreshView(self):
+        slave_infos = self.Controler.GetSlaveInfos()
         if slave_infos is not None:
-            self.Type = slave_infos["device_type"]
             self.Vendor.SetValue(slave_infos["vendor"])
             self.ProductCode.SetValue(slave_infos["product_code"])
             self.RevisionNumber.SetValue(slave_infos["revision_number"])
@@ -293,7 +184,6 @@
             self.SyncManagersTable.ResetView(self.SyncManagersGrid)
             self.RefreshVariablesGrid(slave_infos["entries"])
         else:
-            self.Type = None
             self.Vendor.SetValue("")
             self.ProductCode.SetValue("")
             self.RevisionNumber.SetValue("")
@@ -301,7 +191,7 @@
             self.SyncManagersTable.SetData([])
             self.SyncManagersTable.ResetView(self.SyncManagersGrid)
             self.RefreshVariablesGrid([])
-            
+    
     def RefreshVariablesGrid(self, entries):
         root = self.VariablesGrid.GetRootItem()
         if not root.IsOk():
@@ -317,7 +207,8 @@
         
         for entry in entries:
             idx += 1
-            if not item.IsOk():
+            create_new = not item.IsOk()
+            if create_new:
                 item = self.VariablesGrid.AppendItem(root, "")
             for col, colname in enumerate(colnames):
                 if col == 0:
@@ -327,9 +218,9 @@
             if entry["PDOMapping"] == "":
                 self.VariablesGrid.SetItemBackgroundColour(item, wx.LIGHT_GREY)
             self.VariablesGrid.SetItemPyData(item, entry)
+            if create_new and wx.Platform != '__WXMSW__':
+                item, root_cookie = self.VariablesGrid.GetNextChild(root, root_cookie)
             idx = self.GenerateVariablesGridBranch(item, entry["children"], colnames, idx)
-            if wx.Platform != '__WXMSW__':
-                item, root_cookie = self.VariablesGrid.GetNextChild(root, root_cookie)
             item, root_cookie = self.VariablesGrid.GetNextChild(root, root_cookie)
         
         to_delete = []
@@ -353,13 +244,13 @@
                 
                 entry_index = self.Controler.ExtractHexDecValue(entry.get("Index", "0"))
                 entry_subindex = self.Controler.ExtractHexDecValue(entry.get("SubIndex", "0"))
-                var_name = "%s_%4.4x_%2.2x" % (self.Type, entry_index, entry_subindex)
+                var_name = "%s_%4.4x_%2.2x" % (self.Controler.PlugName(), entry_index, entry_subindex)
                 if pdo_mapping == "R":
                     dir = "%I"
                 else:
                     dir = "%Q"
                 location = "%s%s" % (dir, self.Controler.GetSizeOfType(data_type)) + \
-                           ".".join(map(lambda x:str(x), self.Controler.GetCurrentLocation() + self.Slave + (entry_index, entry_subindex)))
+                           ".".join(map(lambda x:str(x), self.Controler.GetCurrentLocation() + (self.Controler.GetSlavePos(), entry_index, entry_subindex)))
                 
                 data = wx.TextDataObject(str((location, "location", data_type, var_name, "")))
                 dragSource = wx.DropSource(self.VariablesGrid)
@@ -368,339 +259,4 @@
             
         event.Skip()
 
-[ID_SLAVEPANEL, ID_SLAVEPANELTYPELABEL,
- ID_SLAVEPANELTYPE, ID_SLAVEPANELTYPEBROWSE, 
- ID_SLAVEPANELALIASLABEL, ID_SLAVEPANELALIAS, 
- ID_SLAVEPANELPOSLABEL, ID_SLAVEPANELPOS, 
- ID_SLAVEPANELSLAVEINFOSSTATICBOX, 
-] = [wx.NewId() for _init_ctrls in range(9)]
-
-class SlavePanel(wx.Panel):
-    
-    if wx.VERSION < (2, 6, 0):
-        def Bind(self, event, function, id = None):
-            if id is not None:
-                event(self, id, function)
-            else:
-                event(self, function)
-    
-    def _init_coll_MainSizer_Items(self, parent):
-        parent.AddSizer(self.PositionSizer, 0, border=5, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
-        parent.AddSizer(self.SlaveInfosBoxSizer, 0, border=5, flag=wx.GROW|wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT)
-    
-    def _init_coll_MainSizer_Growables(self, parent):
-        parent.AddGrowableCol(0)
-        parent.AddGrowableRow(1)
-    
-    def _init_coll_PositionSizer_Items(self, parent):
-        parent.AddWindow(self.TypeLabel, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
-        parent.AddSizer(self.TypeSizer, 0, border=0, flag=wx.GROW)
-        parent.AddWindow(self.AliasLabel, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
-        parent.AddWindow(self.Alias, 0, border=0, flag=wx.GROW)
-        parent.AddWindow(self.PosLabel, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
-        parent.AddWindow(self.Pos, 0, border=0, flag=wx.GROW)
-    
-    def _init_coll_PositionSizer_Growables(self, parent):
-        parent.AddGrowableCol(1)
-        parent.AddGrowableCol(3)
-        parent.AddGrowableCol(5)
-        parent.AddGrowableRow(0)
-    
-    def _init_coll_TypeSizer_Items(self, parent):
-        parent.AddWindow(self.Type, 1, border=0, flag=0)
-        parent.AddWindow(self.TypeBrowse, 0, border=0, flag=0)
-    
-    def _init_coll_SlaveInfosBoxSizer_Items(self, parent):
-        parent.AddWindow(self.SlaveInfosPanel, 1, border=0, flag=wx.GROW)
-    
-    def _init_sizers(self):
-        self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
-        self.PositionSizer = wx.FlexGridSizer(cols=6, hgap=5, rows=1, vgap=0)
-        self.TypeSizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.SlaveInfosBoxSizer = wx.StaticBoxSizer(self.SlaveInfosStaticBox, wx.VERTICAL)
-        
-        self._init_coll_MainSizer_Growables(self.MainSizer)
-        self._init_coll_MainSizer_Items(self.MainSizer)
-        self._init_coll_PositionSizer_Growables(self.PositionSizer)
-        self._init_coll_PositionSizer_Items(self.PositionSizer)
-        self._init_coll_TypeSizer_Items(self.TypeSizer)
-        self._init_coll_SlaveInfosBoxSizer_Items(self.SlaveInfosBoxSizer)
-        
-        self.SetSizer(self.MainSizer)
-    
-    def _init_ctrls(self, prnt):
-        wx.Panel.__init__(self, id=ID_SLAVEPANEL, name='SlavePanel', parent=prnt,
-              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
-        
-        self.TypeLabel = wx.StaticText(id=ID_SLAVEPANELTYPELABEL,
-              label=_('Type:'), name='TypeLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.Type = wx.TextCtrl(id=ID_SLAVEPANELTYPE, value='',
-              name='Type', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.TE_READONLY)
-        
-        self.TypeBrowse = wx.Button(id=ID_SLAVEPANELTYPEBROWSE, label='...',
-              name='TypeBrowse', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(30, 24), style=0)
-        self.Bind(wx.EVT_BUTTON, self.OnTypeBrowseClick, id=ID_SLAVEPANELTYPEBROWSE)
-        
-        self.AliasLabel = wx.StaticText(id=ID_SLAVEPANELALIASLABEL,
-              label=_('Alias:'), name='AliasLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.Alias = wx.SpinCtrl(id=ID_SLAVEPANELALIAS,
-              name='Alias', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.SP_ARROW_KEYS, min=0, max=0xffff)
-        self.Bind(wx.EVT_SPINCTRL, self.OnAliasChanged, id=ID_SLAVEPANELALIAS)
-        
-        self.PosLabel = wx.StaticText(id=ID_SLAVEPANELPOSLABEL,
-              label=_('Position:'), name='PositionLabel', parent=self,
-              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
-        
-        self.Pos = wx.SpinCtrl(id=ID_SLAVEPANELPOS,
-              name='Pos', parent=self, pos=wx.Point(0, 0),
-              size=wx.Size(0, 24), style=wx.SP_ARROW_KEYS, min=0, max=0xffff)
-        self.Bind(wx.EVT_SPINCTRL, self.OnPositionChanged, id=ID_SLAVEPANELPOS)
-        
-        self.SlaveInfosStaticBox = wx.StaticBox(id=ID_SLAVEPANELSLAVEINFOSSTATICBOX,
-              label=_('Slave infos:'), name='SlaveInfosStaticBox', parent=self,
-              pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0)
-        
-        self.SlaveInfosPanel = SlaveInfosPanel(self, self.Controler)
-        
-        self._init_sizers()
-    
-    def __init__(self, parent, controler, window, slave):
-        self.Controler = controler
-        self.ParentWindow = window
-        self.Slave = slave
-        
-        self._init_ctrls(parent)
-        
-        self.RefreshView()
-    
-    def GetSlaveTitle(self):
-        type_infos = self.Controler.GetSlaveType(self.Slave)
-        return "%s (%d:%d)" % (type_infos["device_type"], self.Slave[0], self.Slave[1])
-    
-    def GetSlave(self):
-        return self.Slave
-    
-    def SetSlave(self, slave):
-        if self.Slave != slave:
-            self.Slave = slave
-            self.RefreshView()
-
-    def RefreshView(self):
-        self.Alias.SetValue(self.Slave[0])
-        self.Pos.SetValue(self.Slave[1])
-        slave_infos = self.Controler.GetSlaveInfos(self.Slave)
-        if slave_infos is not None:
-            self.Type.SetValue(slave_infos["device_type"])
-        else:
-            type_infos = self.Controler.GetSlaveType(self.Slave)
-            self.Type.SetValue(type_infos["device_type"])
-        self.SlaveInfosPanel.SetSlaveInfos(self.Slave, slave_infos)
-        
-    def OnAliasChanged(self, event):
-        alias = self.Alias.GetValue()
-        if alias != self.Slave[0]:
-            result = self.Controler.SetSlavePos(self.Slave[:2], alias = alias)
-            if result is not None:
-                message = wx.MessageDialog(self, result, _("Error"), wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
-            else:
-                wx.CallAfter(self.ParentWindow.RefreshView, (alias, self.Slave[1]))
-                wx.CallAfter(self.ParentWindow.RefreshParentWindow)
-        event.Skip()
-        
-    def OnPositionChanged(self, event):
-        position = self.Pos.GetValue()
-        if position != self.Slave[1]:
-            result = self.Controler.SetSlavePos(self.Slave, position = position)
-            if result is not None:
-                message = wx.MessageDialog(self, result, _("Error"), wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
-            else:
-                wx.CallAfter(self.ParentWindow.RefreshView, (self.Slave[0], position))
-                wx.CallAfter(self.ParentWindow.RefreshParentWindow)
-        event.Skip()
-
-    def OnTypeBrowseClick(self, event):
-        dialog = SlaveTypeChoiceDialog(self, self.Controler, self.Controler.GetSlaveType(self.Slave))
-        if dialog.ShowModal() == wx.ID_OK:
-            result = self.Controler.SetSlaveType(self.Slave, dialog.GetType())
-            if result is not None:
-                message = wx.MessageDialog(self, result, _("Error"), wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
-            else:
-                wx.CallAfter(self.RefreshView)
-                wx.CallAfter(self.ParentWindow.RefreshSlaveNodesTitles)
-                wx.CallAfter(self.ParentWindow.RefreshParentWindow)
-        dialog.Destroy()
-        event.Skip()
-
-[ID_CONFIGEDITOR, ID_CONFIGEDITORSLAVENODES,
-] = [wx.NewId() for _init_ctrls in range(2)]
-
-[ID_CONFIGEDITORPLUGINMENUSCANNETWORK, ID_CONFIGEDITORPLUGINMENUADDSLAVE, 
- ID_CONFIGEDITORPLUGINMENUDELETESLAVE,
-] = [wx.NewId() for _init_coll_PluginMenu_Items in range(3)]
-
-class ConfigEditor(EditorPanel):
-    
-    ID = ID_CONFIGEDITOR
-    
-    def _init_coll_MainSizer_Items(self, parent):
-        parent.AddWindow(self.SlaveNodes, 0, border=5, flag=wx.GROW|wx.ALL)
-
-    def _init_coll_MainSizer_Growables(self, parent):
-        parent.AddGrowableCol(0)
-        parent.AddGrowableRow(0)
-    
-    def _init_sizers(self):
-        self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=1, vgap=0)
-        
-        self._init_coll_MainSizer_Items(self.MainSizer)
-        self._init_coll_MainSizer_Growables(self.MainSizer)
-        
-        self.Editor.SetSizer(self.MainSizer)
-    
-    def _init_Editor(self, prnt):
-        self.Editor = wx.Panel(id=-1, parent=prnt, pos=wx.Point(0, 0), 
-                size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
-        
-        self.SlaveNodes = wx.Notebook(id=ID_CONFIGEDITORSLAVENODES,
-              name='SlaveNodes', parent=self.Editor, pos=wx.Point(0, 0),
-              size=wx.Size(0, 0), style=wx.NB_LEFT)
-        
-        self._init_sizers()
-    
-    def _init_MenuItems(self):
-        self.MenuItems = [
-            (wx.ITEM_NORMAL, (_("Scan network"), ID_CONFIGEDITORPLUGINMENUSCANNETWORK, '', self.OnScanNetworkMenu)),
-            (wx.ITEM_SEPARATOR, None),
-            (wx.ITEM_NORMAL, (_("Add slave"), ID_CONFIGEDITORPLUGINMENUADDSLAVE, '', self.OnAddSlaveMenu)),
-            (wx.ITEM_NORMAL, (_("Delete slave"), ID_CONFIGEDITORPLUGINMENUDELETESLAVE, '', self.OnDeleteSlaveMenu)),
-        ]
-    
-    def __init__(self, parent, controler, window):
-        EditorPanel.__init__(self, parent, "", window, controler)
-        
-        img = wx.Bitmap(self.Controler.GetIconPath("Cfile.png"), wx.BITMAP_TYPE_PNG).ConvertToImage()
-        self.SetIcon(wx.BitmapFromImage(img.Rescale(16, 16)))
-    
-    def __del__(self):
-        self.Controler.OnCloseEditor(self)
-    
-    def GetTitle(self):
-        fullname = self.Controler.PlugFullName()
-        if not self.Controler.ConfigIsSaved():
-            return "~%s~" % fullname
-        return fullname
-    
-    def GetBufferState(self):
-        return self.Controler.GetBufferState()
-        
-    def Undo(self):
-        self.Controler.LoadPrevious()
-        self.RefreshView()
-            
-    def Redo(self):
-        self.Controler.LoadNext()
-        self.RefreshView()
-    
-    def RefreshView(self, slave_pos=None):
-        slaves = self.Controler.GetSlaves()
-        for i, slave in enumerate(slaves):
-            if i < self.SlaveNodes.GetPageCount():
-                panel = self.SlaveNodes.GetPage(i)
-                panel.SetSlave(slave)
-            else:
-                panel = SlavePanel(self.SlaveNodes, self.Controler, self, slave)
-                self.SlaveNodes.AddPage(panel, "")
-        while self.SlaveNodes.GetPageCount() > len(slaves):
-            self.SlaveNodes.RemovePage(len(slaves))
-        self.RefreshSlaveNodesTitles()
-        if slave_pos is not None:
-            self.SelectSlave(slave_pos)
-    
-    def RefreshParentWindow(self):
-        self.ParentWindow.RefreshTitle()
-        self.ParentWindow.RefreshFileMenu()
-        self.ParentWindow.RefreshEditMenu()
-        self.ParentWindow.RefreshPluginMenu()
-        self.ParentWindow.RefreshPageTitles()
-    
-    def RefreshSlaveNodesTitles(self):
-        for idx in xrange(self.SlaveNodes.GetPageCount()):
-            panel = self.SlaveNodes.GetPage(idx)
-            self.SlaveNodes.SetPageText(idx, panel.GetSlaveTitle())
-            
-    def RefreshPluginMenu(self, plugin_menu):
-        plugin_menu.Enable(ID_CONFIGEDITORPLUGINMENUDELETESLAVE, 
-                           self.SlaveNodes.GetPageCount() > 0)
-    
-    def SelectSlave(self, slave):
-        for idx in xrange(self.SlaveNodes.GetPageCount()):
-            panel = self.SlaveNodes.GetPage(idx)
-            if panel.GetSlave() == slave:
-                self.SlaveNodes.SetSelection(idx)
-                return
-    
-    def OnScanNetworkMenu(self, event):
-        error, returnVal = self.Controler.RemoteExec(SCAN_COMMAND, returnVal = None)
-        if error != 0:
-            dialog = wx.MessageDialog(self, returnVal, "Error", wx.OK|wx.ICON_ERROR)
-            dialog.ShowModal()
-            dialog.Destroy()
-        elif returnVal is not None:
-            print returnVal
-            wx.CallAfter(self.RefreshView)
-
-    def OnAddSlaveMenu(self, event):
-        slave = self.Controler.AddSlave()
-        self.RefreshParentWindow()
-        wx.CallAfter(self.RefreshView, slave)
-        
-    def OnDeleteSlaveMenu(self, event):
-        selected = self.SlaveNodes.GetSelection()
-        if selected != -1:
-            panel = self.SlaveNodes.GetPage(selected)
-            if self.Controler.RemoveSlave(panel.GetSlave()[:2]):
-                self.RefreshParentWindow()
-                wx.CallAfter(self.RefreshView)
-
-
-[ID_DS402NODEEDITOR,
-] = [wx.NewId() for _init_ctrls in range(1)]
-
-class DS402NodeEditor(EditorPanel):
-    
-    ID = ID_DS402NODEEDITOR
-    
-    def _init_Editor(self, prnt):
-        self.Editor = SlaveInfosPanel(prnt, self.Controler)
-        
-    def __init__(self, parent, controler, window):
-        EditorPanel.__init__(self, parent, "", window, controler)
-        
-        img = wx.Bitmap(self.Controler.GetIconPath("Cfile.png"), wx.BITMAP_TYPE_PNG).ConvertToImage()
-        self.SetIcon(wx.BitmapFromImage(img.Rescale(16, 16)))
-    
-    def __del__(self):
-        self.Controler.OnCloseEditor(self)
-    
-    def GetTitle(self):
-        return self.Controler.PlugFullName()
-    
-    def GetBufferState(self):
-        return False, False
-        
-    def RefreshView(self):
-        self.Editor.SetSlaveInfos(self.Controler.GetSlavePos(), self.Controler.GetSlaveInfos())
-        
\ No newline at end of file
+DS402NodeEditor = NodeEditor