Defining all slaves as Etherlab master subplugin instead of editing them in an editor with vertical notebook
authorlaurent
Sun, 18 Mar 2012 23:57:32 +0100
changeset 2041 ce3727171207
parent 2040 d676082c1d2f
child 2042 563ccc918ded
Defining all slaves as Etherlab master subplugin instead of editing them in an editor with vertical notebook
ethercat_tests/wago_sanyo/MCL@motion/baseplugin.xml
ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/config.xml
ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/coupler@EthercatSlave/baseplugin.xml
ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/inputs@EthercatSlave/baseplugin.xml
ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/outputs@EthercatSlave/baseplugin.xml
ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/sanyo@EthercatDS402Slave/baseplugin.xml
ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/sanyo@EthercatDS402Slave/plugin.xml
ethercat_tests/wago_sanyo/plc.xml
etherlab/ConfigEditor.py
etherlab/etherlab.py
etherlab/plc_ds402node.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ethercat_tests/wago_sanyo/MCL@motion/baseplugin.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BaseParams Name="MCL" IEC_Channel="1"/>
--- a/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/config.xml	Sun Mar 11 21:57:00 2012 +0100
+++ b/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/config.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -23,8 +23,8 @@
     <Slave>
       <Info>
         <Name>EL1088</Name>
-        <PhysAddr>0</PhysAddr>
-        <AutoIncAddr>1</AutoIncAddr>
+        <PhysAddr>1</PhysAddr>
+        <AutoIncAddr>0</AutoIncAddr>
         <Physics></Physics>
         <VendorId>2</VendorId>
         <ProductCode>71315538</ProductCode>
@@ -35,8 +35,8 @@
     <Slave>
       <Info>
         <Name>EL2088</Name>
-        <PhysAddr>0</PhysAddr>
-        <AutoIncAddr>2</AutoIncAddr>
+        <PhysAddr>2</PhysAddr>
+        <AutoIncAddr>0</AutoIncAddr>
         <Physics></Physics>
         <VendorId>2</VendorId>
         <ProductCode>136851538</ProductCode>
@@ -44,5 +44,17 @@
         <SerialNo>0</SerialNo>
       </Info>
     </Slave>
+    <Slave>
+      <Info>
+        <Name>SanyoDenki RS2 EtherCAT</Name>
+        <PhysAddr>3</PhysAddr>
+        <AutoIncAddr>1</AutoIncAddr>
+        <Physics></Physics>
+        <VendorId>441</VendorId>
+        <ProductCode>2</ProductCode>
+        <RevisionNo>0</RevisionNo>
+        <SerialNo>0</SerialNo>
+      </Info>
+    </Slave>
   </Config>
 </EtherCATConfig>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/coupler@EthercatSlave/baseplugin.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BaseParams Name="coupler" IEC_Channel="0"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/inputs@EthercatSlave/baseplugin.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BaseParams Name="inputs" IEC_Channel="1"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/outputs@EthercatSlave/baseplugin.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BaseParams Name="outputs" IEC_Channel="2"/>
--- a/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/sanyo@EthercatDS402Slave/baseplugin.xml	Sun Mar 11 21:57:00 2012 +0100
+++ b/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/sanyo@EthercatDS402Slave/baseplugin.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<BaseParams Name="sanyo" IEC_Channel="1"/>
+<BaseParams Name="sanyo" IEC_Channel="3"/>
--- a/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/sanyo@EthercatDS402Slave/plugin.xml	Sun Mar 11 21:57:00 2012 +0100
+++ b/ethercat_tests/wago_sanyo/ethercat@etherlab/master@EthercatNode/sanyo@EthercatDS402Slave/plugin.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<EtherlabDS402Slave Node_Type="SanyoDenki RS2 EtherCAT"/>
+<DS402SlaveParams RatioNumerator="65536" RatioDenominator="180" PositionOffset="0"/>
--- a/ethercat_tests/wago_sanyo/plc.xml	Sun Mar 11 21:57:00 2012 +0100
+++ b/ethercat_tests/wago_sanyo/plc.xml	Sun Mar 18 23:57:32 2012 +0100
@@ -8,7 +8,7 @@
               productVersion="1.0"
               creationDateTime="2011-10-20T19:01:52"/>
   <contentHeader name="test_ethercat"
-                 modificationDateTime="2012-02-15T00:53:41">
+                 modificationDateTime="2012-03-18T23:04:07">
     <coordinateInfo>
       <fbd>
         <scaling x="0" y="0"/>
@@ -27,12 +27,12 @@
       <pou name="Prog" pouType="program">
         <interface>
           <localVars>
-            <variable name="CLOCK" address="%QX0.0.0.2.28672.1">
+            <variable name="CLOCK" address="%QX0.0.2.28672.1">
               <type>
                 <BOOL/>
               </type>
             </variable>
-            <variable name="LED" address="%QX0.0.0.2.28688.1">
+            <variable name="LED" address="%QX0.0.2.28688.1">
               <type>
                 <BOOL/>
               </type>
@@ -40,26 +40,36 @@
                 <simpleValue value="TRUE"/>
               </initialValue>
             </variable>
-            <variable name="ControlWord" address="%QW0.0.1.0.24640.0">
+            <variable name="ControlWord" address="%QW0.0.3.24640.0">
               <type>
                 <UINT/>
               </type>
             </variable>
-            <variable name="StatusWord" address="%IW0.0.1.0.24641.0">
+            <variable name="StatusWord" address="%IW0.0.3.24641.0">
               <type>
                 <UINT/>
               </type>
             </variable>
-            <variable name="ErrorCode" address="%IW0.0.1.0.24639.0">
+            <variable name="ErrorCode" address="%IW0.0.3.24639.0">
               <type>
                 <UINT/>
               </type>
             </variable>
-            <variable name="Mode" address="%IB0.0.1.0.24673.0">
+            <variable name="ModeDisplay" address="%IB0.0.3.24673.0">
               <type>
                 <SINT/>
               </type>
             </variable>
+            <variable name="ActualPosition" address="%ID0.0.3.24676.0">
+              <type>
+                <DINT/>
+              </type>
+            </variable>
+            <variable name="TargetPosition" address="%QD0.0.3.24698.0">
+              <type>
+                <DINT/>
+              </type>
+            </variable>
           </localVars>
         </interface>
         <body>
@@ -68,14 +78,353 @@
           </ST>
         </body>
       </pou>
+      <pou name="test_MCL" pouType="program">
+        <interface>
+          <localVars>
+            <variable name="CLOCK">
+              <type>
+                <UDINT/>
+              </type>
+            </variable>
+          </localVars>
+          <localVars>
+            <variable name="Axis" address="%IW0.0.3.0">
+              <type>
+                <INT/>
+              </type>
+            </variable>
+          </localVars>
+          <localVars>
+            <variable name="switch">
+              <type>
+                <derived name="MC_Power"/>
+              </type>
+            </variable>
+            <variable name="move1">
+              <type>
+                <derived name="MC_MoveAbsolute"/>
+              </type>
+            </variable>
+          </localVars>
+        </interface>
+        <body>
+          <FBD>
+            <inVariable localId="1" height="27" width="53">
+              <position x="45" y="36"/>
+              <connectionPointOut>
+                <relPosition x="53" y="13"/>
+              </connectionPointOut>
+              <expression>CLOCK</expression>
+            </inVariable>
+            <block localId="2" width="111" height="116" typeName="ADD">
+              <position x="149" y="5"/>
+              <inputVariables>
+                <variable formalParameter="IN1">
+                  <connectionPointIn>
+                    <relPosition x="0" y="44"/>
+                    <connection refLocalId="1">
+                      <position x="149" y="49"/>
+                      <position x="98" y="49"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="IN2">
+                  <connectionPointIn>
+                    <relPosition x="0" y="92"/>
+                    <connection refLocalId="3">
+                      <position x="149" y="97"/>
+                      <position x="98" y="97"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="OUT">
+                  <connectionPointOut>
+                    <relPosition x="111" y="44"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <inVariable localId="3" height="27" width="18">
+              <position x="80" y="84"/>
+              <connectionPointOut>
+                <relPosition x="18" y="13"/>
+              </connectionPointOut>
+              <expression>1</expression>
+            </inVariable>
+            <outVariable localId="4" height="27" width="53">
+              <position x="302" y="36"/>
+              <connectionPointIn>
+                <relPosition x="0" y="13"/>
+                <connection refLocalId="2" formalParameter="OUT">
+                  <position x="302" y="49"/>
+                  <position x="260" y="49"/>
+                </connection>
+              </connectionPointIn>
+              <expression>CLOCK</expression>
+            </outVariable>
+            <inVariable localId="5" height="27" width="53">
+              <position x="46" y="234"/>
+              <connectionPointOut>
+                <relPosition x="53" y="13"/>
+              </connectionPointOut>
+              <expression>CLOCK</expression>
+            </inVariable>
+            <inVariable localId="6" height="27" width="50">
+              <position x="49" y="268"/>
+              <connectionPointOut>
+                <relPosition x="50" y="13"/>
+              </connectionPointOut>
+              <expression>4000</expression>
+            </inVariable>
+            <block localId="7" width="99" height="89" typeName="GT">
+              <position x="155" y="210"/>
+              <inputVariables>
+                <variable formalParameter="IN1">
+                  <connectionPointIn>
+                    <relPosition x="0" y="37"/>
+                    <connection refLocalId="5">
+                      <position x="155" y="247"/>
+                      <position x="99" y="247"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="IN2">
+                  <connectionPointIn>
+                    <relPosition x="0" y="71"/>
+                    <connection refLocalId="6">
+                      <position x="155" y="281"/>
+                      <position x="99" y="281"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="OUT">
+                  <connectionPointOut>
+                    <relPosition x="99" y="37"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <block localId="8" width="159" height="204" typeName="MC_Power" instanceName="switch">
+              <position x="398" y="173"/>
+              <inputVariables>
+                <variable formalParameter="Axis">
+                  <connectionPointIn>
+                    <relPosition x="0" y="38"/>
+                    <connection refLocalId="9">
+                      <position x="398" y="211"/>
+                      <position x="344" y="211"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Enable">
+                  <connectionPointIn>
+                    <relPosition x="0" y="74"/>
+                    <connection refLocalId="7" formalParameter="OUT">
+                      <position x="398" y="247"/>
+                      <position x="254" y="247"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="EnablePositive">
+                  <connectionPointIn>
+                    <relPosition x="0" y="110"/>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="EnableNegative">
+                  <connectionPointIn>
+                    <relPosition x="0" y="146"/>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="Axis">
+                  <connectionPointOut>
+                    <relPosition x="159" y="38"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Status">
+                  <connectionPointOut>
+                    <relPosition x="159" y="74"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Valid">
+                  <connectionPointOut>
+                    <relPosition x="159" y="110"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Error">
+                  <connectionPointOut>
+                    <relPosition x="159" y="146"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="ErrorID">
+                  <connectionPointOut>
+                    <relPosition x="159" y="182"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <inVariable localId="9" height="27" width="36">
+              <position x="308" y="198"/>
+              <connectionPointOut>
+                <relPosition x="36" y="13"/>
+              </connectionPointOut>
+              <expression>Axis</expression>
+            </inVariable>
+            <block localId="10" width="252" height="389" typeName="MC_MoveAbsolute" instanceName="move1">
+              <position x="744" y="173"/>
+              <inputVariables>
+                <variable formalParameter="Axis">
+                  <connectionPointIn>
+                    <relPosition x="0" y="38"/>
+                    <connection refLocalId="8" formalParameter="Axis">
+                      <position x="744" y="211"/>
+                      <position x="557" y="211"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Execute">
+                  <connectionPointIn>
+                    <relPosition x="0" y="74"/>
+                    <connection refLocalId="8" formalParameter="Status">
+                      <position x="744" y="247"/>
+                      <position x="557" y="247"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="ContinuousUpdate">
+                  <connectionPointIn>
+                    <relPosition x="0" y="110"/>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Position">
+                  <connectionPointIn>
+                    <relPosition x="0" y="146"/>
+                    <connection refLocalId="11">
+                      <position x="744" y="319"/>
+                      <position x="672" y="319"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Velocity">
+                  <connectionPointIn>
+                    <relPosition x="0" y="182"/>
+                    <connection refLocalId="12">
+                      <position x="744" y="355"/>
+                      <position x="671" y="355"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Acceleration">
+                  <connectionPointIn>
+                    <relPosition x="0" y="218"/>
+                    <connection refLocalId="12">
+                      <position x="744" y="391"/>
+                      <position x="707" y="391"/>
+                      <position x="707" y="355"/>
+                      <position x="671" y="355"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Deceleration">
+                  <connectionPointIn>
+                    <relPosition x="0" y="254"/>
+                    <connection refLocalId="12">
+                      <position x="744" y="427"/>
+                      <position x="707" y="427"/>
+                      <position x="707" y="355"/>
+                      <position x="671" y="355"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Jerk">
+                  <connectionPointIn>
+                    <relPosition x="0" y="290"/>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="Direction">
+                  <connectionPointIn>
+                    <relPosition x="0" y="326"/>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="BufferMode">
+                  <connectionPointIn>
+                    <relPosition x="0" y="362"/>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="Axis">
+                  <connectionPointOut>
+                    <relPosition x="252" y="38"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Done">
+                  <connectionPointOut>
+                    <relPosition x="252" y="74"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Busy">
+                  <connectionPointOut>
+                    <relPosition x="252" y="110"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Active">
+                  <connectionPointOut>
+                    <relPosition x="252" y="146"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="CommandAborted">
+                  <connectionPointOut>
+                    <relPosition x="252" y="182"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="Error">
+                  <connectionPointOut>
+                    <relPosition x="252" y="218"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="ErrorID">
+                  <connectionPointOut>
+                    <relPosition x="252" y="254"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <inVariable localId="11" height="27" width="54">
+              <position x="630" y="306"/>
+              <connectionPointOut>
+                <relPosition x="54" y="13"/>
+              </connectionPointOut>
+              <expression>1800.0</expression>
+            </inVariable>
+            <inVariable localId="12" height="27" width="46">
+              <position x="637" y="342"/>
+              <connectionPointOut>
+                <relPosition x="46" y="13"/>
+              </connectionPointOut>
+              <expression>360.0</expression>
+            </inVariable>
+          </FBD>
+        </body>
+      </pou>
     </pous>
   </types>
   <instances>
     <configurations>
       <configuration name="conf">
         <resource name="res">
-          <task name="main_task" interval="T#0.4ms" priority="0">
+          <task name="main_task" interval="T#0.5ms" priority="0">
             <pouInstance name="main_instance" typeName="Prog"/>
+            <pouInstance name="test_MCL_instance" typeName="test_MCL"/>
           </task>
         </resource>
       </configuration>
--- 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
--- a/etherlab/etherlab.py	Sun Mar 11 21:57:00 2012 +0100
+++ b/etherlab/etherlab.py	Sun Mar 18 23:57:32 2012 +0100
@@ -7,118 +7,129 @@
 from xmlclass import *
 from plugger import PlugTemplate
 from PLCControler import UndoBuffer, LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
-from ConfigEditor import ConfigEditor, DS402NodeEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
+from ConfigEditor import NodeEditor, DS402NodeEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
+
+try:
+    from plugins.motion import Headers, AxisXSD
+    HAS_MCL = True
+except:
+    HAS_MCL = False
+
+
+TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L",
+    "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L", 
+    "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"}
+
+DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64",
+    "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64", 
+    "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"}
+
+VARCLASSCONVERSION = {"T": LOCATION_VAR_INPUT, "R": LOCATION_VAR_OUTPUT, "RT": LOCATION_VAR_MEMORY}
 
 #--------------------------------------------------
-#                 Ethercat DS402 Node
+#         Remote Exec Etherlab Commands
 #--------------------------------------------------
 
-NODE_VARIABLES = [
-    ("ControlWord", 0x6040, 0x00, "UINT", "Q"),
-    ("TargetPosition", 0x607a, 0x00, "DINT", "Q"),
-    ("StatusWord", 0x6041, 0x00, "UINT", "I"),
-    ("ModesOfOperationDisplay", 0x06061, 0x00, "SINT", "I"),
-    ("ActualPosition", 0x6064, 0x00, "DINT", "I"),
-    ("ErrorCode", 0x603f, 0x00, "UINT", "I"),
-]
-
-class _EthercatDS402SlavePlug:
-    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
-    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-      <xsd:element name="EtherlabDS402Slave">
-        <xsd:complexType>
-          <xsd:attribute name="Node_Type" type="xsd:string" use="optional"/>
-        </xsd:complexType>
-      </xsd:element>
-    </xsd:schema>
-    """
-    EditorType = DS402NodeEditor
+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 Node
+#--------------------------------------------------
+
+class _EthercatSlavePlug:
+
+    NODE_PROFILE = None
+    EditorType = NodeEditor
     
     def ExtractHexDecValue(self, value):
         return ExtractHexDecValue(value)
-
+    
     def GetSizeOfType(self, type):
         return TYPECONVERSION.get(self.GetPlugRoot().GetBaseType(type), None)
     
-    def _GetChildBySomething(self, something, toks):
-        return self
+    def GetSlavePos(self):
+        return self.BaseParams.getIEC_Channel()
     
     def GetParamsAttributes(self, path = None):
-        infos = PlugTemplate.GetParamsAttributes(self, path = None)
-        for element in infos:
-            if element["name"] == "EtherlabDS402Slave":
-                for child in element["children"]:
-                    if child["name"] == "Node_Type":
-                        child["type"] = [module[0] for module in self.PlugParent.GetModulesByProfile(402)]
-        return infos
-    
-    def GetAllChannels(self):
-        AllChannels = PlugTemplate.GetAllChannels(self)
-        for slave_pos in self.PlugParent.GetSlaves():
-            if slave_pos[0] not in AllChannels:
-                AllChannels.append(slave_pos[0])
-        AllChannels.sort()
-        return AllChannels
-
-    def GetCurrentLocation(self):
-        """
-        @return:  Tupple containing plugin IEC location of current plugin : %I0.0.4.5 => (0,0,4,5)
-        """
-        return self.PlugParent.GetCurrentLocation() + self.GetSlavePos()
-
-    def GetSlavePos(self):
-        return self.BaseParams.getIEC_Channel(), 0
-
-    def GetSlaveTypeInfos(self):
-        slave_type = self.EtherlabDS402Slave.getNode_Type()
-        
-        for module_type, vendor_id, product_code, revision_number in self.PlugParent.GetModulesByProfile(402):
-            if module_type == slave_type:
-                return {"device_type": module_type,
-                        "vendor": GenerateHexDecValue(vendor_id),
-                        "product_code": GenerateHexDecValue(product_code, 16),
-                        "revision_number": GenerateHexDecValue(revision_number, 16)}
-        
-        return None
+        if path:
+            parts = path.split(".", 1)
+            if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
+                return self.MandatoryParams[1].getElementInfos(parts[0], parts[1])
+            elif self.PlugParams and parts[0] == self.PlugParams[0]:
+                return self.PlugParams[1].getElementInfos(parts[0], parts[1])
+        else:
+            params = []
+            if wx.VERSION < (2, 8, 0) and self.MandatoryParams:
+                params.append(self.MandatoryParams[1].getElementInfos(self.MandatoryParams[0]))
+            slave_type = self.PlugParent.GetSlaveType(self.GetSlavePos())
+            params.append({
+                'use': 'required', 
+                'type': 'element', 
+                'name': 'SlaveParams', 
+                'value': None, 
+                'children': [{
+                    'use': 'optional', 
+                    'type': self.PlugParent.GetSlaveTypesLibrary(self.NODE_PROFILE), 
+                    'name': 'Type', 
+                    'value': (slave_type["device_type"], slave_type)}, 
+                   {'use': 'optional', 
+                    'type': 'unsignedLong', 
+                    'name': 'Alias', 
+                    'value': self.PlugParent.GetSlaveAlias(self.GetSlavePos())}]
+            })
+            if self.PlugParams:
+                params.append(self.PlugParams[1].getElementInfos(self.PlugParams[0]))
+            return params
+        
+    def SetParamsAttribute(self, path, value):
+        position = self.BaseParams.getIEC_Channel()
+        value, changed = PlugTemplate.SetParamsAttribute(self, path, value)
+        # Filter IEC_Channel, Slave_Type and Alias that have specific behavior
+        if path == "BaseParams.IEC_Channel":
+            self.PlugParent.SetSlavePosition(position, value)
+        elif path == "SlaveParams.Type":
+            self.PlugParent.SetSlaveType(position, value)
+            slave_type = self.PlugParent.GetSlaveType(self.GetSlavePos())
+            value = (slave_type["device_type"], slave_type)
+            changed = True
+        elif path == "SlaveParams.Alias":
+            self.PlugParent.SetSlaveAlias(position, value)
+            changed = True
+        return value, changed
 
     def GetSlaveInfos(self):
-        slave_typeinfos = self.GetSlaveTypeInfos()
-        if slave_typeinfos is not None:
-            device = self.PlugParent.GetModuleInfos(slave_typeinfos)
-            if device is not None:
-                infos = slave_typeinfos.copy()
-                entries = device.GetEntriesList()
-                entries_list = entries.items()
-                entries_list.sort()
-                entries = []
-                current_index = None
-                current_entry = None
-                for (index, subindex), entry in entries_list:
-                    entry["children"] = []
-                    if index != current_index:
-                        current_index = index
-                        current_entry = entry
-                        entries.append(entry)
-                    elif current_entry is not None:
-                        current_entry["children"].append(entry)
-                    else:
-                        entries.append(entry)
-                infos.update({"physics": device.getPhysics(),
-                              "sync_managers": device.GetSyncManagers(),
-                              "entries": entries})
-                return infos
-        return None
-
+        return self.PlugParent.GetSlaveInfos(self.GetSlavePos())
+    
     def GetVariableLocationTree(self):
-        slave_typeinfos = self.GetSlaveTypeInfos()
-        vars = []
-        if slave_typeinfos is not None:
-            vars = self.PlugParent.GetDeviceLocationTree(self.GetCurrentLocation(), slave_typeinfos) 
-
         return  {"name": self.BaseParams.getName(),
                  "type": LOCATION_PLUGIN,
                  "location": self.GetFullIEC_Channel(),
-                 "children": vars}
+                 "children": self.PlugParent.GetDeviceLocationTree(self.GetSlavePos(), self.GetCurrentLocation(), self.BaseParams.getName())
+        }
 
     PluginMethods = [
         {"bitmap" : os.path.join("images", "EditCfile"),
@@ -127,7 +138,6 @@
          "method" : "_OpenView"},
     ]
 
-
     def PlugGenerate_C(self, buildpath, locations):
         """
         Generate C code
@@ -141,79 +151,111 @@
             }, ...]
         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
         """
-        current_location = self.GetCurrentLocation()
-        
-        location_str = "_".join(map(lambda x:str(x), current_location))
-        
-        slave_pos = current_location[-2:]
-        
-        slave_typeinfos = self.GetSlaveTypeInfos()
-        device = None
-        if slave_typeinfos is not None:
-            device = self.PlugParent.GetModuleInfos(slave_typeinfos)
-        
-        if device is None:
-            raise (ValueError, 
-                   _("No information found for DS402 node \"%s\" at location %s!") % (
-                      slave_typeinfos["device_type"], ".".join(current_location)))
-            
-        self.PlugParent.FileGenerator.DeclareSlave(slave_pos, slave_typeinfos)
-        
-        plc_ds402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_ds402node.c")
-        plc_ds402node_file = open(plc_ds402node_filepath, 'r')
-        plc_ds402node_code = plc_ds402node_file.read()
-        plc_ds402node_file.close()
-        
-        from plugins.motion import Headers
-        
-        str_completion = {
-            "location": location_str,
-            "MCL_headers": Headers,
-            "extern_located_variables_declaration": [],
-            "entry_variables": [],
-            "init_entry_variables": [],
-        }
-        
-        for variable in NODE_VARIABLES:
-            var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable))
-            var_infos["location"] = location_str
-            var_infos["var_size"] = self.PlugParent.GetSizeOfType(var_infos["var_type"])
-            var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos
-            
-            str_completion["extern_located_variables_declaration"].append(
-                    "IEC_%(var_type)s *%(var_name)s;" % var_infos)
-            str_completion["entry_variables"].append(
-                    "    IEC_%(var_type)s *%(name)s;" % var_infos)
-            str_completion["init_entry_variables"].append(
-                    "    __DS402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos)
-            
-            self.PlugParent.FileGenerator.DeclareVariable(
-                    slave_pos, var_infos["index"], var_infos["subindex"], 
-                    var_infos["var_type"], var_infos["dir"], var_infos["var_name"])
-        
-        for element in ["extern_located_variables_declaration", 
-                        "entry_variables", 
-                        "init_entry_variables"]:
-            str_completion[element] = "\n".join(str_completion[element])
-        
-        Gen_DS402Nodefile_path = os.path.join(buildpath, "ds402node_%s.c"%location_str)
-        ds402nodefile = open(Gen_DS402Nodefile_path, 'w')
-        ds402nodefile.write(plc_ds402node_code % str_completion)
-        ds402nodefile.close()
-        
-        return [(Gen_DS402Nodefile_path, '"-I%s"'%os.path.abspath(self.GetPlugRoot().GetIECLibPath()))],"",True
-        
-
-
-TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L",
-    "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L", 
-    "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"}
-
-DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64",
-    "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64", 
-    "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"}
-
-VARCLASSCONVERSION = {"T": LOCATION_VAR_INPUT, "R": LOCATION_VAR_OUTPUT, "RT": LOCATION_VAR_MEMORY}
+        return [],"",False
+
+#--------------------------------------------------
+#                 Ethercat DS402 Node
+#--------------------------------------------------
+
+if HAS_MCL:
+    
+    NODE_VARIABLES = [
+        ("ControlWord", 0x6040, 0x00, "UINT", "Q"),
+        ("TargetPosition", 0x607a, 0x00, "DINT", "Q"),
+        ("StatusWord", 0x6041, 0x00, "UINT", "I"),
+        ("ModesOfOperationDisplay", 0x06061, 0x00, "SINT", "I"),
+        ("ActualPosition", 0x6064, 0x00, "DINT", "I"),
+        ("ErrorCode", 0x603f, 0x00, "UINT", "I"),
+    ]
+    
+    class _EthercatDS402SlavePlug(_EthercatSlavePlug):
+        XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+          <xsd:element name="DS402SlaveParams">
+            <xsd:complexType>
+              %s
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:schema>
+        """ % AxisXSD
+        
+        NODE_PROFILE = 402
+        EditorType = DS402NodeEditor
+        
+        def PlugGenerate_C(self, buildpath, 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_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
+            """
+            current_location = self.GetCurrentLocation()
+            
+            location_str = "_".join(map(lambda x:str(x), current_location))
+            
+            plc_ds402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_ds402node.c")
+            plc_ds402node_file = open(plc_ds402node_filepath, 'r')
+            plc_ds402node_code = plc_ds402node_file.read()
+            plc_ds402node_file.close()
+            
+            str_completion = {
+                "location": location_str,
+                "MCL_headers": Headers,
+                "extern_located_variables_declaration": [],
+                "entry_variables": [],
+                "init_axis_params": [],
+                "init_entry_variables": [],
+            }
+            
+            for variable in NODE_VARIABLES:
+                var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable))
+                var_infos["location"] = location_str
+                var_infos["var_size"] = self.GetSizeOfType(var_infos["var_type"])
+                var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos
+                
+                str_completion["extern_located_variables_declaration"].append(
+                        "IEC_%(var_type)s *%(var_name)s;" % var_infos)
+                str_completion["entry_variables"].append(
+                        "    IEC_%(var_type)s *%(name)s;" % var_infos)
+                str_completion["init_entry_variables"].append(
+                        "    __DS402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos)
+                
+                self.PlugParent.FileGenerator.DeclareVariable(
+                        self.GetSlavePos(), var_infos["index"], var_infos["subindex"], 
+                        var_infos["var_type"], var_infos["dir"], var_infos["var_name"])
+            
+            params = self.PlugParams[1].getElementInfos(self.PlugParams[0])
+            for param in params["children"]:
+                if param["value"] is not None:
+                    param_infos = {
+                        "location": location_str,
+                        "param_name": param["name"],
+                    }
+                    if param["type"] == "boolean":
+                        param_infos["param_value"] = {True: "true", False: "false"}[param["value"]]
+                    else:
+                        param_infos["param_value"] = str(param["value"])
+                    str_completion["init_axis_params"].append(
+                        "        __DS402Node_%(location)s.axis->%(param_name)s = %(param_value)s;" % param_infos)
+            
+            for element in ["extern_located_variables_declaration", 
+                            "entry_variables", 
+                            "init_axis_params", 
+                            "init_entry_variables"]:
+                str_completion[element] = "\n".join(str_completion[element])
+            
+            Gen_DS402Nodefile_path = os.path.join(buildpath, "ds402node_%s.c"%location_str)
+            ds402nodefile = open(Gen_DS402Nodefile_path, 'w')
+            ds402nodefile.write(plc_ds402node_code % str_completion)
+            ds402nodefile.close()
+            
+            return [(Gen_DS402Nodefile_path, '"-I%s"'%os.path.abspath(self.GetPlugRoot().GetIECLibPath()))],"",True
 
 #--------------------------------------------------
 #                 Ethercat MASTER
@@ -257,18 +299,6 @@
         slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"]))
         slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"]))
     setattr(cls, "setType", setType)
-    
-cls = EtherCATConfigClasses.get("Slave_Info", None)
-if cls:
-
-    def getSlavePosition(self):
-        return self.getPhysAddr(), self.getAutoIncAddr()
-    setattr(cls, "getSlavePosition", getSlavePosition)
-
-    def setSlavePosition(self, alias, pos):
-        self.setPhysAddr(alias)
-        self.setAutoIncAddr(pos)
-    setattr(cls, "setSlavePosition", setSlavePosition)
 
 class _EthercatPlug:
     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
@@ -281,9 +311,10 @@
       </xsd:element>
     </xsd:schema>
     """
-    EditorType = ConfigEditor
-    
-    PlugChildsTypes = [("EthercatDS402Slave", _EthercatDS402SlavePlug, "Ethercat DS402 Slave")]
+    
+    PlugChildsTypes = [("EthercatSlave", _EthercatSlavePlug, "Ethercat Slave")]
+    if HAS_MCL:
+        PlugChildsTypes.append(("EthercatDS402Slave", _EthercatDS402SlavePlug, "Ethercat DS402 Slave"))
     
     def __init__(self):
         filepath = self.ConfigFileName()
@@ -314,62 +345,70 @@
     def GetSlaves(self):
         slaves = []
         for slave in self.Config.getConfig().getSlave():
-            slaves.append(slave.getInfo().getSlavePosition())
+            slaves.append(slave.getInfo().getPhysAddr())
         slaves.sort()
         return slaves
 
     def GetSlave(self, slave_pos):
         for slave in self.Config.getConfig().getSlave():
             slave_info = slave.getInfo()
-            if slave_info.getSlavePosition() == slave_pos:
+            if slave_info.getPhysAddr() == slave_pos:
                 return slave
         return None
 
-    def AddSlave(self):
-        slaves = self.GetSlaves()
-        for PlugInstance in self.IterChilds():
-            slaves.append(PlugInstance.GetSlavePos())
-        slaves.sort()
-        if len(slaves) > 0:
-            new_pos = (slaves[-1][0] + 1, 0)
-        else:
-            new_pos = (0, 0)
-        slave = EtherCATConfigClasses["Config_Slave"]()
-        slave_infos = slave.getInfo()
-        slave_infos.setName("undefined")
-        slave_infos.setSlavePosition(new_pos[0], new_pos[1])
-        self.Config.getConfig().appendSlave(slave)
-        self.BufferConfig()
-        return new_pos
-    
-    def RemoveSlave(self, slave_pos):
+    def PlugAddChild(self, PlugName, PlugType, IEC_Channel=0):
+        """
+        Create the plugins that may be added as child to this node self
+        @param PlugType: string desining the plugin class name (get name from PlugChildsTypes)
+        @param PlugName: string for the name of the plugin instance
+        """
+        newPluginOpj = PlugTemplate.PlugAddChild(self, PlugName, PlugType, IEC_Channel)
+        
+        slave = self.GetSlave(newPluginOpj.BaseParams.getIEC_Channel())
+        if slave is None:
+            slave = EtherCATConfigClasses["Config_Slave"]()
+            slave_infos = slave.getInfo()
+            slave_infos.setName("undefined")
+            slave_infos.setPhysAddr(newPluginOpj.BaseParams.getIEC_Channel())
+            slave_infos.setAutoIncAddr(0)
+            self.Config.getConfig().appendSlave(slave)
+            self.BufferConfig()
+            self.OnPlugSave()
+        
+        return newPluginOpj
+
+    def _doRemoveChild(self, PlugInstance):
+        slave_pos = PlugInstance.GetSlavePos()
         config = self.Config.getConfig()
         for idx, slave in enumerate(config.getSlave()):
             slave_infos = slave.getInfo()
-            if slave_infos.getSlavePosition() == slave_pos:
+            if slave_infos.getPhysAddr() == slave_pos:
                 config.removeSlave(idx)
                 self.BufferConfig()
-                return True
-        return False
-    
-    def SetSlavePos(self, slave_pos, alias=None, position=None):
-        for PlugInstance in self.IterChilds():
-            if PlugInstance.BaseParams.getIEC_Channel() == alias:
-                return _("Slave with alias \"%d\" already exists!" % alias)
+                self.OnPlugSave()
+        PlugTemplate._doRemoveChild(self, PlugInstance)
+
+    def SetSlavePosition(self, slave_pos, new_pos):
         slave = self.GetSlave(slave_pos)
         if slave is not None:
             slave_info = slave.getInfo()
-            new_pos = slave_pos
-            if alias is not None:
-                new_pos = (alias, new_pos[1])
-            if position is not None:
-                new_pos = (new_pos[0], position)
-            if self.GetSlave(new_pos) is not None:
-                return _("Slave with position \"%d:%d\" already exists!" % new_pos)
-            slave_info.setSlavePosition(*new_pos)
+            slave_info.setPhysAddr(new_pos)
             self.BufferConfig()
+    
+    def GetSlaveAlias(self, slave_pos):
+        slave = self.GetSlave(slave_pos)
+        if slave is not None:
+            slave_info = slave.getInfo()
+            return slave_info.getAutoIncAddr()
         return None
     
+    def SetSlaveAlias(self, slave_pos, alias):
+        slave = self.GetSlave(slave_pos)
+        if slave is not None:
+            slave_info = slave.getInfo()
+            slave_info.setAutoIncAddr(alias)
+            self.BufferConfig()
+    
     def GetSlaveType(self, slave_pos):
         slave = self.GetSlave(slave_pos)
         if slave is not None:
@@ -381,7 +420,6 @@
         if slave is not None:
             slave.setType(type_infos)
             self.BufferConfig()
-        return None
     
     def GetSlaveInfos(self, slave_pos):
         slave = self.GetSlave(slave_pos)
@@ -415,81 +453,51 @@
     def GetModuleInfos(self, type_infos):
         return self.PlugParent.GetModuleInfos(type_infos)
     
-    def GetModulesByProfile(self, profile_type):
-        return self.PlugParent.GetModulesByProfile(profile_type)
-    
-    def GetSlaveTypesLibrary(self):
-        return self.PlugParent.GetModulesLibrary()
-    
-    def GetDeviceLocationTree(self, current_location, type_infos):
-        vars = []
-        
-        device = self.GetModuleInfos(type_infos)
-        if device is not None:
-            sync_managers = []
-            for sync_manager in device.getSm():
-                sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
-                sync_manager_direction = sync_manager_control_byte & 0x0c
-                if sync_manager_direction:
-                    sync_managers.append(LOCATION_VAR_OUTPUT)
-                else:
-                    sync_managers.append(LOCATION_VAR_INPUT)
-            
-            entries = device.GetEntriesList().items()
-            entries.sort()
-            for (index, subindex), entry in entries:
-                var_size = self.GetSizeOfType(entry["Type"])
-                if var_size is not None:
-                    var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
-                    if var_class is not None:
-                        if var_class == LOCATION_VAR_INPUT:
-                            var_dir = "%I"
-                        else:
-                            var_dir = "%Q"    
-                    
-                        vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
-                                     "type": var_class,
-                                     "size": var_size,
-                                     "IEC_type": entry["Type"],
-                                     "var_name": "%s_%4.4x_%2.2x" % ("_".join(type_infos["device_type"].split()), index, subindex),
-                                     "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location + 
-                                                                                                (index, subindex)))),
-                                     "description": "",
-                                     "children": []})
+    def GetSlaveTypesLibrary(self, profile_filter=None):
+        return self.PlugParent.GetModulesLibrary(profile_filter)
+    
+    def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
+        slave = self.GetSlave(slave_pos)
+        if slave is not None:
+            type_infos = slave.getType()
+        
+            vars = []
+            
+            device = self.GetModuleInfos(type_infos)
+            if device is not None:
+                sync_managers = []
+                for sync_manager in device.getSm():
+                    sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
+                    sync_manager_direction = sync_manager_control_byte & 0x0c
+                    if sync_manager_direction:
+                        sync_managers.append(LOCATION_VAR_OUTPUT)
+                    else:
+                        sync_managers.append(LOCATION_VAR_INPUT)
+                
+                entries = device.GetEntriesList().items()
+                entries.sort()
+                for (index, subindex), entry in entries:
+                    var_size = self.GetSizeOfType(entry["Type"])
+                    if var_size is not None:
+                        var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
+                        if var_class is not None:
+                            if var_class == LOCATION_VAR_INPUT:
+                                var_dir = "%I"
+                            else:
+                                var_dir = "%Q"    
+                        
+                            vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
+                                         "type": var_class,
+                                         "size": var_size,
+                                         "IEC_type": entry["Type"],
+                                         "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex),
+                                         "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location + 
+                                                                                                    (index, subindex)))),
+                                         "description": "",
+                                         "children": []})
         
         return vars
     
-    def GetVariableLocationTree(self):
-        '''See PlugTemplate.GetVariableLocationTree() for a description.'''
-
-        current_location = self.GetCurrentLocation()
-        
-        groups = []
-        for slave_pos in self.GetSlaves():
-            
-            slave = self.GetSlave(slave_pos)
-            if slave is not None:
-                type_infos = slave.getType()
-                
-                vars = self.GetDeviceLocationTree(current_location + slave_pos, type_infos)
-                if len(vars) > 0:
-                    groups.append({"name": "%s (%d,%d)" % ((type_infos["device_type"],) + slave_pos),
-                                   "type": LOCATION_GROUP,
-                                   "location": ".".join(map(str, current_location + slave_pos)) + ".x",
-                                   "children": vars})
-                
-        return  {"name": self.BaseParams.getName(),
-                 "type": LOCATION_PLUGIN,
-                 "location": self.GetFullIEC_Channel(),
-                 "children": groups}
-    
-    PluginMethods = [
-        {"bitmap" : os.path.join("images", "EditCfile"),
-         "name" : _("Edit Config"), 
-         "tooltip" : _("Edit Config"),
-         "method" : "_OpenView"},
-    ]
-
     def PlugTestModified(self):
         return self.ChangesToSave or not self.ConfigIsSaved()    
 
@@ -548,14 +556,14 @@
         for slave_pos in slaves:
             slave = self.GetSlave(slave_pos)
             if slave is not None:
-                self.FileGenerator.DeclareSlave(slave_pos, slave.getType())
+                self.FileGenerator.DeclareSlave(slave_pos, slave.getInfo().getAutoIncAddr(), slave.getType())
         
         for location in locations:
             loc = location["LOC"][len(current_location):]
-            slave_pos = loc[:2]
-            if slave_pos in slaves:
+            slave_pos = loc[0]
+            if slave_pos in slaves and len(loc) == 3:
                 self.FileGenerator.DeclareVariable(
-                    slave_pos, loc[2], loc[3], location["IEC_TYPE"], location["DIR"], location["NAME"])
+                    slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"])
         
         return [],"",False
         
@@ -709,13 +717,11 @@
     def __del__(self):
         self.Controler = None            
 
-    def DeclareSlave(self, slave_identifier, slave):
-        self.Slaves.append((slave_identifier, slave))
-        self.Slaves.sort()
-        return self.Slaves.index((slave_identifier, slave))
-
-    def DeclareVariable(self, slave_identifier, index, subindex, iec_type, dir, name):
-        slave_variables = self.UsedVariables.setdefault(slave_identifier, {})
+    def DeclareSlave(self, slave_index, slave_alias, slave):
+        self.Slaves.append((slave_index, slave_alias, slave))
+
+    def DeclareVariable(self, slave_index, index, subindex, iec_type, dir, name):
+        slave_variables = self.UsedVariables.setdefault(slave_index, {})
         
         entry_infos = slave_variables.get((index, subindex), None)
         if entry_infos is None:
@@ -752,12 +758,19 @@
             for entry_infos in slave_entries.itervalues():
                 entry_infos["mapped"] = False
         
-        for slave_idx, (slave_pos, type_infos) in enumerate(self.Slaves):
+        self.Slaves.sort()
+        alias = {}
+        for (slave_idx, slave_alias, type_infos) in self.Slaves:
+            if alias.get(slave_alias) is not None:
+                alias[slave_alias] += 1
+            else:
+                alias[slave_alias] = 0
+            slave_pos = (slave_alias, alias[slave_alias])
             
             device = self.Controler.GetModuleInfos(type_infos)
             if device is not None:
             
-                slave_variables = self.UsedVariables.get(slave_pos, {})
+                slave_variables = self.UsedVariables.get(slave_idx, {})
                 device_entries = device.GetEntriesList()
                 
                 if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0:
@@ -1306,39 +1319,45 @@
                             raise ValueError, "Not such group \"%\"" % device_group
                         vendor_category["groups"][device_group]["devices"].append((device.getType().getcontent(), device))
     
-    def GetModulesLibrary(self):
+    def GetModulesLibrary(self, profile_filter=None):
         library = []
         children_dict = {}
         for vendor_id, vendor in self.ModulesLibrary.iteritems():
             groups = []
-            library.append({"name": vendor["name"],
-                            "type": ETHERCAT_VENDOR,
-                            "children": groups})
             for group_type, group in vendor["groups"].iteritems():
                 group_infos = {"name": group["name"],
                                "order": group["order"],
                                "type": ETHERCAT_GROUP,
+                               "infos": None,
                                "children": children_dict.setdefault(group_type, [])}
-                if group["parent"] is not None:
-                    parent_children = children_dict.setdefault(group["parent"], [])
-                    parent_children.append(group_infos)
-                else:
-                    groups.append(group_infos)
                 device_dict = {}
                 for device_type, device in group["devices"]:
-                    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()}}
-                    group_infos["children"].append(device_infos)
-                    device_type_occurrences = device_dict.setdefault(device_type, [])
-                    device_type_occurrences.append(device_infos)
+                    if profile_filter is None or profile_filter in device.GetProfileNumbers():
+                        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()},
+                                        "children": []}
+                        group_infos["children"].append(device_infos)
+                        device_type_occurrences = device_dict.setdefault(device_type, [])
+                        device_type_occurrences.append(device_infos)
                 for device_type_occurrences in device_dict.itervalues():
                     if len(device_type_occurrences) > 1:
                         for occurrence in device_type_occurrences:
                             occurrence["name"] += _(" (rev. %s)") % occurrence["infos"]["revision_number"]
+                if len(group_infos["children"]) > 0:
+                    if group["parent"] is not None:
+                        parent_children = children_dict.setdefault(group["parent"], [])
+                        parent_children.append(group_infos)
+                    else:
+                        groups.append(group_infos)
+            if len(groups) > 0:
+                library.append({"name": vendor["name"],
+                                "type": ETHERCAT_VENDOR,
+                                "infos": None,
+                                "children": groups})
         library.sort(lambda x, y: cmp(x["name"], y["name"]))
         return library
     
@@ -1354,16 +1373,5 @@
                         revision_number == ExtractHexDecValue(type_infos["revision_number"])):
                         return device
         return None
-    
-    def GetModulesByProfile(self, profile_type):
-        modules = []
-        for vendor_id, vendor in self.ModulesLibrary.iteritems():
-            for group_type, group in vendor["groups"].iteritems():
-                for device_type, device in group["devices"]:
-                    if profile_type in device.GetProfileNumbers():
-                        product_code = ExtractHexDecValue(device.getType().getProductCode())
-                        revision_number = ExtractHexDecValue(device.getType().getRevisionNo())
-                        modules.append((device_type, vendor_id, product_code, revision_number))
-        return modules
-
-            
+
+            
--- a/etherlab/plc_ds402node.c	Sun Mar 11 21:57:00 2012 +0100
+++ b/etherlab/plc_ds402node.c	Sun Mar 18 23:57:32 2012 +0100
@@ -19,6 +19,7 @@
 IEC_UINT __InactiveMask = 0x4f;
 IEC_UINT __ActiveMask = 0x6f;
 IEC_UINT __PowerMask = 0x10;
+IEC_BOOL __FirstTick = 1;
 
 typedef enum {
 	__Unknown,
@@ -44,8 +45,8 @@
 
 int __init_%(location)s()
 {
-%(init_entry_variables)s;
-*__IW%(location)s_0 = __MK_AllocAxis(&(__DS402Node_%(location)s.axis));
+%(init_entry_variables)s
+	*__IW%(location)s_0 = __MK_AllocAxis(&(__DS402Node_%(location)s.axis));
 	return 0;
 }
 
@@ -58,6 +59,11 @@
 	IEC_UINT statusword_inactive = *(__DS402Node_%(location)s.StatusWord) & __InactiveMask;
 	IEC_UINT statusword_active = *(__DS402Node_%(location)s.StatusWord) & __ActiveMask;
 
+	if (__FirstTick) {
+%(init_axis_params)s
+		_FirstTick = 0;
+	}
+
 	// DS402 node state computation
 	__DS402Node_%(location)s.state = __Unknown;
 	switch (statusword_inactive) {