Merge default changes in SVGHMI svghmi
authorEdouard Tisserant
Thu, 18 Jun 2020 10:42:08 +0200
branchsvghmi
changeset 2981 a0932a52e53b
parent 2980 2a21d6060d64 (current diff)
parent 2653 8b612b357679 (diff)
child 2982 1627d552f181
Merge default changes in SVGHMI
ConfigTreeNode.py
POULibrary.py
ProjectController.py
etherlab/images/genicons.sh
--- a/etherlab/CommonEtherCATFunction.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/CommonEtherCATFunction.py	Thu Jun 18 10:42:08 2020 +0200
@@ -50,10 +50,9 @@
                 return name.getcontent()
     return default
 
-# --------------------------------------------------
+#--------------------------------------------------
 #         Remote Exec Etherlab Commands
-# --------------------------------------------------
-
+#--------------------------------------------------
 
 # --------------------- for master ---------------------------
 MASTER_STATE = """
@@ -84,18 +83,21 @@
 returnVal = result
 """
 
-# ethercat sdos -p (slave position)
-SLAVE_SDO = """
+# ethercat upload -p (slave position) -t (type) (index) (sub index)
+SDO_UPLOAD = """
 import commands
-result = commands.getoutput("ethercat sdos -p %d")
-returnVal =result
-"""
-
-# ethercat upload -p (slave position) (main index) (sub index)
-GET_SLOW_SDO = """
-import commands
-result = commands.getoutput("ethercat upload -p %d %s %s")
-returnVal =result
+sdo_data = []
+input_data = "%s"
+slave_pos = %d
+command_string = ""
+for sdo_token in input_data.split(","):  
+    if len(sdo_token) > 1:
+        sdo_token = sdo_token.strip()
+        type, idx, subidx = sdo_token.split(" ")
+        command_string = "ethercat upload -p " + str(slave_pos) + " -t " + type + " " + idx + " " + subidx
+        result = commands.getoutput(command_string)
+        sdo_data.append(result)
+returnVal =sdo_data
 """
 
 # ethercat download -p (slave position) (main index) (sub index) (value)
@@ -119,6 +121,25 @@
 returnVal =result
 """
 
+# ethercat reg_read -p (slave position) (address) (size)
+MULTI_REG_READ = """ 
+import commands
+output = []
+addr, size = range(2)
+slave_num = %d 
+reg_info_str = "%s"
+reg_info_list = reg_info_str.split("|")
+for slave_idx in range(slave_num):
+    for reg_info in reg_info_list:
+        param = reg_info.split(",")
+        result = commands.getoutput("ethercat reg_read -p "
+                                    + str(slave_idx) + " "
+                                    + param[addr] + " "
+                                    + param[size])
+        output.append(str(slave_idx) + "," + param[addr] + "," + result)
+returnVal = output
+"""
+
 # ethercat sii_write -p (slave position) - (contents)
 SII_WRITE = """
 import subprocess
@@ -143,6 +164,12 @@
 returnVal =result
 """
 
+# ethercat pdos
+PDOS = """
+import commands
+result = commands.getoutput("ethercat pdos -p 0")
+returnVal =result  
+"""
 
 # --------------------------------------------------
 #    Common Method For EtherCAT Management
@@ -152,14 +179,40 @@
     # ----- Data Structure for ethercat management ----
     SlaveState = ""
 
+    # SDO base data type for Ethercatmaster
+    BaseDataTypes = {
+            "bool": ["BOOLEAN", "BOOL", "BIT"],
+            "uint8": ["BYTE", "USINT", "BIT1", "BIT2", "BIT3", "BIT4", "BIT5", "BIT6",
+                      "BIT7", "BIT8", "BITARR8", "UNSIGNED8"],
+            "uint16": ["BITARR16", "UNSIGNED16", "UINT"],
+            "uint32": ["BITARR32", "UNSIGNED24", "UINT24", "UNSIGNED32", "UDINT"],
+            "uint64": ["UNSINED40", "UINT40", "UNSIGNED48", "UINT48", "UNSIGNED56", 
+                       "UINT56", "UNSIGNED64", "ULINT"],
+            "int8": ["INTEGER8", "SINT"],
+            "int16": ["INTEGER16", "INT"],
+            "int32": ["INTEGER24", "INT24", "INTEGER32", "DINT"],
+            "int64": ["INTEGER40", "INT40", "INTEGER48", "INT48", "INTEGER56", "INT56",
+                      "INTEGER64", "LINT"],
+            "float": ["REAL", "REAL32"],
+            "double": ["LREAL", "REAL64"],
+            "string": ["VISUBLE_STRING", "STRING(n)"],
+            "octet_string": ["OCTET_STRING"],
+            "unicode_string": ["UNICODE_STRING"]
+            }
+    
     # category of SDO data
     DatatypeDescription, CommunicationObject, ManufacturerSpecific, \
         ProfileSpecific, Reserved, AllSDOData = range(6)
 
-    # store the execution result of "ethercat sdos" command into SaveSDOData.
-    SaveSDOData = []
-
-    # Flags for checking "write" permission of OD entries
+    # SDO data informations: index, sub-index, type, bit size, category-name 
+    SDOVariables = []
+    SDOSubEntryData = []
+    
+    # defalut value of SDO data in XML
+    # Not Used
+    DefaultValueDic = {}
+    
+    # Flags for checking "write" permission of OD entries 
     CheckPREOP = False
     CheckSAFEOP = False
     CheckOP = False
@@ -241,15 +294,6 @@
     # -------------------------------------------------------------------------------
     #                        Used SDO Management
     # -------------------------------------------------------------------------------
-    def GetSlaveSDOFromSlave(self):
-        """
-        Get SDO objects information of current slave using "ethercat sdos -p %d" command.
-        Command example : "ethercat sdos -p 0"
-        @return return_val : execution results of "ethercat sdos" command (need to be parsed later)
-        """
-        _error, return_val = self.Controler.RemoteExec(SLAVE_SDO % (self.Controler.GetSlavePos()), return_val=None)
-        return return_val
-
     def SDODownload(self, data_type, idx, sub_idx, value):
         """
         Set an SDO object value to user-specified value using "ethercat download" command.
@@ -258,11 +302,14 @@
         @param idx : index of the SDO entry
         @param sub_idx : subindex of the SDO entry
         @param value : value of SDO entry
-        """
-        _error, _return_val = self.Controler.RemoteExec(
-            SDO_DOWNLOAD % (data_type, self.Controler.GetSlavePos(), idx, sub_idx, value),
-            return_val=None)
-
+        """ 
+        valid_type = self.GetValidDataType(data_type)
+        _error, return_val = self.Controler.RemoteExec(
+            SDO_DOWNLOAD%(valid_type, self.Controler.GetSlavePos(),
+                idx, sub_idx, value), return_val = None)
+        
+        return return_val
+    
     def BackupSDODataSet(self):
         """
         Back-up current SDO entry information to restore the SDO data
@@ -282,12 +329,316 @@
         for dummy in range(6):
             self.SaveSDOData.append([])
 
+    def GetAllSDOValuesFromSlave(self):
+        """
+        Get SDO values of All SDO entries.
+        @return return_val: list of result of "SDO_UPLOAD"
+        """
+        entry_infos = ""
+        alldata_idx = len(self.SDOVariables)
+        counter = 0
+        for category in self.SDOVariables:
+            counter +=1
+            # for avoid redundant repetition 
+            if counter == alldata_idx:
+                continue
+            
+            for entry in category:
+                valid_type = self.GetValidDataType(entry["type"])
+                for_command_string = "%s %s %s ," % \
+                        (valid_type, entry["idx"], entry["subIdx"])
+                entry_infos += for_command_string
+             
+        error, return_val = self.Controler.RemoteExec(SDO_UPLOAD%(entry_infos, self.Controler.GetSlavePos()), return_val = None)
+        
+        return return_val
+
+    def GetSDOValuesFromSlave(self, entries_info):
+        """
+        Get SDO values of some SDO entries.
+        @param entries_info: dictionary of SDO entries that is wanted to know the value. 
+        @return return_val: list of result of "SDO_UPLOAD"
+        """
+        entry_infos = ""
+
+        entries_info_list = entries_info.items()
+        entries_info_list.sort()
+        
+        for (idx, subIdx), entry in entries_info_list:
+            valid_type = self.GetValidDataType(entry["type"])
+            for_command_string = "%s %s %s ," % \
+                        (valid_type, str(idx), str(subIdx))
+            entry_infos += for_command_string
+
+        error, return_val = self.Controler.RemoteExec(SDO_UPLOAD%(entry_infos, self.Controler.GetSlavePos()), return_val = None)
+        
+        return return_val
+
+    def ExtractObjects(self):
+        """
+        Extract object type items from imported ESI xml.
+        And they are stuctured as dictionary.
+        @return objects: dictionary of objects
+        """
+        objects = {}
+
+        slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
+        type_infos = slave.getType()
+        device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
+         
+        if device is not None :
+            for dictionary in device.GetProfileDictionaries():
+                dictionary.load()
+                for object in dictionary.getObjects().getObject():
+                    object_index = ExtractHexDecValue(object.getIndex().getcontent())
+                    objects[(object_index)] = object
+        
+        return objects
+
+    def ExtractAllDataTypes(self):
+        """
+        Extract all data types from imported ESI xml.
+        @return dataTypes: dictionary of datatypes 
+        """
+        dataTypes = {}
+        
+        slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
+        type_infos = slave.getType()
+        device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
+
+        for dictionary in device.GetProfileDictionaries():
+            dictionary.load()
+        
+            datatypes = dictionary.getDataTypes()
+            if datatypes is not None:
+
+                for datatype in datatypes.getDataType():
+                    dataTypes[datatype.getName()] = datatype
+        return dataTypes
+    
+    def IsBaseDataType(self, datatype):
+        """
+        Check if the datatype is a base data type.
+        @return baseTypeFlag: true if datatype is a base data type, unless false
+        """
+        baseTypeFlag = False
+        for baseDataTypeList in self.BaseDataTypes.values():
+            if datatype in baseDataTypeList:
+                baseTypeFlag = True
+                break
+        return baseTypeFlag
+
+    def GetBaseDataType(self, datatype):
+        """
+        Get a base data type corresponding the datatype.
+        @param datatype: Some data type (string format)
+        @return base data type
+        """
+        if self.IsBaseDataType(datatype):
+            return datatype
+        elif not datatype.find("STRING") == -1:
+            return datatype
+        else:
+            datatypes = self.ExtractAllDataTypes()
+            base_datatype = datatypes[datatype].getBaseType()
+            return self.GetBaseDataType(base_datatype)
+
+    def GetValidDataType(self, datatype):
+        """
+        Convert the datatype into a data type that is possible to download/upload 
+        in etherlab master stack.
+        @param datatype: Some data type (string format)
+        @return base_type: vaild data type
+        """
+        base_type = self.GetBaseDataType(datatype)
+
+        if re.match("STRING\([0-9]*\)", datatype) is not None:
+            return "string"
+        else:
+            for key, value in self.BaseDataTypes.items():
+                if base_type in value:
+                    return key
+        return base_type 
+
+    # Not Used 
+    def GetAllEntriesList(self):
+        """
+        Get All entries information that includes index, sub-index, name,
+        type, bit size, PDO mapping, and default value.
+        @return self.entries: dictionary of entry
+        """
+        slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
+        type_infos = slave.getType()
+        device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
+        self.entries = device.GetEntriesList()
+        datatypes = self.ExtractAllDataTypes()
+        objects = self.ExtractObjects()
+        entries_list = self.entries.items()
+        entries_list.sort()
+
+        # append sub entries
+        for (index, subidx), entry in entries_list:
+            # entry_* is string type
+            entry_type = entry["Type"]
+            entry_index = entry["Index"]
+            
+            try:
+                object_info = objects[index].getInfo()
+            except:
+                continue
+            
+            if object_info is not None:
+                obj_content = object_info.getcontent()
+            
+            typeinfo = datatypes.get(entry_type, None)
+            bitsize = typeinfo.getBitSize()
+            type_content = typeinfo.getcontent()
+           
+            # ArrayInfo type
+            if type_content is not None and type_content["name"] == "ArrayInfo":
+                for arrayinfo in type_content["value"]:
+                    element_num = arrayinfo.getElements()
+                    first_subidx = arrayinfo.getLBound()
+                    for offset in range(element_num):
+                        new_subidx = int(first_subidx) + offset
+                        entry_subidx = hex(new_subidx)
+                        if obj_content["value"][new_subidx]["name"] == "SubItem":
+                            subitem = obj_content["value"][new_subidx]["value"]
+                            subname = subitem[new_subidx].getName()
+                        if subname is not None:
+                            entry_name = "%s - %s" % \
+                                    (ExtractName(objects[index].getName()), subname)
+                        else:
+                            entry_name = ExtractName(objects[index].getName()) 
+                        self.entries[(index, new_subidx)] = {
+                                "Index": entry_index,
+                                "SubIndex": entry_subidx,
+                                "Name": entry_name,
+                                "Type": typeinfo.getBaseType(),
+                                "BitSize": str(bitsize/element_num),
+                                "Access": entry["Access"],
+                                "PDOMapping": entry["PDOMapping"]}
+                        try:
+                            value_info = subitem[new_subidx].getInfo().getcontent()\
+                                                            ["value"][0]["value"][0]
+                            self.AppendDefaultValue(index, new_subidx, value_info)
+                        except:
+                            pass
+
+                try:
+                    value_info = subitem[subidx].getInfo().getcontent()\
+                                                            ["value"][0]["value"][0]
+                    self.AppendDefaultValue(index, subidx, value_info)
+                except:
+                    pass
+            
+            # EnumInfo type
+            elif type_content is not None and type_content["name"] == "EnumInfo":
+                self.entries[(index, subidx)]["EnumInfo"] = {}
+                
+                for enuminfo in type_content["value"]:
+                    text = enuminfo.getText()
+                    enum = enuminfo.getEnum()
+                    self.entries[(index, subidx)]["EnumInfo"][str(enum)] = text
+
+                self.entries[(index, subidx)]["DefaultValue"] = "0x00" 
+            
+            # another types
+            else:
+                if subidx == 0x00:
+                    tmp_subidx = 0x00
+
+                try:
+                    if obj_content["value"][tmp_subidx]["name"] == "SubItem":
+                        sub_name = entry["Name"].split(" - ")[1]
+                        for num in range(len(obj_content["value"])):
+                            if sub_name == \
+                                    obj_content["value"][num]["value"][num].getName():
+                                subitem_content = obj_content["value"][tmp_subidx]\
+                                                             ["value"][tmp_subidx]
+                                value_info = subitem_content.getInfo().getcontent()\
+                                                             ["value"][0]["value"][0]
+                                tmp_subidx += 1
+                                break
+                            else:
+                                value_info = None
+                        
+                    else:
+                        value_info = \
+                                obj_content["value"][tmp_subidx]["value"][tmp_subidx]
+
+                    self.AppendDefaultValue(index, subidx, value_info)
+
+                except:
+                    pass
+
+        return self.entries
+                   
+    # Not Used  
+    def AppendDefaultValue(self, index, subidx, value_info=None):
+        """
+        Get the default value from the ESI xml
+        @param index: entry index
+        @param subidx: entry sub index
+        @param value_info: dictionary of infomation about default value
+
+        """
+        # there is not default value.
+        if value_info == None:
+            return
+
+        raw_value = value_info["value"]
+        
+        # default value is hex binary type.
+        if value_info["name"]  == "DefaultData":
+            raw_value_bit = list(hex(raw_value).split("0x")[1])
+             
+            datatype = self.GetValidDataType(self.entries[(index, subidx)]["Type"])
+            if datatype is "string" or datatype is "octet_string":
+
+                if "L" in raw_value_bit:
+                    raw_value_bit.remove("L")
+
+                default_value = "".join(raw_value_bit).decode("hex")
+           
+            elif datatype is "unicode_string":
+                default_value = "".join(raw_value_bit).decode("hex").\
+                                                           decode("utf-8")
+            
+            else:   
+                bit_num = len(raw_value_bit)
+                # padding 
+                if not bit_num%2 == 0:
+                    raw_value_bit.insert(0, "0")
+
+                default_value_bit = []
+            
+                # little endian -> big endian
+                for num in range(bit_num):
+                    if num%2 == 0:
+                        default_value_bit.insert(0, raw_value_bit[num])
+                        default_value_bit.insert(1, raw_value_bit[num+1])
+                
+                default_value = "0x%s" % "".join(default_value_bit)
+
+        # default value is string type.
+        # this case is not tested yet.
+        elif value_info["name"] == "DefaultString":
+            default_value = raw_value
+
+        # default value is Hex or Dec type.
+        elif value_info["name"] == "DefaultValue":
+            default_value = "0x" + hex(ExtractHexDecValue(raw_value))
+
+        self.entries[(index, subidx)]["DefaultValue"] = default_value
+
     # -------------------------------------------------------------------------------
     #                        Used PDO Monitoring
     # -------------------------------------------------------------------------------
     def RequestPDOInfo(self):
         """
-        Load slave information from RootClass (XML data) and parse the information (calling SlavePDOData() method).
+        Load slave information from RootClass (XML data) and parse the information 
+        (calling SlavePDOData() method).
         """
         # Load slave information from ESI XML file (def EthercatMaster.py)
         slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
@@ -314,6 +665,13 @@
             pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
             entries = pdo.getEntry()
             pdo_name = ExtractName(pdo.getName())
+            excludes = pdo.getExclude()
+            exclude_list = []
+            Sm = pdo.getSm()
+
+            if excludes :
+                for exclude in excludes:
+                    exclude_list.append(ExtractHexDecValue(exclude.getcontent()))
 
             # Initialize entry number count
             count = 0
@@ -329,13 +687,15 @@
                         "entry_index": index,
                         "subindex": subindex,
                         "name": ExtractName(entry.getName()),
-                        "bitlen": entry.getBitLen(),
-                        "type": entry.getDataType().getcontent()
+                        "bitlen": entry.getBitLen()
                     }
+                    if entry.getDataType() is not None:
+                        entry_infos["type"] = entry.getDataType().getcontent()
                     self.TxPDOInfo.append(entry_infos)
                     count += 1
 
-            categorys = {"pdo_index": pdo_index, "name": pdo_name, "number_of_entry": count}
+            categorys = {"pdo_index" : pdo_index, "name" : pdo_name, "sm" : Sm,
+                         "number_of_entry" : count, "exclude_list" : exclude_list}  
             self.TxPDOCategory.append(categorys)
 
         # Parsing RxPDO entries
@@ -344,6 +704,13 @@
             pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
             entries = pdo.getEntry()
             pdo_name = ExtractName(pdo.getName())
+            excludes = pdo.getExclude()
+            exclude_list = []
+            Sm = pdo.getSm()
+
+            if excludes :
+                for exclude in excludes:
+                    exclude_list.append(ExtractHexDecValue(exclude.getcontent()))
 
             # Initialize entry number count
             count = 0
@@ -359,14 +726,16 @@
                         "entry_index": index,
                         "subindex": subindex,
                         "name": ExtractName(entry.getName()),
-                        "bitlen": str(entry.getBitLen()),
-                        "type": entry.getDataType().getcontent()
+                        "bitlen": str(entry.getBitLen())
                     }
+                    if entry.getDataType() is not None:
+                        entry_infos["type"] = entry.getDataType().getcontent()
                     self.RxPDOInfo.append(entry_infos)
                     count += 1
 
-            categorys = {"pdo_index": pdo_index, "name": pdo_name, "number_of_entry": count}
-            self.RxPDOCategory.append(categorys)
+            categorys = {"pdo_index" : pdo_index, "name" : pdo_name, "sm" : Sm,
+                         "number_of_entry" : count, "exclude_list" : exclude_list}
+            self.RxPDOCategory.append(categorys) 
 
     def GetTxPDOCategory(self):
         """
@@ -404,10 +773,10 @@
         """
         Initialize PDO management data structure.
         """
-        self.TxPDOInfos = []
-        self.TxPDOCategorys = []
-        self.RxPDOInfos = []
-        self.RxPDOCategorys = []
+        self.TxPDOInfo = []
+        self.TxPDOCategory = []
+        self.RxPDOInfo = []
+        self.RxPDOCategory = []
 
     # -------------------------------------------------------------------------------
     #                        Used EEPROM Management
@@ -496,11 +865,11 @@
                         smartview_infos[cfg] = str(int(bootstrap_data[4*iter+2:4*(iter+1)]+bootstrap_data[4*iter:4*iter+2], 16))
 
             # get protocol (profile) types supported by mailbox; <Device>-<Mailbox>
-            mb = device.getMailbox()
-            if mb is not None:
-                for mailbox_protocol in mailbox_protocols:
-                    if getattr(mb, "get%s" % mailbox_protocol)() is not None:
-                        smartview_infos["supported_mailbox"] += "%s,  " % mailbox_protocol
+            with device.getMailbox() as mb:
+                if mb is not None:
+                    for mailbox_protocol in mailbox_protocols:
+                        if getattr(mb, "get%s" % mailbox_protocol)() is not None:
+                            smartview_infos["supported_mailbox"] += "%s,  " % mailbox_protocol
             smartview_infos["supported_mailbox"] = smartview_infos["supported_mailbox"].strip(", ")
 
             # get standard configuration of mailbox; <Device>-<Sm>
@@ -515,24 +884,24 @@
                     pass
 
             # get device identity from <Device>-<Type>
-            #  vendor ID; by default, pre-defined value in self.ModulesLibrary
-            #             if device type in 'vendor' item equals to actual slave device type, set 'vendor_id' to vendor ID.
+            # vendor ID; by default, pre-defined value in self.ModulesLibrary
+            # if device type in 'vendor' item equals to actual slave device type, set 'vendor_id' to vendor ID.
             for vendor_id, vendor in self.Controler.CTNParent.CTNParent.ModulesLibrary.Library.iteritems():
                 for available_device in vendor["groups"][vendor["groups"].keys()[0]]["devices"]:
                     if available_device[0] == type_infos["device_type"]:
                         smartview_infos["vendor_id"] = "0x" + "{:0>8x}".format(vendor_id)
 
-            #  product code;
+            # product code;
             if device.getType().getProductCode() is not None:
                 product_code = device.getType().getProductCode()
                 smartview_infos["product_code"] = "0x"+"{:0>8x}".format(ExtractHexDecValue(product_code))
 
-            #  revision number;
+            # revision number;
             if device.getType().getRevisionNo() is not None:
                 revision_no = device.getType().getRevisionNo()
                 smartview_infos["revision_no"] = "0x"+"{:0>8x}".format(ExtractHexDecValue(revision_no))
 
-            #  serial number;
+            # serial number;
             if device.getType().getSerialNo() is not None:
                 serial_no = device.getType().getSerialNo()
                 smartview_infos["serial_no"] = "0x"+"{:0>8x}".format(ExtractHexDecValue(serial_no))
@@ -548,14 +917,14 @@
         @param decnum : decimal value
         @return hex_data : hexadecimal representation of input value in decimal
         """
-        value = "%x" % decnum
+        value = "%x" % int(decnum, 16)
         value_len = len(value)
         if (value_len % 2) == 0:
             hex_len = value_len
         else:
             hex_len = (value_len // 2) * 2 + 2
 
-        hex_data = ("{:0>"+str(hex_len)+"x}").format(decnum)
+        hex_data = ("{:0>"+str(hex_len)+"x}").format(int(decnum, 16))
 
         return hex_data
 
@@ -750,7 +1119,7 @@
             data = ""
             for eeprom_element in device.getEeprom().getcontent():
                 if eeprom_element["name"] == "BootStrap":
-                    data = "{:0>16x}".format(eeprom_element)
+                    data = "{:0>16x}".format(int(eeprom_element,16))
             eeprom += self.GenerateEEPROMList(data, 0, 16)
 
             # get Standard Mailbox for EEPROM offset 0x0030-0x0037; <Device>-<sm>
@@ -794,11 +1163,11 @@
 
             # get supported mailbox protocols for EEPROM offset 0x0038-0x0039;
             data = 0
-            mb = device.getMailbox()
-            if mb is not None:
-                for bit, mbprot in enumerate(mailbox_protocols):
-                    if getattr(mb, "get%s" % mbprot)() is not None:
-                        data += 1 << bit
+            with device.getMailbox() as mb:
+                if mb is not None:
+                    for bit, mbprot in enumerate(mailbox_protocols):
+                        if getattr(mb, "get%s" % mbprot)() is not None:
+                            data += 1 << bit
             data = "{:0>4x}".format(data)
             eeprom.append(data[2:4])
             eeprom.append(data[0:2])
@@ -808,11 +1177,12 @@
                 eeprom.append("00")
 
             # get EEPROM size for EEPROM offset 0x007c-0x007d;
+            # Modify by jblee because of update IDE module (minidom -> lxml) 
             data = ""
-            for eeprom_element in device.getEeprom().getcontent():
-                if eeprom_element["name"] == "ByteSize":
-                    eeprom_size = int(str(eeprom_element))
-                    data = "{:0>4x}".format(int(eeprom_element)//1024*8-1)
+            for eeprom_element in device.getEeprom().getchildren():
+                if eeprom_element.tag == "ByteSize":
+                    eeprom_size = int(objectify.fromstring(eeprom_element.tostring()).text)
+                    data = "{:0>4x}".format(eeprom_size/1024*8-1)
 
             if data == "":
                 eeprom.append("00")
@@ -822,7 +1192,7 @@
                 eeprom.append(data[0:2])
 
             # Version for EEPROM 0x007e-0x007f;
-            #  According to "EtherCAT Slave Device Description(V0.3.0)"
+            # According to "EtherCAT Slave Device Description(V0.3.0)"
             eeprom.append("01")
             eeprom.append("00")
 
@@ -915,7 +1285,7 @@
                     vendor_specific_data += "{:0>2x}".format(ord(data[character]))
         data = ""
 
-        #  element2-1; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<GroupType>
+        # element2-1; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<GroupType>
         data = device.getGroupType()
         if data is not None and isinstance(data, text):
             for vendor_spec_string in vendor_spec_strings:
@@ -978,6 +1348,7 @@
                 vendor_specific_data += "{:0>2x}".format(len(data))
                 for character in range(len(data)):
                     vendor_specific_data += "{:0>2x}".format(ord(data[character]))
+
         data = ""
 
         #  element4; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Name(LcId is "1033" or "1"?)>
@@ -1183,42 +1554,41 @@
         # word 3 : Physical Layer Port info. and CoE Details
         eeprom.append("01")  # Physical Layer Port info - assume 01
         #  CoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<CoE>
-        coe_details = 0
-        mb = device.getMailbox()
         coe_details = 1  # sdo enabled
-        if mb is not None:
-            coe = mb.getCoE()
-            if coe is not None:
-                for bit, flag in enumerate(["SdoInfo", "PdoAssign", "PdoConfig",
-                                            "PdoUpload", "CompleteAccess"]):
-                    if getattr(coe, "get%s" % flag)() is not None:
-                        coe_details += 1 << bit
-        eeprom.append("{:0>2x}".format(coe_details))
-
-        # word 4 : FoE Details and EoE Details
-        #  FoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<FoE>
-        if mb is not None and mb.getFoE() is not None:
-            eeprom.append("01")
-        else:
-            eeprom.append("00")
-        #  EoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<EoE>
-        if mb is not None and mb.getEoE() is not None:
-            eeprom.append("01")
-        else:
-            eeprom.append("00")
-
-        # word 5 : SoE Channels(reserved) and DS402 Channels
-        #  SoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<SoE>
-        if mb is not None and mb.getSoE() is not None:
-            eeprom.append("01")
-        else:
-            eeprom.append("00")
-        #  DS402Channels; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<CoE>: DS402Channels
-        ds402ch = False
-        if mb is not None:
-            coe = mb.getCoE()
-            if coe is not None:
-                ds402ch = coe.getDS402Channels()
+        with device.getMailbox() as mb
+            if mb is not None:
+                coe = mb.getCoE()
+                if coe is not None:
+                    for bit, flag in enumerate(["SdoInfo", "PdoAssign", "PdoConfig",
+                                                "PdoUpload", "CompleteAccess"]):
+                        if getattr(coe, "get%s" % flag)() is not None:
+                            coe_details += 1 << bit
+            eeprom.append("{:0>2x}".format(coe_details))
+
+            # word 4 : FoE Details and EoE Details
+            #  FoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<FoE>
+            if mb is not None and mb.getFoE() is not None:
+                eeprom.append("01")
+            else:
+                eeprom.append("00")
+            #  EoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<EoE>
+            if mb is not None and mb.getEoE() is not None:
+                eeprom.append("01")
+            else:
+                eeprom.append("00")
+
+            # word 5 : SoE Channels(reserved) and DS402 Channels
+            #  SoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<SoE>
+            if mb is not None and mb.getSoE() is not None:
+                eeprom.append("01")
+            else:
+                eeprom.append("00")
+            #  DS402Channels; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<CoE>: DS402Channels
+            ds402ch = False
+            if mb is not None:
+                coe = mb.getCoE()
+                if coe is not None:
+                    ds402ch = coe.getDS402Channels()
         eeprom.append("01" if ds402ch in [True, 1] else "00")
 
         # word 6 : SysmanClass(reserved) and Flags
@@ -1546,6 +1916,24 @@
             return_val=None)
         return return_val
 
+    def MultiRegRead(self, slave_num, reg_infos):
+        """
+        
+        @slave_num:
+        @param addr_info:
+        @return return_val: 
+        """
+        reg_info_str = ""
+        for reg_info in reg_infos:
+            reg_info_str = reg_info_str + "%s|" % reg_info
+        reg_info_str = reg_info_str.strip("|")
+
+        _error, return_val = self.Controler.RemoteExec(\
+            MULTI_REG_READ%(slave_num, reg_info_str),
+            return_val = None)
+        
+        return return_val
+    
     def RegWrite(self, address, data):
         """
         Write data to slave ESC register using "ethercat reg_write -p %d %s %s" command.
@@ -1567,6 +1955,40 @@
         _error, _return_val = self.Controler.RemoteExec(RESCAN % (self.Controler.GetSlavePos()), return_val=None)
 
     # -------------------------------------------------------------------------------
+    #                        Used DC Configuration
+    #-------------------------------------------------------------------------------
+    def LoadESIData(self):
+        return_data = []
+        slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
+        type_infos = slave.getType()
+        device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
+        if device.getDc() is not None:
+            for OpMode in device.getDc().getOpMode():
+                temp_data = {
+                    "desc" : OpMode.getDesc() if OpMode.getDesc() is not None else "Unused",
+                    "assign_activate" : OpMode.getAssignActivate() \
+                        if OpMode.getAssignActivate() is not None else "#x0000",
+                    "cycletime_sync0" : OpMode.getCycleTimeSync0().getcontent() \
+                        if OpMode.getCycleTimeSync0() is not None else None,
+                    "shifttime_sync0" : OpMode.getShiftTimeSync0().getcontent() \
+                        if OpMode.getShiftTimeSync0() is not None else None,
+                    "cycletime_sync1" : OpMode.getShiftTimeSync1().getcontent() \
+                        if OpMode.getShiftTimeSync1() is not None else None,
+                    "shifttime_sync1" : OpMode.getShiftTimeSync1().getcontent() \
+                        if OpMode.getShiftTimeSync1() is not None else None
+                }
+
+                if OpMode.getCycleTimeSync0() is not None:
+                    temp_data["cycletime_sync0_factor"] = OpMode.getCycleTimeSync0().getFactor()
+
+                if OpMode.getCycleTimeSync1() is not None:
+                    temp_data["cycletime_sync1_factor"] = OpMode.getCycleTimeSync1().getFactor()
+
+                return_data.append(temp_data)
+
+        return return_data
+    
+    #-------------------------------------------------------------------------------
     #                        Common Use Methods
     # -------------------------------------------------------------------------------
     def CheckConnect(self, cyclic_flag):
--- a/etherlab/ConfigEditor.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/ConfigEditor.py	Thu Jun 18 10:42:08 2020 +0200
@@ -149,6 +149,9 @@
                     self.VariablesGrid.SetItemText(item, str(idx), 0)
                 else:
                     value = entry.get(colname, "")
+                    # add jblee
+                    if value is None:
+                        value = ""
                     if colname == "Access":
                         value = GetAccessValue(value, entry.get("PDOMapping", ""))
                     self.VariablesGrid.SetItemText(item, value, col)
@@ -617,9 +620,7 @@
         self.MasterStateEditor = wx.ScrolledWindow(prnt, style=wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL)
         self.MasterStateEditor.Bind(wx.EVT_SIZE, self.OnResize)
 
-        self.MasterStateEditor_Panel_Main_Sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
-        self.MasterStateEditor_Panel_Main_Sizer.AddGrowableCol(0)
-        self.MasterStateEditor_Panel_Main_Sizer.AddGrowableRow(0)
+        self.MasterStateEditor_Panel_Main_Sizer = wx.BoxSizer(wx.VERTICAL)
 
         self.MasterStateEditor_Panel = MasterStatePanelClass(self.MasterStateEditor, self.Controler)
 
@@ -628,6 +629,18 @@
         self.MasterStateEditor.SetSizer(self.MasterStateEditor_Panel_Main_Sizer)
         return self.MasterStateEditor
 
+    def OnResize2(self, event):
+        self.MasterStateEditor.GetBestSize()
+        xstart, ystart = self.MasterStateEditor.GetViewStart()
+        window_size = self.MasterStateEditor.GetClientSize()
+        maxx, maxy = self.MasterStateEditor.GetMinSize()
+        posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT))
+        posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT))
+        self.MasterStateEditor.Scroll(posx, posy)
+        self.MasterStateEditor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
+                maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy)
+        event.Skip()
+
     def _create_EthercatMasterEditor(self, prnt):
         self.EthercatMasterEditor = wx.ScrolledWindow(prnt,
                                                       style=wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL)
--- a/etherlab/EtherCATManagementEditor.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/EtherCATManagementEditor.py	Thu Jun 18 10:42:08 2020 +0200
@@ -71,11 +71,15 @@
         panels = [
             ("Slave State",        SlaveStatePanelClass, []),
             ("SDO Management",     SDOPanelClass, []),
-            ("PDO Monitoring",     PDOPanelClass, []),
+            ("PDO Mapping",     PDOPanelClass, [
+                    ("Rx PDO", RxPDOPanelClass),
+                    ("Tx PDO", TxPDOPanelClass)]),
+            ("MDP Setting",		MDPPanel, []),
             ("ESC Management",     EEPROMAccessPanel, [
-                ("Smart View", SlaveSiiSmartView),
-                ("Hex View", HexView)]),
-            ("Register Access",     RegisterAccessPanel, [])
+                    ("Smart View",  SlaveSiiSmartView),
+                    ("Hex View",    HexView)]),
+            ("Register Access",    RegisterAccessPanel, []),
+            ("DC Configuration",    DCConfigPanel, [])
         ]
         for pname, pclass, subs in panels:
             self.AddPage(pclass(self, self.Controler), pname)
@@ -230,7 +234,9 @@
         Event handler for slave state transition button click (Init, PreOP, SafeOP, OP button)
         @param event : wx.EVT_BUTTON object
         """
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(False)
+        # Check whether beremiz connected or not.
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
         if check_connect_flag:
             state_dic = ["INIT", "PREOP", "SAFEOP", "OP"]
 
@@ -255,7 +261,12 @@
         Timer event handler for periodic slave state monitoring (Default period: 1 sec = 1000 msec).
         @param event : wx.TIMER object
         """
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(True)
+        if self.IsShownOnScreen() is False:
+            return
+
+        # Check whether beremiz connected or not.
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = True)
         if check_connect_flag:
             returnVal = self.Controler.CommonMethod.GetSlaveStateFromSlave()
             line = returnVal.split("\n")
@@ -285,9 +296,15 @@
           - start slave state monitoring thread
         @param event : wx.EVT_BUTTON object
         """
-        self.SlaveStateThread = wx.Timer(self)
-        # set timer period (1000 ms)
-        self.SlaveStateThread.Start(1000)
+        # Check whether beremiz connected or not.
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
+        if check_connect_flag:
+            self.SlaveStateThread = wx.Timer(self)
+            # set timer period (2000 ms)
+            self.SlaveStateThread.Start(2000)
+        else:
+            pass
 
     def CurrentStateThreadStop(self, event):
         """
@@ -318,143 +335,218 @@
 
         self.Controler = controler
 
+        self.SDOMonitorEntries = {}
+        #----------------------------- SDO Monitor -------------------------------#
+        self.RBList = ["ON","OFF"]
+        
+        self.SDOMonitorRB = wx.RadioBox(self, label=_("monitoring"),
+                                        choices=self.RBList, majorDimension=2)
+        
+        self.SDOMonitorRB.SetSelection(1)
+        self.Bind(wx.EVT_RADIOBOX, self.OnRadioBox, self.SDOMonitorRB)
+       
+        self.SDOMonitorGrid = wx.grid.Grid(self,size=wx.Size(850,150),style=wx.EXPAND
+                                                        |wx.ALIGN_CENTER_HORIZONTAL
+                                                        |wx.ALIGN_CENTER_VERTICAL) 
+        self.SDOMonitorGrid.Bind(gridlib.EVT_GRID_CELL_LEFT_DCLICK, 
+                                                    self.onMonitorGridDoubleClick)
+
+        #----------------------------- SDO Entries ----------------------------#
+        self.SDOUpdateBtn = wx.Button(self, label=_("update"))         
+        self.SDOUpdateBtn.Bind(wx.EVT_BUTTON, self.OnSDOUpdate)
+        
+        self.SDOTraceThread = None
+        self.SDOMonitoringFlag = False
+        self.SDOValuesList = []
+        # Default SDO Page Number
+        self.SDOPageNum = 2
+
+        #----------------------------- Sizer --------------------------------------#
         self.SDOManagementMainSizer = wx.BoxSizer(wx.VERTICAL)
-        self.SDOManagementInnerMainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10)
-
-        self.SDOUpdate = wx.Button(self, label=_('update'))
-        self.SDOUpdate.Bind(wx.EVT_BUTTON, self.SDOInfoUpdate)
-
-        self.CallSDONoteBook = SDONoteBook(self, controler=self.Controler)
-        self.SDOManagementInnerMainSizer.Add(self.SDOUpdate)
-        self.SDOManagementInnerMainSizer.Add(self.CallSDONoteBook, wx.ALL | wx.EXPAND)
-
-        self.SDOManagementMainSizer.Add(self.SDOManagementInnerMainSizer)
-
+        self.SDOInfoBox = wx.StaticBox(self, label=_("SDO Entries"))
+        self.SDOMonitorBox = wx.StaticBox(self, label=_("SDO Monitor"))
+
+        self.SDONoteBook = SDONoteBook(self, controler=self.Controler)
+        self.SDOInfoBoxSizer = wx.StaticBoxSizer(self.SDOInfoBox, orient=wx.VERTICAL)
+        self.SDOMonitorBoxSizer = wx.StaticBoxSizer(self.SDOMonitorBox, 
+                                                                    orient=wx.VERTICAL)
+        self.SDOInfoBoxSizer.Add(self.SDOUpdateBtn)
+        
+        self.SDOInfoBoxSizer.Add(self.SDONoteBook, wx.ALL|wx.EXPAND)
+        self.SDOMonitorBoxSizer.Add(self.SDOMonitorRB)
+        self.SDOMonitorBoxSizer.Add(self.SDOMonitorGrid)
+        self.SDOManagementMainSizer.AddMany([self.SDOInfoBoxSizer, 
+                                             self.SDOMonitorBoxSizer])
         self.SetSizer(self.SDOManagementMainSizer)
-
-    def SDOInfoUpdate(self, event):
-        """
-        Evenet handler for SDO "update" button.
-          - Load SDO data from current slave
-        @param event : wx.EVT_BUTTON object
-        """
-        self.Controler.CommonMethod.SaveSDOData = []
+        
+        #----------------------------- fill the contents --------------------------# 
+        #self.entries = self.Controler.CTNParent.CTNParent.GetEntriesList()
+
+        slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
+        type_infos = slave.getType()
+        device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
+        self.entries = device.GetEntriesList()
+
+        self.Controler.CommonMethod.SDOVariables = []
+        self.Controler.CommonMethod.SDOSubEntryData = []
         self.Controler.CommonMethod.ClearSDODataSet()
-        self.SDOFlag = False
-
-        # Check whether beremiz connected or not.
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(False)
+        self.SDOParserXML(self.entries)
+        self.SDONoteBook.CreateNoteBook()      
+        self.CreateSDOMonitorGrid()
+        self.Refresh()
+
+    def OnSDOUpdate(self, event):
+        SlavePos = self.Controler.GetSlavePos()
+        num = self.SDOPageNum - 1
+        if num < 0:
+            for i in range(len(self.Controler.CommonMethod.SDOVariables)):
+                data = self.Controler.GetCTRoot()._connector.GetSDOEntriesData(
+                           self.Controler.CommonMethod.SDOVariables[i], SlavePos)
+                self.Controler.CommonMethod.SDOVariables[i] = data
+        else :
+            SDOUploadEntries = self.Controler.CommonMethod.SDOVariables[num]        
+            data = self.Controler.GetCTRoot()._connector.GetSDOEntriesData(SDOUploadEntries, SlavePos)
+            self.Controler.CommonMethod.SDOVariables[num] = data
+
+        self.SDONoteBook.CreateNoteBook()      
+        self.Refresh()
+
+    def OnRadioBox(self, event):
+        """
+        There are two selections that are on and off.
+        If the on is selected, the monitor thread begins to run.
+        If the off is selected, the monitor thread gets off.
+        @param event: wx.EVT_RADIOBOX object 
+        """
+        on, off = range(2)
+
+        if event.GetInt() == on:
+            CheckThreadFlag = self.SDOMonitoringThreadOn()
+            if not CheckThreadFlag:
+                self.SDOMonitorRB.SetSelection(off)
+        elif event.GetInt() == off:
+            self.SDOMonitoringThreadOff()
+
+    def SDOMonitoringThreadOn(self):
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
         if check_connect_flag:
-            self.SDOs = self.Controler.CommonMethod.GetSlaveSDOFromSlave()
-            # SDOFlag is "False", user click "Cancel" button
-            self.SDOFlag = self.SDOParser()
-
-            if self.SDOFlag:
-                self.CallSDONoteBook.CreateNoteBook()
-                self.Refresh()
-
-    def SDOParser(self):
-        """
-        Parse SDO data set that obtain "SDOInfoUpdate" Method
-        @return True or False
-        """
-
-        slaveSDO_progress = wx.ProgressDialog(_("Slave SDO Monitoring"), _("Now Uploading..."),
-                                              maximum=len(self.SDOs.splitlines()),
-                                              parent=self,
-                                              style=wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME |
-                                              wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME |
-                                              wx.PD_AUTO_HIDE | wx.PD_SMOOTH)
-
-        # If keep_going flag is False, SDOParser method is stop and return "False".
-        keep_going = True
-        count = 0
-
-        # SDO data example
-        # SDO 0x1000, "Device type"
-        # 0x1000:00,r-r-r-,uint32,32 bit,"Device type",0x00020192, 131474
-        for details_line in self.SDOs.splitlines():
-            count += 1
-            line_token = details_line.split("\"")
-            # len(line_token[2]) case : SDO 0x1000, "Device type"
-            if len(line_token[2]) == 0:
-                title_name = line_token[1]
-            # else case : 0x1000:00,r-r-r-,uint32,32 bit,"Device type",0x00020192, 131474
-            else:
-                # line_token = ['0x1000:00,r-r-r-,uint32,32 bit,', 'Device type', ',0x00020192, 131474']
-                token_head, name, token_tail = line_token
-
-                # token_head = ['0x1000:00', 'r-r-r-', 'uint32', '32 bit', '']
-                token_head = token_head.split(",")
-                ful_idx, access, type, size, _empty = token_head
-                # ful_idx.split(":") = ['0x1000', '00']
-                idx, sub_idx = ful_idx.split(":")
-
-                # token_tail = ['', '0x00020192', '131474']
-                token_tail = token_tail.split(",")
-                try:
-                    _empty, hex_val, _dec_val = token_tail
-
-                # SDO data is not return "dec value"
-                # line example :
-                # 0x1702:01,rwr-r-,uint32,32 bit," 1st mapping", ----
-                except Exception:
-                    _empty, hex_val = token_tail
-
-                name_after_check = self.StringTest(name)
-
-                # convert hex type
-                sub_idx = "0x" + sub_idx
-
-                if type == "octet_string":
-                    hex_val = ' ---- '
-
-                # SResult of SlaveSDO data parsing. (data type : dictionary)
-                self.Data = {'idx': idx.strip(), 'subIdx': sub_idx.strip(), 'access': access.strip(),
-                             'type': type.strip(), 'size': size.strip(),  'name': name_after_check.strip("\""),
-                             'value': hex_val.strip(), "category": title_name.strip("\"")}
-
-                category_divide_value = [0x1000, 0x2000, 0x6000, 0xa000, 0xffff]
-
-                for count in range(len(category_divide_value)):
-                    if int(idx, 0) < category_divide_value[count]:
-                        self.Controler.CommonMethod.SaveSDOData[count].append(self.Data)
-                        break
-
-                self.Controler.CommonMethod.SaveSDOData[self.AllSDOData].append(self.Data)
-
-            if count >= len(self.SDOs.splitlines()) // 2:
-                (keep_going, _skip) = slaveSDO_progress.Update(count, "Please waiting a moment!!")
-            else:
-                (keep_going, _skip) = slaveSDO_progress.Update(count)
-
-            # If user click "Cancel" loop suspend immediately
-            if not keep_going:
-                break
-
-        slaveSDO_progress.Destroy()
-        return keep_going
-
-    def StringTest(self, check_string):
-        """
-        Test value 'name' is alphanumeric
-        @param check_string : input data for check
-        @return result : output data after check
-        """
-        # string.printable is print this result
-        # '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
-        # !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c
-        allow_range = string.printable
-        result = check_string
-        for i in range(0, len(check_string)):
-            # string.isalnum() is check whether string is alphanumeric or not
-            if check_string[len(check_string)-1-i:len(check_string)-i] in allow_range:
-                result = check_string[:len(check_string) - i]
-                break
-        return result
-
-
-# -------------------------------------------------------------------------------
+            self.SetSDOTraceValues(self.SDOMonitorEntries)
+            self.Controler.GetCTRoot()._connector.GetSDOData()
+            self.SDOTraceThread = Thread(target=self.SDOMonitorThreadProc)
+            self.SDOMonitoringFlag = True
+            self.SDOTraceThread.start()
+        return check_connect_flag
+
+    def SDOMonitoringThreadOff(self):
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
+        if check_connect_flag:
+            self.SDOMonitoringFlag = False
+            if self.SDOTraceThread is not None:
+                self.SDOTraceThread.join()
+            self.SDOTraceThread = None
+            self.Controler.GetCTRoot()._connector.StopSDOThread()
+
+    def SetSDOTraceValues(self, SDOMonitorEntries):
+        SlavePos = self.Controler.GetSlavePos()
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = True)
+        if check_connect_flag:
+            self.Controler.GetCTRoot()._connector.SetSDOTraceValues(SDOMonitorEntries, SlavePos)
+
+    def SDOMonitorThreadProc(self):
+        while self.SDOMonitoringFlag and self.Controler.GetCTRoot()._connector.PLCStatus != "Started":
+            self.SDOValuesList = self.Controler.GetCTRoot()._connector.GetSDOData()
+            LocalData = self.SDOValuesList[0].items()
+            LocalData.sort()
+            if self.SDOValuesList[1] != self.Controler.GetSlavePos():
+                continue
+            row = 0
+            for (idx, subidx), data in LocalData:
+                self.SDOMonitorGrid.SetCellValue(row, 6, str(data["value"]))
+                row += 1
+            time.sleep(0.5)
+
+    def CreateSDOMonitorGrid(self):
+        """
+        It creates SDO Monitor table and specifies cell size and labels.
+        """
+        self.SDOMonitorGrid.CreateGrid(0,7) 
+        SDOCellSize = [(0, 65), (1, 65), (2, 50), (3, 70), 
+                         (4, 40), (5, 450), (6, 85)]
+
+        for (index, size) in SDOCellSize:
+            self.SDOMonitorGrid.SetColSize(index, size)
+        
+        self.SDOMonitorGrid.SetRowLabelSize(0)
+
+        SDOTableLabel = [(0, "Index"), (1, "Subindex"), (2, "Access"),
+                         (3, "Type"), (4, "Size"), (5, "Name"), (6, "Value")]
+        
+        for (index, label) in SDOTableLabel:
+            self.SDOMonitorGrid.SetColLabelValue(index, label)
+            self.SDOMonitorGrid.SetColLabelAlignment(index, wx.ALIGN_CENTER)
+
+    def onMonitorGridDoubleClick(self, event):
+        """
+        Event Handler for double click on the SDO entries table.
+        It adds the entry into the SDO monitor table.
+        If the entry is already in the SDO monitor table, 
+        then it's removed from the SDO monitor table.
+        @pram event: gridlib.EVT_GRID_CELL_LEFT_DCLICK object
+        """
+        row = event.GetRow()
+        idx = self.SDOMonitorGrid.GetCellValue(row, 0)
+        subIdx = self.SDOMonitorGrid.GetCellValue(row, 1)
+        
+        del self.SDOMonitorEntries[(idx, subIdx)]
+        self.SDOMonitorGrid.DeleteRows(row, 1)
+        # add jblee
+        self.SetSDOTraceValues(self.SDOMonitorEntries)
+        self.SDOMonitorGrid.Refresh()
+
+    def SDOParserXML(self, entries):
+        """
+        Parse SDO data set that obtain "ESI file"
+        @param entries: SDO entry list 
+        """  
+        entries_list = entries.items()
+        entries_list.sort()
+        self.ForDefaultValueFlag = False
+        self.CompareValue = ""
+        self.sub_entry_value_list = []
+
+        for (index, subidx), entry in entries_list:
+            # exclude entry that isn't in the objects
+            check_mapping = entry["PDOMapping"]
+            if check_mapping is "T" or check_mapping is "R":
+                if "PDO index" not in entry.keys():
+                    continue
+
+            idx = "0" + entry["Index"].strip("#")
+            #subidx = hex(int(entry["SubIndex"], 0))
+            try :        
+                subidx = "0x" + entry["SubIndex"]
+            except :
+                subidx = "0x0"
+            datatype = entry["Type"]
+
+            try :
+                default_value = entry["DefaultData"]
+            except :
+                default_value = " --- "
+            # Result of SlaveSDO data parsing. (data type : dictionary)
+            self.Data = {'idx':idx, 'subIdx':subidx, 'access':entry["Access"], 
+                         'type':datatype, 'size': str(entry["BitSize"]), 
+                         'name':entry["Name"], 'value':default_value}
+            category_divide_value = [0x1000, 0x2000, 0x6000, 0xa000, 0xffff]
+                
+            for count in range(len(category_divide_value)) :
+                if int(idx, 0) < category_divide_value[count]:
+                    self.Controler.CommonMethod.SDOVariables[count].append(self.Data)
+                    break
+
+        self.Controler.CommonMethod.SDOSubEntryData = self.sub_entry_value_list
+    
+#-------------------------------------------------------------------------------
 #                    For SDO Notebook (divide category)
 # -------------------------------------------------------------------------------
 class SDONoteBook(wx.Notebook):
@@ -494,17 +586,35 @@
 
         self.DeleteAllPages()
 
+        self.Controler.CommonMethod.SDOVariables[5] = []
+        for i in range(4):
+            self.Controler.CommonMethod.SDOVariables[5] += self.Controler.CommonMethod.SDOVariables[i]
+            
         for txt, count in page_texts:
-            self.Data = self.Controler.CommonMethod.SaveSDOData[count]
-            self.Win = SlaveSDOTable(self, self.Data)
+            self.Data = self.Controler.CommonMethod.SDOVariables[count]
+            self.SubEntryData = self.Controler.CommonMethod.SDOSubEntryData
+            self.Win = SlaveSDOTable(self, self.Data, self.SubEntryData)
             self.AddPage(self.Win, txt)
 
+    # add jblee
+    def OnPageChanged(self, event):
+        old = event.GetOldSelection()
+        new = event.GetSelection()
+        sel = self.GetSelection()
+        self.parent.SDOPageNum = new
+        event.Skip()
+
+    def OnPageChanging(self, event):
+        old = event.GetOldSelection()
+        new = event.GetSelection()
+        sel = self.GetSelection()
+        event.Skip()
 
 # -------------------------------------------------------------------------------
 #                    For SDO Grid (fill index, subindex, etc...)
 # -------------------------------------------------------------------------------
 class SlaveSDOTable(wx.grid.Grid):
-    def __init__(self, parent, data):
+    def __init__(self, parent, data, fixed_value):
         """
         Constructor
         @param parent: Reference to the parent SDOPanelClass class
@@ -518,30 +628,31 @@
         self.SDOFlag = True
         if data is None:
             self.SDOs = []
-        else:
+            self.sub_entry_value = []
+        else :
             self.SDOs = data
-
-        self.CreateGrid(len(self.SDOs), 8)
-        SDOCellSize = [(0, 65), (1, 65), (2, 50), (3, 55),
-                       (4, 40), (5, 200), (6, 250), (7, 85)]
-
+            self.sub_entry_value = fixed_value
+        
+        self.CreateGrid(len(self.SDOs), 7)
+        SDOCellSize = [(0, 65), (1, 65), (2, 50), (3, 70), 
+                         (4, 40), (5, 400), (6, 135)]
+        
         for (index, size) in SDOCellSize:
             self.SetColSize(index, size)
 
         self.SetRowLabelSize(0)
 
         SDOTableLabel = [(0, "Index"), (1, "Subindex"), (2, "Access"),
-                         (3, "Type"), (4, "Size"), (5, "Category"),
-                         (6, "Name"), (7, "Value")]
+                         (3, "Type"), (4, "Size"), (5, "Name"), (6, "Value")]
 
         for (index, label) in SDOTableLabel:
             self.SetColLabelValue(index, label)
-            self.SetColLabelAlignment(index, wx.ALIGN_CENTRE)
+            self.SetColLabelAlignment(index, wx.ALIGN_CENTER)
 
         attr = wx.grid.GridCellAttr()
 
-        # for SDO download
-        self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.SDOModifyDialog)
+        # for SDO download and monitoring 
+        self.Bind(gridlib.EVT_GRID_CELL_LEFT_DCLICK, self.onGridDoubleClick)
 
         for i in range(7):
             self.SetColAttr(i, attr)
@@ -554,56 +665,45 @@
         """
         Cell is filled by new parsing data
         """
-        sdo_list = ['idx', 'subIdx', 'access', 'type', 'size', 'category', 'name', 'value']
+        sdo_list = ['idx', 'subIdx', 'access', 'type', 'size', 'name', 'value']
+        count = 0
         for row_idx in range(len(self.SDOs)):
             for col_idx in range(len(self.SDOs[row_idx])):
-                self.SetCellValue(row_idx, col_idx, self.SDOs[row_idx][sdo_list[col_idx]])
+                
+                # the top entries that have sub index is shaded.
+                if int(self.SDOs[row_idx]['subIdx'], 16) == 0x00:
+                    try:
+                        if int(self.SDOs[row_idx + 1]['subIdx'], 16) is not 0x00:
+                            self.SetCellBackgroundColour(row_idx, col_idx, \
+                                                                wx.LIGHT_GREY)
+                    except:
+                        pass
+
+                if self.SDOs[row_idx][sdo_list[col_idx]] == "modifying":
+                    if len(self.sub_entry_value) == count:
+                        continue
+                    self.SetCellValue(row_idx, col_idx, self.sub_entry_value[count])
+                    count += 1
+                else :
+                    self.SetCellValue(row_idx, col_idx, \
+                                  self.SDOs[row_idx][sdo_list[col_idx]])
+
                 self.SetReadOnly(row_idx, col_idx, True)
                 if col_idx < 5:
                     self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
 
     def CheckSDODataAccess(self, row):
         """
-        CheckSDODataAccess method is checking that access data has "w"
-        Access field consist 6 char, if mean
-           rw      rw     rw
-        (preop) (safeop) (op)
-        Example Access field : rwrwrw, rwrw--
+        check that access field has "rw"
         @param row : Selected cell by user
         @return Write_flag : If data has "w", flag is true
         """
-        write_flag = False
         check = self.SDOs[row]['access']
-        if check[1:2] == 'w':
-            self.Controler.CommonMethod.Check_PREOP = True
-            write_flag = True
-        if check[3:4] == 'w':
-            self.Controler.CommonMethod.Check_SAFEOP = True
-            write_flag = True
-        if check[5:] == 'w':
-            self.Controler.CommonMethod.Check_OP = True
-            write_flag = True
-
-        return write_flag
-
-    def DecideSDODownload(self, state):
-        """
-        compare current state and "access" field,
-        result notify SDOModifyDialog method
-        @param state : current slave state
-        @return True or False
-        """
-        # Example of 'state' parameter : "0  0:0  PREOP  +  EL9800 (V4.30) (PIC24, SPI, ET1100)"
-        state = state[self.Controler.GetSlavePos()].split("  ")[2]
-        if state == "PREOP" and self.Controler.CommonMethod.Check_PREOP:
+        if check == "rw":
             return True
-        elif state == "SAFEOP" and self.Controler.CommonMethod.Check_SAFEOP:
-            return True
-        elif state == "OP" and self.Controler.CommonMethod.Check_OP:
-            return True
-
-        return False
-
+        else:
+            return False
+    
     def ClearStateFlag(self):
         """
         Initialize StateFlag
@@ -613,51 +713,112 @@
         self.Controler.CommonMethod.Check_SAFEOP = False
         self.Controler.CommonMethod.Check_OP = False
 
-    def SDOModifyDialog(self, event):
+    def onGridDoubleClick (self, event):
         """
         Create dialog for SDO value modify
-        if user enter data, perform command "ethercat download"
-        @param event : wx.grid.EVT_GRID_CELL_LEFT_DCLICK object
+        if user enter data, perform command "ethercat download"  
+        @param event : gridlib.EVT_GRID_CELL_LEFT_DCLICK object
         """
         self.ClearStateFlag()
-
-        # CheckSDODataAccess is checking that OD(Object Dictionary) has "w"
-        if event.GetCol() == 7 and self.CheckSDODataAccess(event.GetRow()):
-            dlg = wx.TextEntryDialog(
-                self,
-                _("Enter hex or dec value (if enter dec value, it automatically conversed hex value)"),
-                "SDOModifyDialog",
-                style=wx.OK | wx.CANCEL)
-
-            start_value = self.GetCellValue(event.GetRow(), event.GetCol())
+        
+        # CheckSDODataAccess is checking that OD(Object Dictionary) has "w" 
+        if event.GetCol() == 6 and self.CheckSDODataAccess(event.GetRow()) :    
+            dlg = wx.TextEntryDialog (self, 
+                    "Enter hex or dec value (if enter dec value, " \
+                  + "it automatically conversed hex value)",
+                    "SDOModifyDialog", style = wx.OK | wx.CANCEL)
+
+            start_value = self.GetCellValue(event.GetRow(), event.GetCol()) 
             dlg.SetValue(start_value)
-
+            
             if dlg.ShowModal() == wx.ID_OK:
-                try:
-                    int(dlg.GetValue(), 0)
-                    # check "Access" field
-                    if self.DecideSDODownload(self.Controler.CommonMethod.SlaveState[self.Controler.GetSlavePos()]):
+                # Check whether beremiz connected or not.
+                # If this method is called cyclically, set the cyclic flag true
+                check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
+                if check_connect_flag:
+                    try :
+                        input_val = hex(int(dlg.GetValue(), 0))
                         # Request "SDODownload"
-                        self.Controler.CommonMethod.SDODownload(
-                            self.SDOs[event.GetRow()]['type'],
-                            self.SDOs[event.GetRow()]['idx'],
-                            self.SDOs[event.GetRow()]['subIdx'],
-                            dlg.GetValue())
-
-                        self.SetCellValue(event.GetRow(), event.GetCol(), hex(int(dlg.GetValue(), 0)))
-                    else:
-                        self.Controler.CommonMethod.CreateErrorDialog(_('You cannot SDO download this state'))
-                # Error occured process of "int(variable)"
-                # User input is not hex, dec value
-                except ValueError:
-                    self.Controler.CommonMethod.CreateErrorDialog(_('You can input only hex, dec value'))
-
-
-# -------------------------------------------------------------------------------
-#                 For PDO Monitoring Panel
+                        return_val = self.Controler.CommonMethod.SDODownload(
+                                                self.SDOs[event.GetRow()]["type"], 
+                                                self.SDOs[event.GetRow()]["idx"], 
+                                                self.SDOs[event.GetRow()]["subIdx"], 
+                                                dlg.GetValue())
+                        if return_val is "":
+                            SDOUploadEntry = {"idx" : self.SDOs[event.GetRow()]["idx"],
+                                              "subIdx" : self.SDOs[event.GetRow()]["subIdx"],
+                                              "size" : self.SDOs[event.GetRow()]["size"]
+                                             }
+                            data = self.Controler.GetCTRoot()._connector.GetSDOEntryData(
+                                SDOUploadEntry, self.Controler.GetSlavePos())
+                            hex_val = hex(data)[:-1]                           
+
+                            # download data check
+                            if input_val == hex_val:
+                                display_val = "%s(%d)" % (hex_val, data) 
+                                self.SetCellValue(event.GetRow(), event.GetCol(), 
+                                                  display_val)
+                            else :
+                                self.Controler.CommonMethod.CreateErrorDialog(\
+                                            'SDO Value not completely download, please try again')    
+                        else:
+                            self.Controler.GetCTRoot().logger.write_error(return_val)
+                            
+                    # Error occured process of "int(variable)"
+                    # User input is not hex, dec value
+                    except ValueError:
+                        self.Controler.CommonMethod.CreateErrorDialog(\
+                                            'You can input only hex, dec value')    
+        else:
+            SDOPanel = self.parent.parent
+            row = event.GetRow() 
+            
+            idx = self.SDOs[row]["idx"]
+            subIdx = self.SDOs[row]["subIdx"]
+            SDOPanel.SDOMonitorEntries[(idx, subIdx)] = {
+                                                "access": self.SDOs[row]["access"],
+                                                "type": self.SDOs[row]["type"],
+                                                "size": self.SDOs[row]["size"],
+                                                "name": self.SDOs[row]["name"],
+                                                # add jblee
+                                                "value": ""}
+            
+            del_rows = SDOPanel.SDOMonitorGrid.GetNumberRows()
+            
+            try: 
+                SDOPanel.SDOMonitorGrid.DeleteRows(0, del_rows)
+            except:
+                pass
+
+            SDOPanel.SDOMonitorGrid.AppendRows(len(SDOPanel.SDOMonitorEntries))
+            SDOPanel.SetSDOTraceValues(SDOPanel.SDOMonitorEntries)
+            
+            SME_list = SDOPanel.SDOMonitorEntries.items()
+            SME_list.sort()
+
+            gridRow = 0
+            for (idx, subIdx), entry in SME_list:
+                SDOPanel.SDOMonitorGrid.SetCellValue(gridRow, 0, str(idx))
+                SDOPanel.SDOMonitorGrid.SetCellValue(gridRow, 1, str(subIdx))
+                for col, key in [(2, "access"),
+                                 (3, "type"),
+                                 (4, "size"),
+                                 (5, "name")]:
+                    SDOPanel.SDOMonitorGrid.SetCellValue(gridRow, col, entry[key])
+                for col in range(7):
+                    SDOPanel.SDOMonitorGrid.SetReadOnly(gridRow, col, True)
+                    if col < 5 :
+                        SDOPanel.SDOMonitorGrid.SetCellAlignment(\
+                                        gridRow, col, wx.ALIGN_CENTER, wx.ALIGN_CENTER)
+                gridRow += 1
+            
+            SDOPanel.SDOMonitorGrid.Refresh()
+
+#-------------------------------------------------------------------------------
+#                 For PDO Mapping Panel
 # PDO Class UI  : Panel -> Choicebook (RxPDO, TxPDO) ->
 #                 Notebook (PDO Index) -> Grid (PDO entry)
-# -------------------------------------------------------------------------------
+#-------------------------------------------------------------------------------
 class PDOPanelClass(wx.Panel):
     def __init__(self, parent, controler):
         """
@@ -667,16 +828,152 @@
         """
         wx.Panel.__init__(self, parent, -1)
         self.Controler = controler
-
-        self.PDOMonitoringEditorMainSizer = wx.BoxSizer(wx.VERTICAL)
-        self.PDOMonitoringEditorInnerMainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10)
-
-        self.CallPDOChoicebook = PDOChoicebook(self, controler=self.Controler)
-        self.PDOMonitoringEditorInnerMainSizer.Add(self.CallPDOChoicebook, wx.ALL)
-
-        self.PDOMonitoringEditorMainSizer.Add(self.PDOMonitoringEditorInnerMainSizer)
-
-        self.SetSizer(self.PDOMonitoringEditorMainSizer)
+        sizer = wx.FlexGridSizer(cols=1, hgap=20,rows=3, vgap=20)
+        line = wx.StaticText(self, -1, "\n In order to control Ethercat device, user must select proper PDO set.\
+                                        \n Each PDO sets describe operation modes (CSP, CSV, CST) supported by Ethercat devices.\
+                                      \n\n PDOs have two types, RxPDO and TxPDO.\
+                                        \n  - RxPDO refers to the Receive Process Data Object. It means the control parameters which sent from controller to the EtherCAT Slave device.\
+                                        \n    In general, ControlWord (0x6040), Modes of Operations (0x6060), and TargetPosition (0x607A) are regarded as RxPDO.\
+                                        \n  - TxPDO refers to the Transmit Process Data Object. It used to report status of EtherCAT Slave device to the controller in order to calibrate their next actuation.\
+                                        \n    StatusWord (0x6041), Modes of Operation Display (0x6061), and ActualPosition (0x607A) include in TxPDO.\
+                                      \n\n PDO Mapping feature provides available RxPDO and TxPDO sets which defined in Ethercat slave description XML.\
+                                        \n If there is no selection of PDO set, first set (0x1600, 0x1A00) will be chosen as default configuration.")
+        
+        sizer.Add(line)
+        self.SetSizer(sizer)
+
+class RxPDOPanelClass(wx.Panel):
+    def __init__(self, parent, controler):
+        """
+        Constructor
+        @param parent: Reference to the parent EtherCATManagementTreebook class
+        @param controler: _EthercatSlaveCTN class in EthercatSlave.py
+        """
+        wx.Panel.__init__(self, parent, -1)
+        self.Controler = controler
+
+        # add jblee
+        #self.PDOIndexList = ["RxPDO"]
+        self.PDOIndexList = []
+        self.LoadPDOSelectData()
+
+        #HSAHN ADD. 2015.7.26 PDO Select Function ADD
+        self.Controler.CommonMethod.RequestPDOInfo()
+        self.PDOcheckBox = []
+        self.rx_pdo_entries = self.Controler.CommonMethod.GetRxPDOCategory()
+        if len(self.rx_pdo_entries):
+            for i in range(len(self.rx_pdo_entries)):
+                self.PDOcheckBox.append(wx.CheckBox(self, label=str(hex(self.rx_pdo_entries[i]['pdo_index'])), size=(120,15)))
+                if not self.Controler.SelectedRxPDOIndex and self.rx_pdo_entries[i]['sm'] is not None:
+                    self.PDOcheckBox[-1].SetValue(True)
+                    self.Controler.SelectedRxPDOIndex.append(int(self.PDOcheckBox[-1].GetLabel(), 0))
+                    self.InitSavePDO()
+                elif self.rx_pdo_entries[i]['pdo_index'] in self.Controler.SelectedRxPDOIndex:
+                    self.PDOIndexList.append(str(hex(self.rx_pdo_entries[i]['pdo_index'])))
+                    self.PDOcheckBox[-1].SetValue(True)
+                    
+            for cb in self.PDOcheckBox:
+                self.Bind(wx.EVT_CHECKBOX, self.PDOSelectCheck, cb)
+
+            self.PDOListBox = wx.StaticBox(self, label=_("PDO Mapping Select"))
+            self.PDOListBoxSizer = wx.StaticBoxSizer(self.PDOListBox, orient=wx.HORIZONTAL)
+            self.RxPDOListBox = wx.StaticBox(self, label=_("RxPDO"))
+            self.RxPDOListBoxSizer = wx.StaticBoxSizer(self.RxPDOListBox, orient=wx.VERTICAL)
+            self.RxPDOListBoxInnerSizer = wx.FlexGridSizer(cols=3, hgap=5, rows=10, vgap=5)
+            self.RxPDOListBoxInnerSizer.AddMany(self.PDOcheckBox[0:len(self.rx_pdo_entries)])
+            self.RxPDOListBoxSizer.Add(self.RxPDOListBoxInnerSizer)
+            self.PDOListBoxSizer.Add(self.RxPDOListBoxSizer)
+            self.PDOWarningText = wx.StaticText(self, -1,
+                       "  *Warning*\n\n By default configuration, \n\n first mapping set is selected. \n\n Choose the PDO mapping!",
+                       size=(220, -1))
+            self.PDOListBoxSizer.Add(self.PDOWarningText)
+
+            self.PDOMonitoringEditorMainSizer = wx.BoxSizer(wx.VERTICAL)
+            self.PDOMonitoringEditorInnerMainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10)
+
+        
+            self.CallPDOChoicebook = PDONoteBook(self, controler=self.Controler, name="Rx")
+            self.PDOMonitoringEditorInnerMainSizer.Add(self.CallPDOChoicebook, wx.ALL)
+
+            self.PDOInformationBox = wx.StaticBox(self, label=_("RxPDO Mapping List"))
+            self.PDOInformationBoxSizer = wx.StaticBoxSizer(self.PDOInformationBox, orient=wx.VERTICAL)
+            self.PDOInformationBoxSizer.Add(self.PDOMonitoringEditorInnerMainSizer)
+
+            self.PDOMonitoringEditorMainSizer.Add(self.PDOListBoxSizer)
+            self.PDOMonitoringEditorMainSizer.Add(self.PDOInformationBoxSizer)
+            self.SetSizer(self.PDOMonitoringEditorMainSizer)
+
+            # add jblee
+            self.PDOExcludeCheck()
+        else:
+            sizer = wx.FlexGridSizer(cols=1, hgap=20,rows=3, vgap=20)
+            line = wx.StaticText(self, -1, "\n  This device does not support RxPDO.")
+        
+            sizer.Add(line)
+            self.SetSizer(sizer)
+
+    def LoadPDOSelectData(self):
+        RxPDOData = self.Controler.BaseParams.getRxPDO()
+        RxPDOs = []
+        if RxPDOData != "None":
+            RxPDOs = RxPDOData.split()
+        if RxPDOs :
+            for RxPDO in RxPDOs :
+                self.Controler.SelectedRxPDOIndex.append(int(RxPDO, 0))
+
+    def PDOSelectCheck(self, event):
+        # add jblee for Save User Select
+        cb = event.GetEventObject()
+                         # prevent duplicated check
+        if cb.GetValue() and int(cb.GetLabel(), 0) not in self.Controler.SelectedRxPDOIndex:
+            self.Controler.SelectedRxPDOIndex.append(int(cb.GetLabel(), 0))
+            self.PDOIndexList.append(cb.GetLabel())
+        else:
+            self.Controler.SelectedRxPDOIndex.remove(int(cb.GetLabel(), 0))
+            self.PDOIndexList.remove(cb.GetLabel())
+
+        data = ""
+        for PDOIndex in self.PDOIndexList:            
+            data = data + " " + PDOIndex
+
+        self.Controler.BaseParams.setRxPDO(data)
+        self.Controler.GetCTRoot().CTNRequestSave()
+
+        self.PDOExcludeCheck()
+
+    def InitSavePDO(self):
+        for PDOIndex in self.Controler.SelectedRxPDOIndex:
+            self.PDOIndexList.append(str(hex(PDOIndex)))
+
+        data = ""
+        for PDOIndex in self.PDOIndexList:            
+            data = data + " " + PDOIndex
+
+        self.Controler.BaseParams.setRxPDO(data)
+
+    # 2016.06.21
+    # add jblee for check exclude pdo list
+    def PDOExcludeCheck(self):
+        #files = os.listdir(self.Controler.CTNPath())
+        #filepath = os.path.join(self.Controler.CTNPath(), "DataForPDO.txt")
+        CurIndexs = self.Controler.SelectedRxPDOIndex
+        for CB in self.PDOcheckBox:
+            if len(CB.GetLabel().split()) > 1:
+                CB.Enable()
+                CB.SetLabel(CB.GetLabel().split()[0])
+
+        for pdo in self.rx_pdo_entries:
+            for CurIndex in CurIndexs:
+                if pdo["pdo_index"] == CurIndex:
+                    ex_list = pdo["exclude_list"]
+                    for ex_item in ex_list:
+                        for CB in self.PDOcheckBox:
+                            if CB.GetLabel() == hex(ex_item):
+                                CB.SetLabel(CB.GetLabel() + " (Excluded)")
+                                CB.Disable()
+
+    def RefreshPDOInfo(self):
+        pass
 
     def PDOInfoUpdate(self):
         """
@@ -684,28 +981,142 @@
         """
         self.Controler.CommonMethod.RequestPDOInfo()
         self.CallPDOChoicebook.Destroy()
-        self.CallPDOChoicebook = PDOChoicebook(self, controler=self.Controler)
+        self.CallPDOChoicebook = PDOChoicebook(self, controler=self.Controler, name="Rx")
         self.Refresh()
 
-
-# -------------------------------------------------------------------------------
-#                    For PDO Choicebook (divide Tx, Rx PDO)
-# -------------------------------------------------------------------------------
-class PDOChoicebook(wx.Choicebook):
+class TxPDOPanelClass(wx.Panel):
     def __init__(self, parent, controler):
         """
         Constructor
-        @param parent: Reference to the parent PDOPanelClass class
+        @param parent: Reference to the parent EtherCATManagementTreebook class
         @param controler: _EthercatSlaveCTN class in EthercatSlave.py
         """
-        wx.Choicebook.__init__(self, parent, id=-1, size=(500, 500), style=wx.CHB_DEFAULT)
+        wx.Panel.__init__(self, parent, -1)
         self.Controler = controler
 
-        RxWin = PDONoteBook(self, controler=self.Controler, name="Rx")
-        TxWin = PDONoteBook(self, controler=self.Controler, name="Tx")
-        self.AddPage(RxWin, "RxPDO")
-        self.AddPage(TxWin, "TxPDO")
-
+        # add jblee
+        self.PDOIndexList = []
+        self.LoadPDOSelectData()
+
+        #HSAHN ADD. 2015.7.26 PDO Select Function ADD
+        self.Controler.CommonMethod.RequestPDOInfo()
+        self.PDOcheckBox = []
+        self.tx_pdo_entries = self.Controler.CommonMethod.GetTxPDOCategory()
+        if len(self.tx_pdo_entries):
+            for i in range(len(self.tx_pdo_entries)):
+                self.PDOcheckBox.append(wx.CheckBox(self, label=str(hex(self.tx_pdo_entries[i]['pdo_index'])), size=(120,15)))
+                if not self.Controler.SelectedTxPDOIndex and self.tx_pdo_entries[i]['sm'] is not None:
+                    self.PDOcheckBox[-1].SetValue(True)
+                    self.Controler.SelectedTxPDOIndex.append(int(self.PDOcheckBox[-1].GetLabel(), 0))
+                    self.InitSavePDO()
+                elif self.tx_pdo_entries[i]['pdo_index'] in self.Controler.SelectedTxPDOIndex:
+                    self.PDOIndexList.append(str(hex(self.tx_pdo_entries[i]['pdo_index'])))
+                    self.PDOcheckBox[-1].SetValue(True)
+            for cb in self.PDOcheckBox:
+                self.Bind(wx.EVT_CHECKBOX, self.PDOSelectCheck, cb)
+        
+            self.PDOListBox = wx.StaticBox(self, label=_("PDO Mapping Select"))
+            self.PDOListBoxSizer = wx.StaticBoxSizer(self.PDOListBox, orient=wx.HORIZONTAL)
+            self.TxPDOListBox = wx.StaticBox(self, label=_("TxPDO"))
+            self.TxPDOListBoxSizer = wx.StaticBoxSizer(self.TxPDOListBox, orient=wx.VERTICAL)
+            self.TxPDOListBoxInnerSizer = wx.FlexGridSizer(cols=3, hgap=5, rows=10, vgap=5)
+            self.TxPDOListBoxInnerSizer.AddMany(self.PDOcheckBox[0:len(self.tx_pdo_entries)])
+            self.TxPDOListBoxSizer.Add(self.TxPDOListBoxInnerSizer)
+            self.PDOListBoxSizer.Add(self.TxPDOListBoxSizer)
+            self.PDOWarningText = wx.StaticText(self, -1,
+                       "  *Warning*\n\n By default configuration, \n\n first mapping set is selected. \n\n Choose the PDO mapping!",
+                       size=(220, -1))
+            self.PDOListBoxSizer.Add(self.PDOWarningText)
+
+            self.PDOMonitoringEditorMainSizer = wx.BoxSizer(wx.VERTICAL)
+            self.PDOMonitoringEditorInnerMainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=10)
+
+            self.CallPDOChoicebook = PDONoteBook(self, controler=self.Controler, name="Tx")
+            self.PDOMonitoringEditorInnerMainSizer.Add(self.CallPDOChoicebook, wx.ALL)
+
+            self.PDOInformationBox = wx.StaticBox(self, label=_("TxPDO Mapping List"))
+            self.PDOInformationBoxSizer = wx.StaticBoxSizer(self.PDOInformationBox, orient=wx.VERTICAL)
+            self.PDOInformationBoxSizer.Add(self.PDOMonitoringEditorInnerMainSizer)
+
+            self.PDOMonitoringEditorMainSizer.Add(self.PDOListBoxSizer)
+            self.PDOMonitoringEditorMainSizer.Add(self.PDOInformationBoxSizer)
+            self.SetSizer(self.PDOMonitoringEditorMainSizer)
+
+            # add jblee
+            self.PDOExcludeCheck()
+        else:
+            sizer = wx.FlexGridSizer(cols=1, hgap=20,rows=3, vgap=20)
+            line = wx.StaticText(self, -1, "\n  This device does not support TxPDO.")
+        
+            sizer.Add(line)
+            self.SetSizer(sizer)
+
+    def LoadPDOSelectData(self):
+        TxPDOData = self.Controler.BaseParams.getTxPDO()
+        TxPDOs = []
+        if TxPDOData != "None":
+            TxPDOs = TxPDOData.split()
+        if TxPDOs :
+            for TxPDO in TxPDOs :
+                self.Controler.SelectedTxPDOIndex.append(int(TxPDO, 0))
+
+    def PDOSelectCheck(self, event):
+        # add jblee for Save User Select
+        cb = event.GetEventObject()
+                         # prevent duplicated check
+        if cb.GetValue() and int(cb.GetLabel(), 0) not in self.Controler.SelectedTxPDOIndex:
+            self.Controler.SelectedTxPDOIndex.append(int(cb.GetLabel(), 0))
+            self.PDOIndexList.append(cb.GetLabel())
+        else:
+            self.Controler.SelectedTxPDOIndex.remove(int(cb.GetLabel(), 0))
+            self.PDOIndexList.remove(cb.GetLabel())
+
+        data = ""
+        for PDOIndex in self.PDOIndexList:            
+            data = data + " " + PDOIndex
+
+        self.Controler.BaseParams.setTxPDO(data)
+        self.Controler.GetCTRoot().CTNRequestSave()
+
+        self.PDOExcludeCheck()
+
+    def InitSavePDO(self):
+        for PDOIndex in self.Controler.SelectedTxPDOIndex:
+            self.PDOIndexList.append(str(hex(PDOIndex)))
+
+        data = ""
+        for PDOIndex in self.PDOIndexList:            
+            data = data + " " + PDOIndex
+
+        self.Controler.BaseParams.setTxPDO(data)
+
+    # 2016.06.21
+    # add jblee for check exclude pdo list
+    def PDOExcludeCheck(self):
+        CurIndexs = self.Controler.SelectedTxPDOIndex
+        for CB in self.PDOcheckBox:
+            if len(CB.GetLabel().split()) > 1:
+                CB.Enable()
+                CB.SetLabel(CB.GetLabel().split()[0])
+
+        for pdo in self.tx_pdo_entries:
+            for CurIndex in CurIndexs:
+                if pdo["pdo_index"] == CurIndex:
+                    ex_list = pdo["exclude_list"]
+                    for ex_item in ex_list:
+                        for CB in self.PDOcheckBox:
+                            if CB.GetLabel() == hex(ex_item):
+                                CB.SetLabel(CB.GetLabel() + " (Excluded)")
+                                CB.Disable()
+
+    def PDOInfoUpdate(self):
+        """
+        Call RequestPDOInfo method and create Choicebook
+        """
+        self.Controler.CommonMethod.RequestPDOInfo()
+        self.CallPDOChoicebook.Destroy()
+        self.CallPDOChoicebook = PDOChoicebook(self, controler=self.Controler, name="Tx")
+        self.Refresh()
 
 # -------------------------------------------------------------------------------
 #                    For PDO Notebook (divide PDO index)
@@ -718,14 +1129,12 @@
         @param name: identifier whether RxPDO or TxPDO
         @param controler: _EthercatSlaveCTN class in EthercatSlave.py
         """
-        wx.Notebook.__init__(self, parent, id=-1, size=(640, 400))
+        wx.Notebook.__init__(self, parent, id=-1, size=(600, 400))
         self.Controler = controler
 
         count = 0
         page_texts = []
 
-        self.Controler.CommonMethod.RequestPDOInfo()
-
         if name == "Tx":
             # obtain pdo_info and pdo_entry
             # pdo_info include (PDO index, name, number of entry)
@@ -820,12 +1229,10 @@
                 self.SetRowSize(row_idx, 25)
             start_value += 1
 
-
-# -------------------------------------------------------------------------------
-#                    For EEPROM Access Main Panel
-#                 (This class explain EEPROM Access)
-# -------------------------------------------------------------------------------
-class EEPROMAccessPanel(wx.Panel):
+#-------------------------------------------------------------------------------
+#                    For MDP Main Panel         
+#-------------------------------------------------------------------------------  
+class MDPPanel(wx.Panel):
     def __init__(self, parent, controler):
         """
         Constructor
@@ -833,6 +1240,185 @@
         @param controler: _EthercatSlaveCTN class in EthercatSlave.py
         """
         wx.Panel.__init__(self, parent, -1)
+        self.parent = parent
+        self.Controler = controler
+
+        sizer = wx.FlexGridSizer(cols=3, hgap=20, rows=1, vgap=20)
+
+        # Include Module ListBox
+        leftInnerSizer = wx.FlexGridSizer(cols=1, hgap=10,rows=2, vgap=10)
+        # Include Add, Delete Button
+        middleInnerSizer = wx.FlexGridSizer(cols=1, hgap=10,rows=2, vgap=10)
+        # Include Slot ListBox
+        rightInnerSizer = wx.FlexGridSizer(cols=1, hgap=10,rows=2, vgap=10)
+        
+        # Get Module Name as Array
+        # MDPArray = {SlaveName, [data0, data1, ...], SlotIndexIncrement, SlotPdoIncrement}
+        # data = [ModuleName, ModuleInfo, [PDOInfo1, PDOInfo2, ...]]
+        # PDOInfo = {Index, Name, BitSize, Access, PDOMapping, SubIndex, Type}
+        slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
+        type_infos = slave.getType()
+        MDPArray = self.Controler.CTNParent.CTNParent.GetMDPInfos(type_infos)
+
+        NameSet = []
+        if MDPArray:
+            for info in MDPArray[0][1]:
+                NameSet.append(info[0])
+
+        # Module ListBox
+        self.ModuleLabel = wx.StaticText(self, -1, "Module")
+        self.ModuleListBox = wx.ListBox(self, size = (150, 200), choices = NameSet)
+        #self.ModuleListBox = wx.ListBox(self, size = (150, 200), choices = [])
+        self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnModuleListBoxDCClick, self.ModuleListBox)
+
+        # Button
+        self.AddButton = wx.Button(self, label=_(" Add Module  "))
+        self.DeleteButton = wx.Button(self, label=_("Delete Module"))
+
+        # Button Event Mapping
+        self.AddButton.Bind(wx.EVT_BUTTON, self.OnAddButton)
+        self.DeleteButton.Bind(wx.EVT_BUTTON, self.OnDeleteButton)
+
+        # Slot ListBox
+        self.SlotLabel = wx.StaticText(self, -1, "Slot")
+        self.SlotListBox = wx.ListBox(self, size = (150, 200))
+        self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnSlotListBoxDCClick, self.SlotListBox)
+        self.SelectModule = []
+
+        # Add Object Each Sizer
+        leftInnerSizer.AddMany([self.ModuleLabel, self.ModuleListBox])
+        middleInnerSizer.Add(self.AddButton, 0, wx.TOP, 100)
+        middleInnerSizer.Add(self.DeleteButton, 0, wx.BOTTOM, 120)
+        rightInnerSizer.AddMany([self.SlotLabel, self.SlotListBox])
+        
+        sizer.AddMany([leftInnerSizer, middleInnerSizer, rightInnerSizer])
+        
+        self.SetSizer(sizer)
+
+        self.InitMDPSet()
+
+    def InitMDPSet(self):
+        files = os.listdir(self.Controler.CTNPath())
+        filepath = os.path.join(self.Controler.CTNPath(), "DataForMDP.txt")
+        try:
+            moduleDataFile = open(filepath, 'r')
+            lines = moduleDataFile.readlines()
+
+            for line in lines:
+                if line == "\n":
+                    continue
+                module_pos = line.split()[-1]
+                name_len_limit = len(line) - len(module_pos) - 2
+                module_name = line[0:name_len_limit]
+                
+                self.SelectModule.append((module_name, int(module_pos)))
+
+            localModuleInfo = []        
+            count = 1
+            for (item, pos) in self.SelectModule:
+                slotString = "Slot %d %s : " % (count, item.split()[1]) + item.split()[0]
+                localModuleInfo.append(slotString)
+                count += 1
+            self.SlotListBox.SetItems(localModuleInfo)
+
+        except:
+            moduleDataFile = open(filepath, 'w')
+
+        moduleDataFile.close()
+
+    def OnAddButton(self, event):
+        files = os.listdir(self.Controler.CTNPath())
+        filepath = os.path.join(self.Controler.CTNPath(), "DataForMDP.txt")
+        moduleDataFile = open(filepath, 'w')
+
+        selectNum = self.ModuleListBox.GetSelection()
+        if selectNum >= 0:
+            selectStr = self.ModuleListBox.GetString(selectNum)
+            self.SelectModule.append((selectStr, selectNum))
+            localModuleInfo = []
+            count = 1
+            for (item, pos) in self.SelectModule:
+                slotString = "Slot %d %s : " % (count, item.split()[1]) + item.split()[0]
+                localModuleInfo.append(slotString)
+                count += 1
+            self.SlotListBox.SetItems(localModuleInfo)
+
+        moduleDataFile.close()
+        
+    def OnDeleteButton(self, event):
+        files = os.listdir(self.Controler.CTNPath())
+        filepath = os.path.join(self.Controler.CTNPath(), "DataForMDP.txt")
+        moduleDataFile = open(filepath, 'w')
+
+        selectNum = self.SlotListBox.GetSelection()
+        if selectNum >= 0:
+            selectStr = self.SlotListBox.GetString(selectNum)
+            self.SelectModule.pop(selectNum)
+            localModuleInfo = []
+            count = 1
+            for (item, pos) in self.SelectModule:
+                moduleDataFile.write(item + " " + str(pos) + "\n")
+                slotString = "Slot %d %s : " % (count, item.split()[1]) + item.split()[0]
+                localModuleInfo.append(slotString)
+                count += 1
+            self.SlotListBox.SetItems(localModuleInfo)
+
+        moduleDataFile.close()
+
+    def OnModuleListBoxDCClick(self, event):
+        files = os.listdir(self.Controler.CTNPath())
+        filepath = os.path.join(self.Controler.CTNPath(), "DataForMDP.txt")
+        moduleDataFile = open(filepath, 'w')
+
+        selectNum = self.ModuleListBox.GetSelection()
+        if selectNum >= 0:
+            selectStr = self.ModuleListBox.GetString(selectNum)
+            self.SelectModule.append((selectStr, selectNum))
+            localModuleInfo = []
+            count = 1
+            for (item, pos) in self.SelectModule:
+                moduleDataFile.write(item + " " + str(pos) + "\n")
+                slotString = "Slot %d %s : " % (count, item.split()[1]) + item.split()[0]
+                localModuleInfo.append(slotString)
+                module = self.Controler.CTNParent.CTNParent.GetSelectModule(pos)
+                self.Controler.CommonMethod.SavePDOData(module)
+                count += 1
+            self.SlotListBox.SetItems(localModuleInfo)
+
+        moduleDataFile.close()
+
+    def OnSlotListBoxDCClick(self, event):
+        files = os.listdir(self.Controler.CTNPath())
+        filepath = os.path.join(self.Controler.CTNPath(), "DataForMDP.txt")
+        moduleDataFile = open(filepath, 'w')
+
+        selectNum = self.SlotListBox.GetSelection()
+        if selectNum >= 0:
+            selectStr = self.SlotListBox.GetString(selectNum)
+            self.SelectModule.pop(selectNum)
+            localModuleInfo = []
+            count = 1
+            for (item, pos) in self.SelectModule:
+                moduleDataFile.write(item + " " + str(pos) + "\n")
+                slotString = "Slot %d %s : " % (count, item.split()[1]) + item.split()[0]
+                localModuleInfo.append(slotString)
+                count += 1
+            self.SlotListBox.SetItems(localModuleInfo)
+
+        moduleDataFile.close()
+
+# -------------------------------------------------------------------------------
+#                    For EEPROM Access Main Panel
+#                 (This class explain EEPROM Access)
+# -------------------------------------------------------------------------------
+class EEPROMAccessPanel(wx.Panel):
+    def __init__(self, parent, controler):
+        """
+        Constructor
+        @param parent: Reference to the parent EtherCATManagementTreebook class
+        @param controler: _EthercatSlaveCTN class in EthercatSlave.py
+        """
+        wx.Panel.__init__(self, parent, -1)
         sizer = wx.FlexGridSizer(cols=1, hgap=20, rows=3, vgap=20)
 
         line = wx.StaticText(self, -1, "\n  EEPROM Access is composed to SmartView and HexView. \
@@ -858,22 +1444,24 @@
         self.parent = parent
         self.Controler = controler
 
-        self.PDIType = {
-            0: ['none', '00000000'],
-            4: ['Digital I/O', '00000100'],
-            5: ['SPI Slave', '00000101'],
-            7: ['EtherCAT Bridge (port3)', '00000111'],
-            8: ['uC async. 16bit', '00001000'],
-            9: ['uC async. 8bit', '00001001'],
-            10: ['uC sync. 16bit', '00001010'],
-            11: ['uC sync. 8bit', '00001011'],
-            16: ['32 Digtal Input and 0 Digital Output', '00010000'],
-            17: ['24 Digtal Input and 8 Digital Output', '00010001'],
-            18: ['16 Digtal Input and 16 Digital Output', '00010010'],
-            19: ['8 Digtal Input and 24 Digital Output', '00010011'],
-            20: ['0 Digtal Input and 32 Digital Output', '00010100'],
-            128: ['On-chip bus', '11111111']
-        }
+        # PDI Type 1, 13 unknown Type, Fix Future
+        self.PDIType = {0  :['none', '00000000'], 
+                        1  :['unknown', '00000000'],
+                        4  :['Digital I/O', '00000100'],
+                        5  :['SPI Slave', '00000101'],
+                        7  :['EtherCAT Bridge (port3)', '00000111'],
+                        8  :['uC async. 16bit', '00001000'],
+                        9  :['uC async. 8bit', '00001001'],
+                        10 :['uC sync. 16bit', '00001010'],
+                        11 :['uC sync. 8bit', '00001011'],
+                        13 :['unknown', '00000000'],
+                        16 :['32 Digtal Input and 0 Digital Output', '00010000'],
+                        17 :['24 Digtal Input and 8 Digital Output', '00010001'],
+                        18 :['16 Digtal Input and 16 Digital Output','00010010'],
+                        19 :['8 Digtal Input and 24 Digital Output', '00010011'],
+                        20 :['0 Digtal Input and 32 Digital Output', '00010100'],
+                        128:['On-chip bus', '11111111']
+                        }
 
         sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=2, vgap=5)
         button_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=5)
@@ -935,7 +1523,8 @@
         @param event : wx.EVT_BUTTON object
         """
         # Check whether beremiz connected or not.
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(False)
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
         if check_connect_flag:
             self.SiiBinary = self.Controler.CommonMethod.LoadData()
             self.SetEEPROMData()
@@ -1257,7 +1846,8 @@
         @param event : wx.EVT_BUTTON object
         """
         # Check whether beremiz connected or not.
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(False)
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
         if check_connect_flag:
             # load from EEPROM data and parsing
             self.SiiBinary = self.Controler.CommonMethod.LoadData()
@@ -1274,7 +1864,8 @@
         """
         # Check whether beremiz connected or not,
         # and whether status is "Started" or not.
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(False)
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
         if check_connect_flag:
             status, _log_count = self.Controler.GetCTRoot()._connector.GetPLCstatus()
             if status is not PlcStatus.Started:
@@ -1439,7 +2030,7 @@
         # flag for compact view
         self.CompactFlag = False
 
-        # main grid의 rows and cols
+        # main grid rows and cols
         self.MainRow = [512, 512, 512, 512]
         self.MainCol = 4
 
@@ -1675,7 +2266,8 @@
         @param event: wx.EVT_BUTTON object
         """
         # Check whether beremiz connected or not.
-        check_connect_flag = self.Controler.CommonMethod.CheckConnect(False)
+        # If this method is called cyclically, set the cyclic flag true
+        check_connect_flag = self.Controler.CommonMethod.CheckConnect(cyclic_flag = False)
         if check_connect_flag:
             self.LoadData()
             self.BasicSetData()
@@ -2059,8 +2651,7 @@
         @param parent: wx.ScrollWindow object
         @Param controler: _EthercatSlaveCTN class in EthercatSlave.py
         """
-        wx.Panel.__init__(self, parent, -1, (0, 0),
-                          size=wx.DefaultSize, style=wx.SUNKEN_BORDER)
+        wx.Panel.__init__(self, parent)
         self.Controler = controler
         self.parent = parent
         self.StaticBox = {}
@@ -2070,21 +2661,25 @@
         # ----------------------- Main Sizer and Update Button --------------------------------------------
         self.MasterStateSizer = {"main": wx.BoxSizer(wx.VERTICAL)}
         for key, attr in [
-                ("innerMain",           [1, 10, 2, 10]),
-                ("innerTopHalf",        [2, 10, 1, 10]),
-                ("innerBottomHalf",     [2, 10, 1, 10]),
-                ("innerMasterState",    [2, 10, 3, 10]),
-                ("innerDeviceInfo",     [4, 10, 3, 10]),
-                ("innerFrameInfo",      [4, 10, 5, 10])]:
+            ("innerTop",            [2, 10, 1, 10]),
+            ("innerMiddle",         [1, 10, 1, 10]),
+            ("innerBottom",         [1, 10, 1, 10]),
+            ("innerMasterState",    [2, 10, 3, 10]),
+            ("innerDeviceInfo",     [4, 10, 3, 10]),
+            ("innerFrameInfo",      [4, 10, 5, 10]),
+            ("innerSlaveInfo",     [1, 10, 2, 10])]:
             self.MasterStateSizer[key] = wx.FlexGridSizer(cols=attr[0], hgap=attr[1], rows=attr[2], vgap=attr[3])
 
-        self.UpdateButton = wx.Button(self, label=_('Update'))
-        self.UpdateButton.Bind(wx.EVT_BUTTON, self.OnButtonClick)
-
-        for key, label in [
-                ('masterState', 'EtherCAT Master State'),
-                ('deviceInfo', 'Ethernet Network Card Information'),
-                ('frameInfo', 'Network Frame Information')]:
+        self.MSUpdateButton = wx.Button(self, label=_("Update"))
+        self.MSUpdateButton.Bind(wx.EVT_BUTTON, self.OnMSUpdateButtonClick)
+        self.SIUpdateButton = wx.Button(self, label=_("Update"))
+        self.SIUpdateButton.Bind(wx.EVT_BUTTON, self.OnSIUpdateButtonClick)
+        
+        for key, label in [                
+            ("masterState", "EtherCAT Master State"),
+            ("deviceInfo", "Ethernet Network Card Information"),
+            ("frameInfo", "Network Frame Information"),
+            ("slaveInfo", "Slave Information")]: 
             self.StaticBox[key] = wx.StaticBox(self, label=_(label))
             self.MasterStateSizer[key] = wx.StaticBoxSizer(self.StaticBox[key])
 
@@ -2114,10 +2709,12 @@
 
         # ----------------------- Network Frame Information -----------------------------------------------
         for key, label in [
-                ('Tx frame rate [1/s]', 'Tx Frame Rate [1/s]:'),
-                ('Rx frame rate [1/s]', 'Tx Rate [kByte/s]:'),
-                ('Loss rate [1/s]', 'Loss Rate [1/s]:'),
-                ('Frame loss [%]', 'Frame Loss [%]:')]:
+            ("Tx frame rate [1/s]", "Tx Frame Rate [1/s]:"), 
+            ("Tx rate [KByte/s]", "Tx Rate [KByte/s]:"), 
+            ("Rx frame rate [1/s]", "Rx Frame Rate [1/s]:"), 
+            ("Rx rate [KByte/s]", "Rx Rate [KByte/s]:"), 
+            ("Loss rate [1/s]", "Loss Rate [1/s]:"),
+            ("Frame loss [%]", "Frame Loss [%]:")]:
             self.StaticText[key] = wx.StaticText(self, label=_(label))
             self.MasterStateSizer['innerFrameInfo'].Add(self.StaticText[key])
             self.TextCtrl[key] = {}
@@ -2127,21 +2724,30 @@
 
         self.MasterStateSizer['frameInfo'].AddSizer(self.MasterStateSizer['innerFrameInfo'])
 
+        # ------------------------------- Slave Information  -----------------------------------------------
+        self.SITreeListCtrl = SITreeListCtrl(self, self.Controler) 
+        self.MasterStateSizer["innerSlaveInfo"].AddMany([self.SIUpdateButton,
+                                                               self.SITreeListCtrl])
+        self.MasterStateSizer["slaveInfo"].AddSizer(
+                self.MasterStateSizer["innerSlaveInfo"]) 
+
         # --------------------------------- Main Sizer ----------------------------------------------------
+        self.MasterStateSizer["main"].Add(self.MSUpdateButton)
         for key, sub, in [
-                ('innerTopHalf', ['masterState', 'deviceInfo']),
-                ('innerBottomHalf', ['frameInfo']),
-                ('innerMain', ['innerTopHalf', 'innerBottomHalf'])
-        ]:
+            ("innerTop", [
+                    "masterState", "deviceInfo"]),
+            ("innerMiddle", [
+                    "frameInfo"]),
+            ("innerBottom", [
+                    "slaveInfo"]),
+            ("main", [
+                    "innerTop", "innerMiddle", "innerBottom"])]:
             for key2 in sub:
                 self.MasterStateSizer[key].AddSizer(self.MasterStateSizer[key2])
 
-        self.MasterStateSizer['main'].AddSizer(self.UpdateButton)
-        self.MasterStateSizer['main'].AddSizer(self.MasterStateSizer['innerMain'])
-
-        self.SetSizer(self.MasterStateSizer['main'])
-
-    def OnButtonClick(self, event):
+        self.SetSizer(self.MasterStateSizer["main"])
+    
+    def OnMSUpdateButtonClick(self, event):
         """
         Handle the event of the 'Update' button.
         Update the data of the master state.
@@ -2159,3 +2765,818 @@
                         self.TextCtrl[key].SetValue(self.MasterState[key][0])
         else:
             self.Controler.CommonMethod.CreateErrorDialog(_('PLC not connected!'))
+
+    def OnSIUpdateButtonClick(self, event):
+        """
+        Handle the event of the radio box in the slave information 
+        @param event: wx.EVT_RADIOBOX object
+        """
+        if self.Controler.GetCTRoot()._connector is not None:
+            self.SITreeListCtrl.UpdateSI()
+        
+        else :
+            self.Controler.CommonMethod.CreateErrorDialog('PLC not connected!')
+    
+
+#-------------------------------------------------------------------------------
+#                    For Slave Information  Panel
+#------------------------------------------------------------------------------- 
+class SITreeListCtrl(wx.Panel):
+    
+    EC_Addrs = ["0x0300", "0x0302", "0x0304", "0x0306", "0x0301", "0x0303", "0x0305", 
+                "0x0307", "0x0308", "0x0309", "0x030A", "0x030B", "0x030C", "0x030D",
+                "0x0310", "0x0311", "0x0312", "0x0313", "0x0442", "0x0443"]
+
+    def __init__(self, parent, controler):
+        """
+        Constructor
+        @param parent: Reference to the MasterStatePanel class
+        @param Controler: _EthercatCTN class in EthercatMaster.py
+        """
+
+        wx.Panel.__init__(self, parent, -1, size=wx.Size(750, 350))
+
+        self.Controler=controler
+        
+        self.Tree = wx.gizmos.TreeListCtrl(self, -1, size=wx.Size(750,350), 
+                                                            style=wx.TR_HAS_BUTTONS
+                                                            |wx.TR_HIDE_ROOT
+                                                            |wx.TR_ROW_LINES
+                                                            |wx.TR_COLUMN_LINES
+                                                            |wx.TR_FULL_ROW_HIGHLIGHT)
+        for label, width in [
+                ("name",        400),
+                ("position",    100),
+                ("state",       100),
+                ("error",       100)]:
+            self.Tree.AddColumn(label, width=width)
+        
+        self.Tree.SetMainColumn(0)
+        
+    def UpdateSI(self):
+        """
+        Update the data of the slave information.
+        """
+        position, not_used, state, not_used, name = range(5)
+        
+        slave_node = []
+        slave_info_list = []
+        error_counter= []
+        
+        # get slave informations (name, position, state)
+        slaves_infos = self.Controler.CommonMethod.GetSlaveStateFromSlave()
+        slave_info_lines = slaves_infos.splitlines()
+        
+        for line in slave_info_lines:
+            slave_info_list.append(line.split(None,4))
+
+        slave_num = len(slave_info_lines)
+        
+        reg_info = []
+        for ec in self.EC_Addrs:
+            reg_info.append(ec + ",0x001")
+        
+        # get error counts of slaves
+        err_count_list = self.Controler.CommonMethod.MultiRegRead(slave_num, reg_info)
+                
+        self.Tree.DeleteAllItems()
+        
+        root = self.Tree.AddRoot("") 
+        ec_list_idx = 0
+
+        for slave_idx in range(slave_num):
+            slave_node = self.Tree.AppendItem(root, "")
+            
+            # set name, postion, state 
+            col_num = 0 
+            for info_idx in [name, position, state]:
+                self.Tree.SetItemText(slave_node, 
+                                      slave_info_list[slave_idx][info_idx], col_num)
+                col_num += 1
+
+            error_counter = {}
+            ec_idx = 0
+            
+            # set error counter's name and default value 
+            for ec, sub_ecs in [("Port Error Counters 0/1/2/3",[
+                                    "Invaild Frame Counter 0/1/2/3",
+                                    "RX Error Counter 0/1/2/3"]),
+                                ("Forward RX Error Counter 0/1/2/3", []),       
+                                ("ECAT Processing Unit Error Counter", []),
+                                ("PDI Error Counter", []),
+                                ("Lost Link Counter 0/1/2/3", []),
+                                ("Watchdog Counter Process Data", []),
+                                ("Watchdog Counter PDI", [])]:
+                ec_sub_idx = 0
+                ec_name = ec
+                tree_node = self.Tree.AppendItem(slave_node, "%s" % ec)
+                
+                if ec_name.find("0/1/2/3") > 0:
+                    num_ports = 4
+                    err_count = [0, 0, 0, 0]
+                else:
+                    num_ports = 1
+                    err_count = [0]
+
+                error_counter[(ec_idx, ec_sub_idx)] = {
+                        "name": ec_name,
+                        "tree_node": tree_node,
+                        "num_ports": num_ports,
+                        "err_count": err_count}
+
+                for sub_ec in sub_ecs:
+                    ec_sub_idx += 1
+                    ec_name = sub_ec
+                    tree_node = self.Tree.AppendItem(\
+                        error_counter[(ec_idx, 0)]["tree_node"], 
+                            "%s" % sub_ec)
+                    
+                    if ec_name.find("0/1/2/3") > 0:
+                        num_ports = 4
+                        err_count = [0, 0, 0, 0]
+                    else:
+                        num_ports = 1
+                        err_count = [0]
+
+                    error_counter[(ec_idx, ec_sub_idx)] = {
+                            "name": ec_name,
+                            "tree_node": tree_node, 
+                            "num_ports": num_ports,
+                            "err_count": err_count}
+
+                    for port_num in range(num_ports):
+                        try:
+                            error_counter[(ec_idx, ec_sub_idx)]["err_count"][port_num] += \
+                                    int(err_count_list[ec_list_idx].split(",")[2], 16)
+                        except:
+                            error_counter[(ec_idx, ec_sub_idx)]["err_count"][port_num] = -1
+
+                        ec_list_idx += 1
+                
+                if ec_sub_idx > 0:
+                    for port_num in range(num_ports):
+                        err_sum = 0
+                        for sub_idx in range(1, ec_sub_idx+1):
+                            err_sum += error_counter[(ec_idx, sub_idx)]\
+                                                ["err_count"][port_num]
+                        error_counter[(ec_idx, 0)]["err_count"][port_num] = err_sum
+                        
+                else:
+                    for port_num in range(num_ports):
+                        try:
+                            error_counter[(ec_idx, ec_sub_idx)]["err_count"][port_num] += \
+                                    int(err_count_list[ec_list_idx].split(",")[2], 16)
+                        except:
+                            error_counter[(ec_idx, ec_sub_idx)]["err_count"][port_num] = -1
+                        ec_list_idx += 1
+
+                ec_idx += 1
+            
+            # set texts in "error" column. 
+            ec_info_list = error_counter.items()
+            ec_info_list.sort()
+            
+            err_checker = "none"
+           
+            for (idx, sub_idx), ec_info in ec_info_list:
+                ec_text = ""
+                for port_num in range(ec_info["num_ports"]):
+                    if ec_info["err_count"][port_num] != 0:
+                        err_checker = "occurred"
+
+                    if ec_info["err_count"][port_num] < 0:
+                        ec_text = "reg I/O error"
+                    else:
+                        ec_text = ec_text + "%d/" % ec_info["err_count"][port_num]
+
+                ec_text = ec_text.strip("/")
+                
+                self.Tree.SetItemText(ec_info["tree_node"], ec_text, col_num)
+            
+            self.Tree.SetItemText(slave_node, err_checker, col_num)
+
+class DCConfigPanel(wx.Panel):
+    def __init__(self, parent, controler):
+        """
+        Constructor
+        @param parent: Reference to the MasterStatePanel class
+        @param Controler: _EthercatCTN class in EthercatMaster.py
+        """
+
+        wx.Panel.__init__(self, parent, -1, size=wx.Size(750, 350))
+
+        self.Controler = controler
+        self.parent = parent
+
+        self.ESI_DC_Data = self.Controler.CommonMethod.LoadESIData()
+
+        # initialize SlaveStatePanel UI dictionaries
+        self.StaticBoxDic = {}
+        self.StaticTextDic = {}
+        self.TextCtrlDic = {}
+        self.ComboBoxDic = {}
+        self.CheckBoxDic = {}
+        self.RadioButtonDic = {}
+        OperationModeComboList = []
+        Sync1CycleComboList = []
+        
+        for ESI_Data in self.ESI_DC_Data:
+            OperationModeComboList.append(ESI_Data["desc"])
+
+        UnitComboList = [ "/100", "/ 50", "/ 40", "/ 30", "/ 25", "/ 20", "/16",
+            "/ 10", "/ 8", "/ 5", "/ 4", "/ 3", "/ 2", "x 1", "x 2", "x 3", "x 4", 
+            "x 5", "x 8", "x 10", "x 16", "x 20", "x 25", "x 30", "x 40", "x 50", 
+            "x 100"
+        ]
+
+        UnitComboListPlus = [ "/100", "/ 50", "/ 40", "/ 30", "/ 25", "/ 20", "/16",
+            "/ 10", "/ 8", "/ 5", "/ 4", "/ 3", "/ 2", "x 0", "x 1", "x 2", "x 3", 
+            "x 4", "x 5", "x 8", "x 10", "x 16", "x 20", "x 25", "x 30", "x 40", 
+            "x 50", "x 100"
+        ]
+
+        for i in range(1024):
+            Sync1CycleComboList.append("x " + str(i + 1))
+        
+        # iniitalize BoxSizer and FlexGridSizer
+        self.SizerDic = {
+            "DCConfig_main_sizer" : wx.BoxSizer(wx.VERTICAL),
+            "DCConfig_inner_main_sizer" : wx.FlexGridSizer(cols=1, hgap=50, rows=2, vgap=10),
+            "CyclicMode_InnerSizer" : wx.FlexGridSizer(cols=1, hgap=5, rows=2, vgap=5),
+            "SyncMode_InnerSizer" : wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=5),
+            "OperationMode_InnerSizer" : wx.FlexGridSizer(cols=2, hgap=100, rows=2, vgap=10),
+            "CheckEnable_InnerSizer" : wx.FlexGridSizer(cols=2, hgap=10, rows=1, vgap=10),
+            "Sync0_InnerSizer" : wx.FlexGridSizer(cols=1, hgap=15, rows=3, vgap=10),
+            "Sync0_CycleTimeSizer" : wx.FlexGridSizer(cols=2, hgap=10, rows=2, vgap=5),
+            "Sync0_ShiftTimeSizer" : wx.FlexGridSizer(cols=2, hgap=20, rows=2, vgap=5),
+            "Sync1_InnerSizer" : wx.FlexGridSizer(cols=1, hgap=15, rows=3, vgap=10),
+            "Sync1_CycleTimeSizer" : wx.FlexGridSizer(cols=2, hgap=10, rows=2, vgap=5),
+            "Sync1_ShiftTimeSizer" : wx.FlexGridSizer(cols=2, hgap=20, rows=2, vgap=5)
+        }
+        
+        # initialize StaticBox and StaticBoxSizer
+        for box_name, box_label in [
+                ("CyclicModeBox", "Cyclic Mode"),
+                ("Sync0Box", "Sync0"),
+                ("Sync0CycleTimeBox", "Cycle Time (us):"),
+                ("Sync0ShiftTimeBox", "Shift Time (us):"),
+                ("Sync1Box", "Sync1"),
+                ("Sync1CycleTimeBox", "Cycle Time (us):"),
+                ("Sync1ShiftTimeBox", "Shift Time (us):")
+            ]:
+            self.StaticBoxDic[box_name] = wx.StaticBox(self, label=_(box_label))
+            self.SizerDic[box_name] = wx.StaticBoxSizer(self.StaticBoxDic[box_name])  
+        
+        for statictext_name, statictext_label in [
+                ("MainLabel", "Distributed Clock"),
+                ("OperationModeLabel", "Operation Mode:"),
+                ("SyncUnitCycleLabel", "Sync Unit Cycle (us)"),
+                ("Sync0ShiftTimeUserDefinedLabel", "User Defined"),
+                ("Sync1ShiftTimeUserDefinedLabel", "User Defined"),
+                ("BlankObject", ""),
+                ("BlankObject1", "")
+            ]:
+            self.StaticTextDic[statictext_name] = wx.StaticText(self, label=_(statictext_label))
+
+        for textctl_name in [
+                ("SyncUnitCycle_Ctl"),
+                ("Sync0CycleTimeUserDefined_Ctl"),
+                ("Sync0ShiftTimeUserDefined_Ctl"),
+                ("Sync1CycleTimeUserDefined_Ctl"),
+                ("Sync1ShiftTimeUserDefined_Ctl"),
+            ]:
+            self.TextCtrlDic[textctl_name] = wx.TextCtrl(
+                self, size=wx.Size(130, 24), style=wx.TE_READONLY)
+
+        for checkbox_name, checkbox_label in [
+                ("DCEnable", "Enable"),
+                ("Sync0Enable", "Enable Sync0"),
+                ("Sync1Enable", "Enable Sync1")
+            ]:
+            self.CheckBoxDic[checkbox_name] = wx.CheckBox(self, -1, checkbox_label)
+
+        for combobox_name, combobox_list, size in [
+                ("OperationModeChoice", OperationModeComboList, 250),
+                ("Sync0UnitCycleChoice", UnitComboList, 130),
+                ("Sync1UnitCycleChoice", UnitComboList, 130)
+            ]:
+            self.ComboBoxDic[combobox_name] = wx.ComboBox(self, size=wx.Size(size, 24), 
+                choices = combobox_list, style = wx.CB_DROPDOWN | wx.CB_READONLY)
+
+        for radiobutton_name, radiobutton_label in [
+                ("Sync0CycleTimeUnitRadioButton", "Sync Unit Cycle"),
+                ("Sync0CycleTimeUserDefinedRadioButton", "User Defined"),
+                ("Sync1CycleTimeUnitRadioButton", "Sync Unit Cycle"),
+                ("Sync1CycleTimeUserDefinedRadioButton", "User Defined")
+            ]:
+            self.RadioButtonDic[radiobutton_name] = wx.RadioButton(
+                self, label = radiobutton_label, style = wx.RB_SINGLE)
+
+
+        self.ApplyButton = wx.Button(self, label="Apply")
+
+        # binding event
+        self.Bind(wx.EVT_CHECKBOX, self.CheckDCEnable, self.CheckBoxDic["DCEnable"])
+        #self.Bind(wx.EVT_COMBOBOX, self.SelectOperationMode, self.ComboBoxDic["OperationModeChoice"])
+        #self.Bind(wx.EVT_COMBOBOX, self.SelectUnitCycle, self.ComboBoxDic["Sync0UnitChoice"])
+        self.Bind(wx.EVT_RADIOBUTTON, self.SelectSync0CycleTime, 
+            self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"])
+        self.Bind(wx.EVT_RADIOBUTTON, self.SelectSync0CycleTime, 
+            self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"])
+        self.Bind(wx.EVT_RADIOBUTTON, self.SelectSync1CycleTime, 
+            self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"])
+        self.Bind(wx.EVT_RADIOBUTTON, self.SelectSync1CycleTime, 
+            self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"])
+        self.Bind(wx.EVT_CHECKBOX, self.CheckSync0Enable, self.CheckBoxDic["Sync0Enable"])
+        self.Bind(wx.EVT_CHECKBOX, self.CheckSync1Enable, self.CheckBoxDic["Sync1Enable"])
+        self.Bind(wx.EVT_BUTTON, self.OnClickApplyButton, self.ApplyButton)
+
+        # sync1 shifttime box contents
+        self.SizerDic["Sync1_ShiftTimeSizer"].AddMany([
+            self.StaticTextDic["Sync1ShiftTimeUserDefinedLabel"],
+            self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"]
+        ])
+
+        # sync1 shifttime box
+        self.SizerDic["Sync1ShiftTimeBox"].Add(self.SizerDic["Sync1_ShiftTimeSizer"])
+        
+        # sync1 cycletime box contents
+        self.SizerDic["Sync1_CycleTimeSizer"].AddMany([
+            self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"], 
+            self.ComboBoxDic["Sync1UnitCycleChoice"],
+            self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"],
+            self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"]
+        ])
+
+        # sync0 cycletime box
+        self.SizerDic["Sync1CycleTimeBox"].Add(self.SizerDic["Sync1_CycleTimeSizer"])
+
+        self.SizerDic["Sync1_InnerSizer"].AddMany([
+            self.CheckBoxDic["Sync1Enable"], self.SizerDic["Sync1CycleTimeBox"], 
+            self.SizerDic["Sync1ShiftTimeBox"]
+        ])
+
+        # sync1 box
+        self.SizerDic["Sync1Box"].Add(self.SizerDic["Sync1_InnerSizer"])
+
+        # sync0 shifttime box contents
+        self.SizerDic["Sync0_ShiftTimeSizer"].AddMany([
+            self.StaticTextDic["Sync0ShiftTimeUserDefinedLabel"],
+            self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"]
+        ])
+
+        # sync0 shifttime box
+        self.SizerDic["Sync0ShiftTimeBox"].Add(self.SizerDic["Sync0_ShiftTimeSizer"])
+        
+        # sync0 cycletime box contents
+        self.SizerDic["Sync0_CycleTimeSizer"].AddMany([
+            self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"], 
+            self.ComboBoxDic["Sync0UnitCycleChoice"],
+            self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"],
+            self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"]
+        ])
+
+        # sync0 cycletime box
+        self.SizerDic["Sync0CycleTimeBox"].Add(self.SizerDic["Sync0_CycleTimeSizer"])
+
+        self.SizerDic["Sync0_InnerSizer"].AddMany([
+            self.CheckBoxDic["Sync0Enable"], self.SizerDic["Sync0CycleTimeBox"], 
+            self.SizerDic["Sync0ShiftTimeBox"]
+        ])
+
+        # sync0 box
+        self.SizerDic["Sync0Box"].Add(self.SizerDic["Sync0_InnerSizer"])
+
+        # sync0, sync1 box
+        self.SizerDic["SyncMode_InnerSizer"].AddMany([
+            self.SizerDic["Sync0Box"], self.SizerDic["Sync1Box"]
+        ])
+
+        # CyclicMode Box
+        self.SizerDic["CheckEnable_InnerSizer"].AddMany([
+            self.StaticTextDic["SyncUnitCycleLabel"], 
+            self.TextCtrlDic["SyncUnitCycle_Ctl"]
+        ])
+
+        self.SizerDic["OperationMode_InnerSizer"].AddMany([
+            self.StaticTextDic["OperationModeLabel"], 
+            self.ComboBoxDic["OperationModeChoice"],
+            self.CheckBoxDic["DCEnable"], self.SizerDic["CheckEnable_InnerSizer"]
+        ])
+
+        self.SizerDic["CyclicMode_InnerSizer"].AddMany([
+            self.SizerDic["OperationMode_InnerSizer"], 
+            self.SizerDic["SyncMode_InnerSizer"]
+        ])
+
+        self.SizerDic["CyclicModeBox"].Add(self.SizerDic["CyclicMode_InnerSizer"])
+
+        # Main Sizer
+        self.SizerDic["DCConfig_inner_main_sizer"].AddMany([
+            self.StaticTextDic["MainLabel"], self.ApplyButton,
+            self.SizerDic["CyclicModeBox"]
+        ])
+        
+        self.SizerDic["DCConfig_main_sizer"].Add(self.SizerDic["DCConfig_inner_main_sizer"])
+        
+        self.SetSizer(self.SizerDic["DCConfig_main_sizer"])
+        
+        self.Centre()
+
+        self.UIOnOffSet(False)
+        self.LoadProjectDCData()
+
+    def UIOnOffSet(self, activate):
+        if activate :
+            for object in self.RadioButtonDic:
+                self.RadioButtonDic[object].Enable()
+
+            for object in self.ComboBoxDic:
+                if object == "OperationModeChoice":
+                    continue
+                self.ComboBoxDic[object].Enable()
+
+            for object in self.TextCtrlDic:
+                if object in ["SyncUnitCycle_Ctl", "InputReference_Ctl"]:
+                    continue
+                self.TextCtrlDic[object].Enable()
+
+            for object in self.CheckBoxDic:
+                if object == "DCEnable":
+                    continue
+                self.CheckBoxDic[object].Enable()
+
+        # initial set or DC enable uncheck
+        else :
+            for object in self.RadioButtonDic:
+                self.RadioButtonDic[object].Disable()
+
+            for object in self.ComboBoxDic:
+                if object == "OperationModeChoice":
+                    continue
+                self.ComboBoxDic[object].Disable()
+
+            for object in self.TextCtrlDic:
+                if object == "SyncUnitCycle_Ctl":
+                    continue
+                self.TextCtrlDic[object].Disable()
+
+            for object in self.CheckBoxDic:
+                if object == "DCEnable":
+                    continue
+                self.CheckBoxDic[object].Disable()
+
+            for data in self.ESI_DC_Data:
+                index = self.Controler.ExtractHexDecValue(data["assign_activate"])
+                if index == 0:
+                    config_name = data["desc"]
+                    self.ComboBoxDic["OperationModeChoice"].SetStringSelection(config_name)
+
+    def CheckSync0Enable(self, evt):
+        if evt.GetInt():
+            self.ComboBoxDic["Sync0UnitCycleChoice"].Enable()
+            self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].Enable()
+            self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].Enable()
+            self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].Enable()
+            self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].Enable()
+        else :
+            self.ComboBoxDic["Sync0UnitCycleChoice"].Disable()
+            self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].Disable()
+            self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].Disable()
+            self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].Disable()
+            self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].Disable()
+            self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].SetValue(False)
+            self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].SetValue(False)
+            self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].SetValue("")
+            self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].SetValue("")
+
+    def CheckSync1Enable(self, evt):
+        if evt.GetInt():
+            self.ComboBoxDic["Sync1UnitCycleChoice"].Enable()
+            self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].Enable()
+            self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].Enable()
+            self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Enable()
+            self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].Enable()
+        else :
+            self.ComboBoxDic["Sync1UnitCycleChoice"].Disable()
+            self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].Disable()
+            self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].Disable()
+            self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Disable()
+            self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].Disable()
+            self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].SetValue(False)
+            self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].SetValue(False)
+            self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].SetValue("")
+            self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].SetValue("")
+
+    def CheckDCEnable(self, evt):
+        ns_mode = 1
+        task_cycle_ns = self.GetInterval(ns_mode)
+        sync0_cycle_factor = None
+        sync1_cycle_factor = None
+
+        #task_cycle_ns = self.Controler.GetCTRoot()._Ticktime
+        if (task_cycle_ns > 0):
+            self.UIOnOffSet(evt.GetInt())
+
+            if evt.GetInt():
+                # default select DC enable sync0
+                default_list_num = 0
+                config_name = self.ESI_DC_Data[default_list_num]["desc"]
+                assign_act = self.ESI_DC_Data[default_list_num]["assign_activate"]
+                sync0_cycle_time_ns = self.ESI_DC_Data[default_list_num]["cycletime_sync0"]
+                if sync0_cycle_time_ns == 0 :
+                    sync0_cycle_factor = self.ESI_DC_Data[default_list_num]["cycletime_sync0_factor"]
+                sync0_shift_time_ns = self.ESI_DC_Data[default_list_num]["shifttime_sync0"]
+                sync1_cycle_time_ns = self.ESI_DC_Data[default_list_num]["cycletime_sync1"]
+                if sync1_cycle_time_ns == 0 :
+                    sync1_cycle_factor = self.ESI_DC_Data[default_list_num]["cycletime_sync1_factor"]
+                sync1_shift_time_ns = self.ESI_DC_Data[default_list_num]["shifttime_sync1"]
+
+                cal_assign_act = self.Controler.ExtractHexDecValue(assign_act)
+                sync0_cycle_time_us = str(int(sync0_cycle_time_ns) / 1000)
+                sync0_shift_time_us = str(int(sync0_shift_time_ns) / 1000)
+                sync1_cycle_time_us = str(int(sync1_cycle_time_ns) / 1000)
+                sync1_shift_time_us = str(int(sync1_shift_time_ns) / 1000)
+
+                task_cycle_to_us = str(int(task_cycle_ns) / 1000)
+
+                # DC sync0 mode
+                if cal_assign_act == 768:
+                    # Disable About Sync1 Objects
+                    self.CheckBoxDic["Sync1Enable"].SetValue(False)
+                    self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].Disable()
+                    self.ComboBoxDic["Sync1UnitCycleChoice"].Disable()
+                    self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].Disable()
+                    self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Disable()
+                    self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].Disable()
+
+                else :
+                    self.CheckBoxDic["Sync1Enable"].SetValue(True)
+                    if sync1_cycle_factor is not None:
+                        self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].SetValue(True)
+                        self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].SetValue(False)
+                        self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Disable()
+                        self.SetSyncUnitCycle(sync1_cycle_factor, 
+                            self.ComboBoxDic["Sync1UnitCycleChoice"])
+                    else :
+                        self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].SetValue(False)
+                        self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].SetValue(True)
+                        self.ComboBoxDic["Sync1UnitCycleChoice"].Disable()
+                        self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].SetValue(sync1_cycle_time_us)
+
+                    self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].SetValue(sync1_shift_time_us)
+
+                # Set Sync0 Objects
+                self.CheckBoxDic["Sync0Enable"].SetValue(True)
+                if sync0_cycle_factor is not None:
+                    self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].SetValue(True)
+                    self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].SetValue(False)
+                    self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].Disable()
+                    self.SetSyncUnitCycle(sync0_cycle_factor, 
+                        self.ComboBoxDic["Sync0UnitCycleChoice"])
+                else :
+                    self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].SetValue(False)
+                    self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].SetValue(True)
+                    self.ComboBoxDic["Sync0UnitCycleChoice"].Disable()
+                    self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].SetValue(sync0_cycle_time_us)
+
+                self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].SetValue(sync0_shift_time_us)
+
+                self.ComboBoxDic["OperationModeChoice"].SetStringSelection(config_name)
+                self.TextCtrlDic["SyncUnitCycle_Ctl"].SetValue(task_cycle_to_us)
+            else :
+                self.CheckBoxDic["Sync0Enable"].SetValue(False)
+                self.CheckBoxDic["Sync1Enable"].SetValue(False)
+                self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].SetValue(False)
+                self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].SetValue(False)
+                self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].SetValue(False)
+                self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].SetValue(False)
+                self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].SetValue("")
+                self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].SetValue("")
+                self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].SetValue("")
+                self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].SetValue("")
+
+        else :
+            self.UIOnOffSet(False)
+            #error_str = "DC Enable is not possble, please set task interval"
+            error_str = "Can't Set DC Enable"
+            self.Controler.CommonMethod.CreateErrorDialog(error_str)
+
+    def SetSyncUnitCycle(self, factor, object):
+        # factor > 0 ==> * factor, factor < 0 ==> / factor
+        factor_to_int = int(factor)
+        if factor_to_int > 0:
+            lists = object.GetStrings()
+
+            for token in lists:
+                temp = token.split(" ")
+                if (temp[0] == "x") and (int(temp[1]) == factor_to_int):
+                    object.SetStringSelection(token)
+                    return True
+
+        else : 
+            lists = object.GetStrings()
+
+            for token in lists:
+                temp = token.split(" ")
+                if (temp[0] == "/") and (int(temp[1]) == factor_to_int):
+                    object.SetStringSelection(token)
+                    return True
+
+        return False
+
+    def GetInterval(self, mode):
+        project_infos = self.Controler.GetCTRoot().GetProjectInfos()
+        for project_info_list in project_infos["values"]:
+            if project_info_list["name"] == "Resources" :
+                token = project_info_list["values"][0]["tagname"]
+       
+        tasks, instances = self.Controler.GetCTRoot().GetEditedResourceInfos(token)
+        try:
+            task_cycle_ns = self.ParseTime(tasks[0]["Interval"])
+        except :
+            task_cycle_ns = 0
+        task_cycle_us = int(task_cycle_ns) / 1000
+
+        # mode == 1 ==> return ns
+        # mode == 2 ==> return us
+
+        if mode == 1:
+            return task_cycle_ns
+        if mode == 2:
+            return str(task_cycle_us)
+
+    def ParseTime(self, input):
+        # input example : 't#1ms'
+        # temp.split('#') -> ['t', '1ms']
+        temp = input.split('#')
+         
+        # temp[1] : '1ms'
+        # temp[-2:] : 'ms'
+        # temp[:-2] : '1'
+        if temp[1][-2:] == "ms":
+           # convert nanosecond unit
+           result = int(temp[1][:-2]) * 1000000
+        elif temp[1][-2:] == "us":
+           result = int(temp[1][:-2]) * 1000
+
+        return str(result)
+
+    def SelectSync0CycleTime(self, evt):
+        selected_object = evt.GetEventObject()
+
+        if selected_object.GetLabel() == "User Defined" :
+            self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].SetValue(False)
+            self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].Enable()
+            self.ComboBoxDic["Sync0UnitCycleChoice"].Disable()
+        elif selected_object.GetLabel() == "Sync Unit Cycle" :
+            self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].SetValue(False)
+            self.ComboBoxDic["Sync0UnitCycleChoice"].Enable()
+            self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].Disable()
+
+    def SelectSync1CycleTime(self, evt):
+        selected_object = evt.GetEventObject()
+
+        if selected_object.GetLabel() == "User Defined" :
+            self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].SetValue(False)
+            self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Enable()
+            self.ComboBoxDic["Sync1UnitCycleChoice"].Disable()
+        elif selected_object.GetLabel() == "Sync Unit Cycle" :
+            self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].SetValue(False)
+            self.ComboBoxDic["Sync1UnitCycleChoice"].Enable()
+            self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Disable()
+
+    def GetCycle(self, period, section):
+        temp = section.split(" ")
+
+        if temp[0] == "x":
+            result = int(period) * int(temp[1])
+        elif temp[0] == "/" :
+            result = int(period) / int(temp[1])
+        else :
+            result = ""
+
+        return result
+
+    def OnClickApplyButton(self, evt):
+        us_mode = 2
+        dc_enable = self.CheckBoxDic["DCEnable"].GetValue()
+        dc_desc = self.ComboBoxDic["OperationModeChoice"].GetStringSelection()
+        dc_assign_activate = self.ESI_DC_Data[0]["assign_activate"]
+        dc_assign_activate_mod = dc_assign_activate.split('x')[1]
+
+        if self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].GetValue():
+            temp = self.ComboBoxDic["Sync0UnitCycleChoice"].GetStringSelection()
+            dc_sync0_cycle = "1_" + str(self.GetCycle(self.GetInterval(us_mode), temp))
+        elif  self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].GetValue():
+            dc_sync0_cycle = "2_" + self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].GetValue()
+        else :
+            dc_sync0_cycle = ""
+
+        if self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].GetValue():
+            temp = self.ComboBoxDic["Sync1UnitCycleChoice"].GetStringSelection()
+            dc_sync1_cycle = "1_" + self.GetCycle(self.GetInterval(us_mode), temp)
+        elif  self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].GetValue():
+            dc_sync1_cycle = "2_" + self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].GetValue()
+        else :
+            dc_sync1_cycle = ""
+
+        dc_sync0_shift = self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].GetValue()
+        dc_sync1_shift = self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].GetValue()
+
+        self.Controler.BaseParams.setDC_Enable(dc_enable)
+        self.Controler.BaseParams.setDC_Desc(dc_desc)
+        self.Controler.BaseParams.setDC_Assign_Activate(dc_assign_activate_mod)
+        if dc_sync0_cycle:
+            self.Controler.BaseParams.setDC_Sync0_Cycle_Time(dc_sync0_cycle)
+        if dc_sync0_shift:
+            self.Controler.BaseParams.setDC_Sync0_Shift_Time(dc_sync0_shift)
+        if dc_sync1_cycle:
+            self.Controler.BaseParams.setDC_Sync1_Cycle_Time(dc_sync1_cycle)
+        if dc_sync1_shift:
+            self.Controler.BaseParams.setDC_Sync1_Shift_Time(dc_sync1_shift)
+        project_infos = self.Controler.GetCTRoot().CTNRequestSave()
+
+    def GetSymbol(self, period, cycle):
+        cmp1 = int(period)
+        cmp2 = int(cycle)
+
+        if cmp1 == cmp2 :
+            return "x 1"
+        elif cmp2 > cmp1 :
+            temp = cmp2 / cmp1
+            result = "x " + str(temp)
+        else :
+            temp = cmp1 / cmp2
+            result = "/ " + str(temp)
+
+        return result
+
+    def SetSyncCycle(self, period, sync0_cycle, sync1_cycle):
+        if sync0_cycle != "None":
+            self.CheckBoxDic["Sync0Enable"].SetValue(True)               
+            temp = sync0_cycle.split("_")
+            if temp[0] == "1":
+                symbol = self.GetSymbol(period, temp[1])
+                self.ComboBoxDic["Sync0UnitCycleChoice"].SetStringSelection(symbol)
+                self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].Disable()
+                self.RadioButtonDic["Sync0CycleTimeUnitRadioButton"].SetValue(True)
+            else :
+                self.TextCtrlDic["Sync0CycleTimeUserDefined_Ctl"].SetValue(temp[1])
+                self.ComboBoxDic["Sync0UnitCycleChoice"].Disable()
+                self.RadioButtonDic["Sync0CycleTimeUserDefinedRadioButton"].SetValue(True)
+
+        if sync1_cycle != "None":
+            self.CheckBoxDic["Sync1Enable"].SetValue(True)
+            temp = sync1_cycle.split("_")
+            if temp[0] == "1":
+                symbol = self.GetSymbol(period, temp[1])
+                self.ComboBoxDic["Sync1UnitChoice"].SetStringSelection(symbol)
+                self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Disable()
+                self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].SetValue(True)
+            else :
+                self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].SetValue(temp[1])
+                self.ComboBoxDic["Sync1UnitChoice"].Disable()
+                self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].SetValue(True)
+
+    def LoadProjectDCData(self):
+        ns_mode = 1
+        task_cycle_ns = self.GetInterval(ns_mode)
+        task_cycle_to_us = int(task_cycle_ns) / 1000
+        dc_enable = self.Controler.BaseParams.getDC_Enable()
+        dc_desc = self.Controler.BaseParams.getDC_Desc()
+        dc_assign_activate = self.Controler.BaseParams.getDC_Assign_Activate()
+        dc_sync0_cycle = self.Controler.BaseParams.getDC_Sync0_Cycle_Time()
+        dc_sync0_shift = self.Controler.BaseParams.getDC_Sync0_Shift_Time()
+        dc_sync1_cycle = self.Controler.BaseParams.getDC_Sync1_Cycle_Time()
+        dc_sync1_shift = self.Controler.BaseParams.getDC_Sync1_Shift_Time()
+
+        self.UIOnOffSet(dc_enable)
+
+        if dc_enable:
+            self.CheckBoxDic["DCEnable"].SetValue(dc_enable)
+            self.ComboBoxDic["OperationModeChoice"].SetStringSelection(dc_desc)
+            self.TextCtrlDic["SyncUnitCycle_Ctl"].SetValue(str(task_cycle_to_us))
+            self.SetSyncCycle(str(task_cycle_to_us), dc_sync0_cycle, dc_sync1_cycle)
+            if dc_sync0_shift != "None":
+                self.TextCtrlDic["Sync0ShiftTimeUserDefined_Ctl"].SetValue(dc_sync0_shift)
+            if dc_sync1_shift != "None":
+                self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].SetValue(dc_sync1_shift)
+
+            if dc_assign_activate == "300":
+                self.CheckBoxDic["Sync1Enable"].SetValue(False)
+                self.RadioButtonDic["Sync1CycleTimeUnitRadioButton"].Disable()
+                self.ComboBoxDic["Sync1UnitCycleChoice"].Disable()
+                self.RadioButtonDic["Sync1CycleTimeUserDefinedRadioButton"].Disable()
+                self.TextCtrlDic["Sync1CycleTimeUserDefined_Ctl"].Disable()
+                self.TextCtrlDic["Sync1ShiftTimeUserDefined_Ctl"].Disable()
+
+
+
+
+
+
+
+
+
+        
--- a/etherlab/EthercatCFileGenerator.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/EthercatCFileGenerator.py	Thu Jun 18 10:42:08 2020 +0200
@@ -70,6 +70,37 @@
     }
 """
 
+SLAVE_OUTPUT_PDO_DEFAULT_VALUE_BIT = """
+    {
+        uint8_t value[%(data_size)d];
+        if (ecrt_master_sdo_upload(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &result_size, &abort_code)) {
+            SLOGF(LOG_CRITICAL, "EtherCAT failed to get default value for output PDO in slave %(device_type)s at alias %(alias)d and position %(position)d. Error: %%ud", abort_code);
+            goto ecat_failed;
+        }
+        %(real_var)s = EC_READ_%(data_type)s((uint8_t *)value, %(subindex)d);
+    }
+"""
+
+
+SLAVE_INPUT_PDO_DEFAULT_VALUE = """
+    {
+        uint8_t value[%(data_size)d];
+        if (ecrt_master_sdo_upload(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &result_size, &abort_code)) {
+            SLOGF(LOG_CRITICAL, "EtherCAT failed to get default value for input PDO in slave %(device_type)s at alias %(alias)d and position %(position)d. Error: %%ud", abort_code);
+            goto ecat_failed;
+        }
+        %(real_var)s = EC_READ_%(data_type)s((uint8_t *)value);
+    }
+"""
+
+DC_VARIABLE ="""
+#define DC_ENABLE       %(dc_flag)d
+"""
+
+CONFIG_DC = """
+    ecrt_slave_config_dc (slave%(slave)d, 0x0%(assign_activate)ld, %(sync0_cycle_time)d, %(sync0_shift_time)d, %(sync1_cycle_time)d, %(sync1_shift_time)d);
+"""
+
 
 def ConfigureVariable(entry_infos, str_completion):
     entry_infos["data_type"] = DATATYPECONVERSION.get(entry_infos["var_type"], None)
@@ -192,10 +223,15 @@
             "pdos_configuration_declaration": "",
             "slaves_declaration": "",
             "slaves_configuration": "",
+            # add jblee
+            "slaves_input_pdos_default_values_extraction": "",
             "slaves_output_pdos_default_values_extraction": "",
             "slaves_initialization": "",
             "retrieve_variables": [],
             "publish_variables": [],
+            #-----------This Code templete for dc -------------------#
+            "dc_variable" : "",
+            "config_dc": ""
         }
 
         # Initialize variable storing variable mapping state
@@ -208,6 +244,9 @@
         # Initialize dictionary storing alias auto-increment position values
         alias = {}
 
+        # add jblee
+        slotNumber = 1
+        
         # Generating code for each slave
         for (slave_idx, slave_alias, slave) in self.Slaves:
             type_infos = slave.getType()
@@ -244,11 +283,14 @@
                 # If device support CanOpen over Ethernet, adding code for calling
                 # init commands when initializing slave in master code template strings
                 initCmds = []
+                
                 for initCmd in device_coe.getInitCmd():
                     initCmds.append({
                         "Index": ExtractHexDecValue(initCmd.getIndex()),
                         "Subindex": ExtractHexDecValue(initCmd.getSubIndex()),
-                        "Value": initCmd.getData().getcontent()})
+                        #"Value": initCmd.getData().getcontent()})
+                        "Value": int(initCmd.getData().text, 16)})
+                
                 initCmds.extend(slave.getStartupCommands())
                 for initCmd in initCmds:
                     index = initCmd["Index"]
@@ -314,12 +356,106 @@
                 pdos_index = []
                 exclusive_pdos = {}
                 selected_pdos = []
-                for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
-                                      [(pdo, "Outputs") for pdo in device.getRxPdo()]):
-
+
+                # add jblee
+                TxPdoData = []
+                RxPdoData = []
+                PdoData = []
+
+                # add jblee
+                if len(device.getTxPdo() + device.getRxPdo()) > 0:
+                    for pdo in device.getTxPdo():
+                        PdoData.append((pdo, "Inputs"))
+                    for pdo in device.getRxPdo():
+                        PdoData.append((pdo, "Outputs"))               
+
+                # mod jblee
+                #for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
+                #                      [(pdo, "Outputs") for pdo in device.getRxPdo()]):
+                #for pdo, pdo_type in (TxPdoData + RxPdoData):
+                data_files = os.listdir(self.Controler.CTNPath())
+                PDODataList = []
+                MDPData = []
+                RxPDOData = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getRxPDO()
+                TxPDOData = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getTxPDO()
+                PDOList = RxPDOData.split() + TxPDOData.split()
+                for PDOIndex in PDOList:
+                    if PDOIndex in ["RxPDO", "TxPDO", "None"]:
+                        continue
+                    PDODataList.append(int(PDOIndex, 0))
+
+                # add jblee for DC Configuration
+                dc_enable = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getDC_Enable()
+                sync0_cycle_time = 0
+                sync0_shift_time = 0
+                sync1_cycle_time = 0
+                sync1_shift_time = 0
+                if dc_enable :
+                    sync0_cycle_token = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getDC_Sync0_Cycle_Time()
+                    if sync0_cycle_token != "None":
+                        sync0_cycle_time = int(sync0_cycle_token.split("_")[1]) * 1000
+                    sync0_shift_token = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getDC_Sync0_Shift_Time()
+                    if sync0_shift_token != "None":
+                        sync0_shift_time = int(sync0_shift_token) * 1000
+                    sync1_cycle_token = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getDC_Sync1_Cycle_Time()
+                    if sync1_cycle_token != "None":
+                        sync1_cycle_time = int(sync1_cycle_token.split("_")[1]) * 1000
+                    sync1_shift_token = self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getDC_Sync1_Shift_Time()
+                    if sync1_shift_token != "None":
+                        sync1_shift_time = int(sync1_shift_token) * 1000
+                    
+                    dc_config_data = {
+                        "slave" : slave_idx,
+                        "assign_activate" : int(self.Controler.GetChildByIECLocation((slave_idx,)).BaseParams.getDC_Assign_Activate()),
+                        "sync0_cycle_time" : sync0_cycle_time,
+                        "sync0_shift_time" : sync0_shift_time,
+                        "sync1_cycle_time" : sync1_cycle_time,
+                        "sync1_shift_time" : sync1_shift_time,
+                    }
+
+                    if dc_enable and not str_completion["dc_variable"] :
+                        str_completion["dc_variable"] += DC_VARIABLE % {"dc_flag" : dc_enable}
+                    str_completion["config_dc"] += CONFIG_DC % dc_config_data
+
+                for data_file in data_files:
+                    slave_path = os.path.join(self.Controler.CTNPath(), data_file)
+                    if os.path.isdir(slave_path):
+                        CheckConfNodePath = os.path.join(slave_path, "baseconfnode.xml")
+                        confNodeFile = open(CheckConfNodePath, 'r')
+                        checklines = confNodeFile.readlines()
+                        confNodeFile.close()
+                        # checklines(ex) : <BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="EthercatSlave_0"/>
+                        # checklines[1].split() : [<BaseParams, xmlns:xsd="http://www.w3.org/2001/XMLSchema",
+                        #                           IEC_Channel="0", Name="EthercatSlave_0"/>]
+                        # checklines[1].split()[2] : IEC_Channel="0"
+                        # checklines[1].split()[2].split("\"") = [IEC_Channel=, 0, ]
+                        pos_check = int(checklines[1].split()[2].split("\"")[1])
+                        if slave_idx == pos_check:
+                            MDPDataFilePath = os.path.join(slave_path, "DataForMDP.txt")
+                            if os.path.isfile(MDPDataFilePath):
+                                MDPDataFile = open(MDPDataFilePath, 'r')
+                                MDPData = MDPDataFile.readlines()
+                                MDPDataFile.close()
+
+                        for MDPLine in MDPData:
+                            if MDPLine == "\n":
+                                continue
+                            module_pos = int(MDPLine.split()[-1])
+                            module = self.Controler.CTNParent.GetSelectModule(module_pos)
+                            for pdo in module.getTxPdo():
+                                PdoData.append((pdo, "Inputs"))
+                                PDODataList.append(ExtractHexDecValue(pdo.getIndex().getcontent()))
+                            for pdo in module.getRxPdo():
+                                PdoData.append((pdo, "Outputs"))
+                                PDODataList.append(ExtractHexDecValue(pdo.getIndex().getcontent()))
+
+                for pdo, pdo_type in PdoData:
                     pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
                     pdos_index.append(pdo_index)
 
+                    if PDODataList and (pdo_index in PDODataList):
+                        continue
+
                     excluded_list = pdo.getExclude()
                     if len(excluded_list) > 0:
                         exclusion_list = [pdo_index]
@@ -330,12 +466,14 @@
                         exclusion_scope = exclusive_pdos.setdefault(tuple(exclusion_list), [])
 
                         entries = pdo.getEntry()
+
                         pdo_mapping_match = {
                             "index": pdo_index,
                             "matching": 0,
                             "count": len(entries),
                             "assigned": pdo.getSm() is not None
                         }
+
                         exclusion_scope.append(pdo_mapping_match)
 
                         for entry in entries:
@@ -364,30 +502,57 @@
                         for pdo in exclusion_scope[start_excluding_index:]
                         if PdoAssign or not pdo["assigned"]])
 
-                for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
-                                      [(pdo, "Outputs") for pdo in device.getRxPdo()]):
-                    entries = pdo.getEntry()
-
+                # mod jblee
+                #for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
+                #                      [(pdo, "Outputs") for pdo in device.getRxPdo()]):
+                #for pdo, pdo_type in (TxPdoData + RxPdoData):
+                entry_check_list = []
+                index_padding = 1
+                for pdo, pdo_type in PdoData:
+                    entries = pdo.getEntry()                   
+                    
                     pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
                     if pdo_index in excluded_pdos:
                         continue
-
-                    pdo_needed = pdo_index in selected_pdos
-
-                    entries_infos = []
-
+                    if PDODataList and (pdo_index not in PDODataList):
+                        continue
+                    
+                    #pdo_needed = pdo_index in selected_pdos
+                    pdo_needed = pdo_index in PDODataList
+
+                    if len(MDPData) > 0:
+                        pdo_index += index_padding
+                        index_padding += 1
+
+                    entries_infos = []                   
                     for entry in entries:
                         index = ExtractHexDecValue(entry.getIndex().getcontent())
                         subindex = ExtractHexDecValue(entry.getSubIndex())
+
+                        # add jblee
+                        if len(MDPData) > 0:
+                            increse = self.Controler.CTNParent.GetMDPInfos(type_infos)                          
+                            if increse and index != 0:
+                                index += int(increse[0][2]) * slotNumber
+                                
                         entry_infos = {
                             "index": index,
                             "subindex": subindex,
                             "name": ExtractName(entry.getName()),
                             "bitlen": entry.getBitLen(),
                         }
+
+                        entry_infos.update(type_infos)
+                        #temp_data = "    {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos
+                        check_data = "{0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}" % entry_infos
+                        if entry_check_list and check_data in entry_check_list:
+                            if (entry_infos["index"] == 0) or (entry_infos["name"] == None):
+                                pass
+                            else: 
                         entry_infos.update(type_infos)
                         entries_infos.append("    {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
-
+                        entry_check_list.append(check_data)
+                        
                         entry_declaration = slave_variables.get((index, subindex), None)
                         if entry_declaration is not None and not entry_declaration["mapped"]:
                             pdo_needed = True
@@ -421,9 +586,26 @@
 
                             ConfigureVariable(entry_infos, str_completion)
 
-                            str_completion["slaves_output_pdos_default_values_extraction"] += \
-                                SLAVE_OUTPUT_PDO_DEFAULT_VALUE % entry_infos
-
+                            if entry_infos["data_type"] == "BIT" :
+                                str_completion["slaves_output_pdos_default_values_extraction"] += \
+                                    SLAVE_OUTPUT_PDO_DEFAULT_VALUE_BIT % entry_infos
+                            else :
+                                str_completion["slaves_output_pdos_default_values_extraction"] += \
+                                    SLAVE_OUTPUT_PDO_DEFAULT_VALUE % entry_infos
+
+                        elif pdo_type == "Inputs" and entry.getDataType() is not None and device_coe is not None:
+                            data_type = entry.getDataType().getcontent()
+                            entry_infos["dir"] = "I"
+                            entry_infos["data_size"] = max(1, entry_infos["bitlen"] / 8)
+                            entry_infos["data_type"] = DATATYPECONVERSION.get(data_type)
+                            entry_infos["var_type"] = data_type
+                            entry_infos["real_var"] = "slave%(slave)d_%(index).4x_%(subindex).2x_default" % entry_infos
+                            
+                            ConfigureVariable(entry_infos, str_completion)
+                            
+                            str_completion["slaves_input_pdos_default_values_extraction"] += \
+                                SLAVE_INPUT_PDO_DEFAULT_VALUE % entry_infos
+                    
                     if pdo_needed:
                         for excluded in pdo.getExclude():
                             excluded_index = ExtractHexDecValue(excluded.getcontent())
@@ -448,6 +630,9 @@
                              "entries_number": len(entries_infos),
                              "fixed": pdo.getFixed() is True})
 
+                    # for MDP
+                    slotNumber += 1
+            
                 if PdoConfig and PdoAssign:
                     dynamic_pdos = {}
                     dynamic_pdos_number = 0
@@ -541,8 +726,8 @@
 
                 pdo_offset = 0
                 entry_offset = 0
+                slotNumber = 1
                 for sync_manager_infos in sync_managers:
-
                     for pdo_infos in sync_manager_infos["pdos"]:
                         pdo_infos["offset"] = entry_offset
                         pdo_entries = pdo_infos["entries"]
@@ -564,17 +749,17 @@
                     pdos_infos[element] = "\n".join(pdos_infos[element])
 
                 str_completion["pdos_configuration_declaration"] += SLAVE_PDOS_CONFIGURATION_DECLARATION % pdos_infos
-
-            for (index, subindex), entry_declaration in slave_variables.iteritems():
-                if not entry_declaration["mapped"]:
-                    message = _("Entry index 0x{a1:.4x}, subindex 0x{a2:.2x} not mapped for device {a3}").\
-                              format(a1=index, a2=subindex, a3=type_infos["device_type"])
-                    self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
-
-        for element in ["used_pdo_entry_offset_variables_declaration",
-                        "used_pdo_entry_configuration",
-                        "located_variables_declaration",
-                        "retrieve_variables",
+            
+            #for (index, subindex), entry_declaration in slave_variables.iteritems():
+            #    if not entry_declaration["mapped"]:
+            #        message = _("Entry index 0x%4.4x, subindex 0x%2.2x not mapped for device %s") % \
+            #                        (index, subindex, type_infos["device_type"])
+            #        self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
+                    
+        for element in ["used_pdo_entry_offset_variables_declaration", 
+                        "used_pdo_entry_configuration", 
+                        "located_variables_declaration", 
+                        "retrieve_variables", 
                         "publish_variables"]:
             str_completion[element] = "\n".join(str_completion[element])
 
--- a/etherlab/EthercatCIA402Slave.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/EthercatCIA402Slave.py	Thu Jun 18 10:42:08 2020 +0200
@@ -201,11 +201,69 @@
             ("%%IW%s.402" % ".".join(map(str, self.GetCurrentLocation())),
              "location", "AXIS_REF", self.CTNName(), ""))
 
+    # add jblee
+    """
+    def LoadPDOSelectData(self):
+        ReadData = []
+        files = os.listdir(self.CTNPath())
+        filepath = os.path.join(self.CTNPath(), "DataForPDO.txt")
+        if os.path.isfile(filepath):
+            PDODataRead = open(filepath, 'r')
+            ReadData = PDODataRead.readlines()
+            PDODataRead.close()
+
+        if len(ReadData) > 1:
+            for data in ReadData[0].split() :
+                if data == "RxPDO":
+                    continue
+                self.SelectedRxPDOIndex.append(int(data, 0))
+
+            for data in ReadData[1].split() :
+                if data == "TxPDO":
+                    continue
+                self.SelectedTxPDOIndex.append(int(data, 0))
+    """
+
+    def LoadPDOSelectData(self):
+        RxPDOData = self.BaseParams.getRxPDO()
+        RxPDOs = []
+        if RxPDOData != "None":
+            RxPDOs = RxPDOData.split()
+        if RxPDOs :
+            for RxPDO in RxPDOs :
+                self.SelectedRxPDOIndex.append(int(RxPDO, 0))
+
+        TxPDOData = self.BaseParams.getTxPDO()
+        TxPDOs = []
+        if TxPDOData != "None":
+            TxPDOs = TxPDOData.split()
+        if TxPDOs :
+            for TxPDO in TxPDOs :
+                self.SelectedTxPDOIndex.append(int(TxPDO, 0))
+
+    def LoadDefaultPDOSet(self):
+        ReturnData = []
+        rx_pdo_entries = self.CommonMethod.GetRxPDOCategory()
+        if len(rx_pdo_entries):
+            for i in range(len(rx_pdo_entries)):
+                if rx_pdo_entries[i]['sm'] is not None:
+                    ReturnData.append(rx_pdo_entries[i]['pdo_index'])
+
+        tx_pdo_entries = self.CommonMethod.GetTxPDOCategory()
+        if len(tx_pdo_entries):
+            for i in range(len(tx_pdo_entries)):
+                if tx_pdo_entries[i]['sm'] is not None:
+                    ReturnData.append(tx_pdo_entries[i]['pdo_index'])
+
+        if ReturnData :
+            return ReturnData
+        else :
+            return [5632, 6656]
+        
     def CTNGenerate_C(self, buildpath, locations):
         current_location = self.GetCurrentLocation()
 
         location_str = "_".join(map(str, current_location))
-        slave_pos = self.GetSlavePos()
 
         # Open CIA402 node code template file
         plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0],
@@ -213,160 +271,204 @@
         plc_cia402node_file = open(plc_cia402node_filepath, 'r')
         plc_cia402node_code = plc_cia402node_file.read()
         plc_cia402node_file.close()
-
-        # Init list of generated strings for each code template file section
-        fieldbus_interface_declaration = []
-        fieldbus_interface_definition = []
-        init_axis_params = []
-        extra_variables_retrieve = []
-        extra_variables_publish = []
-        extern_located_variables_declaration = []
-        entry_variables = []
-        init_entry_variables = []
-
-        # Fieldbus interface code sections
+        # HSAHN 150726
+        # add "default_variables_retrieve": [], "default_variables_publish": [],
+		# As PDO mapping object, it will add auto-complete code.
+        # add "modeofop_homing_method", "modeofop_computation_mode" by jblee
+        str_completion = {
+            "slave_pos": self.GetSlavePos(),
+            "location": location_str,
+            "MCL_headers": Headers,
+            "extern_located_variables_declaration": [],
+            "fieldbus_interface_declaration": [],
+            "fieldbus_interface_definition": [],
+            "entry_variables": [],
+            "init_axis_params": [],
+            "init_entry_variables": [],
+            "default_variables_retrieve": [],
+            "default_variables_publish": [],
+            "extra_variables_retrieve": [],
+            "extra_variables_publish": [],
+            "modeofop_homing_method": [],
+            "modeofop_computation_mode": []
+        }
+        
         for blocktype_infos in FIELDBUS_INTERFACE_GLOBAL_INSTANCES:
-            blocktype = blocktype_infos["blocktype"]
-            ucase_blocktype = blocktype.upper()
-            blockname = "_".join([ucase_blocktype, location_str])
-
-            extract_inputs = "\n".join([
-                """\
-                __SET_VAR(%s->, %s,, %s);""" % (blockname, input_name, input_value)
-                for (input_name, input_value) in
-                [("EXECUTE", "__GET_VAR(data__->EXECUTE)")] + [
-                    (input["name"].upper(),
-                     "__GET_VAR(data__->%s)" % input["name"].upper())
-                    for input in blocktype_infos["inputs"]
-                ]])
-
-            return_outputs = "\n".join([
-                """\
-                __SET_VAR(data__->,%(output_name)s,,
-                __GET_VAR(%(blockname)s->%(output_name)s));""" % {
-                    "output_name": output_name,
-                    "blockname": blockname
+            texts = {
+                "blocktype": blocktype_infos["blocktype"],
+                "ucase_blocktype": blocktype_infos["blocktype"].upper(),
+                "location": "_".join(map(str, current_location))
+            }
+            texts["blockname"] = "%(ucase_blocktype)s_%(location)s" % texts
+            
+            inputs = [{"input_name": "POS", "input_value": str(self.GetSlavePos())},
+                      {"input_name": "EXECUTE", "input_value": "__GET_VAR(data__->EXECUTE)"}] +\
+                     [{"input_name": input["name"].upper(), 
+                       "input_value": "__GET_VAR(data__->%s)" % input["name"].upper()}
+                      for input in blocktype_infos["inputs"]]
+            input_texts = []
+            for input_infos in inputs:
+                input_infos.update(texts)
+                input_texts.append(BLOCK_INPUT_TEMPLATE % input_infos)
+            texts["extract_inputs"] = "\n".join(input_texts)
+            
+            outputs = [{"output_name": output} for output in ["DONE", "BUSY", "ERROR"]] + \
+                      [{"output_name": output["name"].upper()} for output in blocktype_infos["outputs"]]
+            output_texts = []
+            for output_infos in outputs:
+                output_infos.update(texts)
+                output_texts.append(BLOCK_OUTPUT_TEMPLATE % output_infos)
+            texts["return_outputs"] = "\n".join(output_texts)
+            
+            str_completion["fieldbus_interface_declaration"].append(
+                    BLOCK_FUNCTION_TEMPLATE % texts)
+            
+            str_completion["fieldbus_interface_definition"].append(
+                    BLOCK_FUNTION_DEFINITION_TEMPLATE % texts)
+
+        variables = NODE_VARIABLES[:]
+
+#HSAHN
+#2015. 7. 24 PDO Variable
+        #if PDO is not selected, use 1st PDO set
+        self.LoadPDOSelectData()
+        if not self.SelectedRxPDOIndex and not self.SelectedTxPDOIndex :
+            self.SelectedPDOIndex = self.LoadDefaultPDOSet()
+        else :
+            self.SelectedPDOIndex = self.SelectedRxPDOIndex + self.SelectedTxPDOIndex
+
+        add_idx = []
+        for i in range(len(ADD_NODE_VARIABLES)):
+            add_idx.append(ADD_NODE_VARIABLES[i]['index'])
+
+        self.CommonMethod.RequestPDOInfo()
+        pdo_info = self.CommonMethod.GetRxPDOCategory() + self.CommonMethod.GetTxPDOCategory()
+        pdo_entry = self.CommonMethod.GetRxPDOInfo() + self.CommonMethod.GetTxPDOInfo()
+        list_index = 0
+        ModeOfOpFlag = False
+        ModeOfOpDisplayFlag = False
+        for i in range(len(pdo_info)):
+            #if pdo_index is in the SelectedPDOIndex: put the PDO mapping information intto the "used" object
+            if pdo_info[i]['pdo_index'] in self.SelectedPDOIndex:
+                used = pdo_entry[list_index:list_index + pdo_info[i]['number_of_entry']]
+                for used_data in used:
+                    # 24672 -> 0x6060, Mode of Operation
+                    if used_data['entry_index'] == 24672:
+                        ModeOfOpFlag = True
+                    # 24673 -> 0x6061, Mode of Operation Display
+                    elif used_data["entry_index"] == 24673:
+                        ModeOfOpDisplayFlag = True
+
+                    if used_data['entry_index'] in add_idx:
+                        idx = add_idx.index(used_data['entry_index'])
+                        adder = list([ADD_NODE_VARIABLES[idx]['name'], ADD_NODE_VARIABLES[idx]['index'], \
+                                     ADD_NODE_VARIABLES[idx]['sub-index'], ADD_NODE_VARIABLES[idx]['type'], \
+                                     ADD_NODE_VARIABLES[idx]['direction']])
+                        variables.append(adder)
+                        if ADD_NODE_VARIABLES[idx]['direction'] == "Q":                           
+                            parsed_string = ADD_NODE_VARIABLES[idx]['name'].replace("Target", "")
+                            # add jblee
+                            check_q_data = "    *(AxsPub.Target%s) = AxsPub.axis->Raw%sSetPoint;" %(parsed_string, parsed_string)
+                            if check_q_data not in str_completion["default_variables_publish"]:
+                                str_completion["default_variables_publish"].append(check_q_data)
+                        elif ADD_NODE_VARIABLES[idx]['direction'] == "I":
+                            parsed_string = ADD_NODE_VARIABLES[idx]['name'].replace("Actual", "")
+                            # add jblee
+                            check_i_data = "    AxsPub.axis->ActualRaw%s = *(AxsPub.Actual%s);" %(parsed_string, parsed_string)
+                            if check_i_data not in str_completion["default_variables_retrieve"]:
+                                str_completion["default_variables_retrieve"].append(check_i_data)
+            list_index += pdo_info[i]['number_of_entry']
+#HSAHN END
+
+        params = self.CTNParams[1].getElementInfos(self.CTNParams[0])
+        for param in params["children"]:
+            if param["name"] in EXTRA_NODE_VARIABLES_DICT:
+                if param["value"]:
+                    extra_variables = EXTRA_NODE_VARIABLES_DICT.get(param["name"])
+                    for variable_infos in extra_variables:
+                        var_infos = {
+                            "location": location_str,
+                            "name": variable_infos["description"][0]
+                        }
+                        variables.append(variable_infos["description"])
+                        retrieve_template = variable_infos.get("retrieve", DEFAULT_RETRIEVE)
+                        publish_template = variable_infos.get("publish", DEFAULT_PUBLISH)
+                        
+                        if retrieve_template is not None:
+                            str_completion["extra_variables_retrieve"].append(
+                                retrieve_template % var_infos)
+                        if publish_template is not None:
+                            str_completion["extra_variables_publish"].append(
+                                publish_template % var_infos)
+
+            #elif param["value"] is not None:
+            if param["value"] is not None:
+                param_infos = {
+                    "location": location_str,
+                    "param_name": param["name"],
                 }
-                for output_name in ["DONE", "BUSY", "ERROR"] + [
-                    output["name"].upper()
-                    for output in blocktype_infos["outputs"]]
-            ])
-
-            loc_dict = {
-                "ucase_blocktype": ucase_blocktype,
-                "blocktype": blocktype,
-                "blockname": blockname,
-                "location_str": location_str,
-                "extract_inputs": extract_inputs,
-                "return_outputs": return_outputs,
-            }
-
-            fieldbus_interface_declaration.append("""
-extern void ETHERLAB%(ucase_blocktype)s_body__(ETHERLAB%(ucase_blocktype)s* data__);
-void __%(blocktype)s_%(location_str)s(MC_%(ucase_blocktype)s *data__) {
-__DECLARE_GLOBAL_PROTOTYPE(ETHERLAB%(ucase_blocktype)s, %(blockname)s);
-ETHERLAB%(ucase_blocktype)s* %(blockname)s = __GET_GLOBAL_%(blockname)s();
-__SET_VAR(%(blockname)s->, POS,, AxsPub.axis->NetworkPosition);
-%(extract_inputs)s
-ETHERLAB%(ucase_blocktype)s_body__(%(blockname)s);
-%(return_outputs)s
-}""" % loc_dict)
-
-            fieldbus_interface_definition.append("""\
-        AxsPub.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location_str)s;\
-""" % loc_dict)
-
-        # Get a copy list of default variables to map
-        variables = NODE_VARIABLES[:]
-
-        # Set AxisRef public struct members value
-        node_params = self.CTNParams[1].getElementInfos(self.CTNParams[0])
-        for param in node_params["children"]:
-            param_name = param["name"]
-
-            # Param is optional variables section enable flag
-            extra_node_variable_infos = EXTRA_NODE_VARIABLES_DICT.get(param_name)
-            if extra_node_variable_infos is not None:
-                param_name = param_name.replace("Enable", "") + "Enabled"
-
-                if not param["value"]:
-                    continue
-
-                # Optional variables section is enabled
-                for variable_infos in extra_node_variable_infos:
-                    var_name = variable_infos["description"][0]
-
-                    # Add each variables defined in section description to the
-                    # list of variables to map
-                    variables.append(variable_infos["description"])
-
-                    # Add code to publish or retrive variable
-                    coded = [
-                        ("retrieve",
-                         extra_variables_retrieve,
-                         "    AxsPub.axis->%(var_name)s = *(AxsPub.%(var_name)s);"),
-                        ("publish",
-                         extra_variables_publish,
-                         "    *(AxsPub.%(var_name)s) = AxsPub.axis->%(var_name)s;")
-                    ]
-                    for var_exchange_dir, _str_list, default_template in coded:
-                        template = variable_infos.get(var_exchange_dir, default_template)
-                        if template is not None:
-                            extra_variables_publish.append(template % locals())
-
-            # Set AxisRef public struct member value if defined
-            if param["value"] is not None:
-                param_value = ({True: "1", False: "0"}[param["value"]]
-                               if param["type"] == "boolean"
-                               else str(param["value"]))
-
-                init_axis_params.append("""\
-                AxsPub.axis->%(param_name)s = %(param_value)s;""" % {
-                    "param_value": param_value,
-                    "param_name": param_name,
-                })
-
-        # Add each variable in list of variables to map to master list of
-        # variables to add to network configuration
-        for name, index, subindex, var_type, dir in variables:
-            var_size = self.GetSizeOfType(var_type)
-            loc_dict = {
-                "var_size": var_size,
-                "var_type": var_type,
-                "name:": name,
-                "location_str": location_str,
-                "index": index,
-                "subindex": subindex,
-            }
-            var_name = """\
-__%(dir)s%(var_size)s%(location_str)s_%(index)d_%(subindex)d""" % loc_dict
-            loc_dict["var_name"] = var_name
-
-            extern_located_variables_declaration.append(
-                "IEC_%(var_type)s *%(var_name)s;" % loc_dict)
-            entry_variables.append(
-                "    IEC_%(var_type)s *%(name)s;" % loc_dict)
-            init_entry_variables.append(
-                "    AxsPub.%(name)s = %(var_name)s;" % loc_dict)
-
+                if param["type"] == "boolean":
+                    param_infos["param_value"] = {True: "1", False: "0"}[param["value"]]
+                    param_infos["param_name"] = param["name"].replace("Enable", "") + "Enabled"
+                    if param["value"] == False:
+                        continue
+                else:
+                    param_infos["param_value"] = str(param["value"])
+                # param_name = param_name.replace("Enable", "") + "Enabled"
+                str_completion["init_axis_params"].append(
+                    "        __CIA402Node_%(location)s.axis->%(param_name)s = %(param_value)s;" % param_infos)
+        
+        check_variable = []
+        for variable in variables:
+            # add jblee
+            if variable in check_variable:
+                continue
+
+            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
+
+            # add jblee
+            if var_infos["index"] in [24672] and ModeOfOpFlag:
+                str_completion["modeofop_homing_method"].append(MODEOFOP_HOMING_METHOD_TEMPLATE)
+                str_completion["modeofop_computation_mode"].append(MODEOFOP_COMPUTATION_MODE_TEMPLATE)
+
+            # add jblee
+            if var_infos["index"] in [24672, 24673] and (not ModeOfOpFlag or not ModeOfOpDisplayFlag):
+                continue
+
+            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(
+                    "    __CIA402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos)
+            
             self.CTNParent.FileGenerator.DeclareVariable(
-                slave_pos, index, subindex, var_type, dir, var_name)
-
-        # Add newline between string in list of generated strings for sections
-        [fieldbus_interface_declaration, fieldbus_interface_definition,
-         init_axis_params, extra_variables_retrieve, extra_variables_publish,
-         extern_located_variables_declaration, entry_variables,
-         init_entry_variables] = map("\n".join, [
-             fieldbus_interface_declaration, fieldbus_interface_definition,
-             init_axis_params, extra_variables_retrieve, extra_variables_publish,
-             extern_located_variables_declaration, entry_variables,
-             init_entry_variables])
-
-        # Write generated content to CIA402 node file
-        Gen_CIA402Nodefile_path = os.path.join(buildpath,
-                                               "cia402node_%s.c" % location_str)
+                    self.GetSlavePos(), var_infos["index"], var_infos["subindex"], 
+                    var_infos["var_type"], var_infos["dir"], var_infos["var_name"])
+
+            # add jblee
+            check_variable.append(variable)
+        
+        for element in ["extern_located_variables_declaration", 
+                        "fieldbus_interface_declaration",
+                        "fieldbus_interface_definition",
+                        "entry_variables", 
+                        "init_axis_params", 
+                        "init_entry_variables",
+                        "default_variables_retrieve",
+                        "default_variables_publish",
+                        "extra_variables_retrieve",
+                        "extra_variables_publish",
+                        "modeofop_homing_method",
+                        "modeofop_computation_mode"]:
+            str_completion[element] = "\n".join(str_completion[element])
+        
+        Gen_CIA402Nodefile_path = os.path.join(buildpath, "cia402node_%s.c"%location_str)
         cia402nodefile = open(Gen_CIA402Nodefile_path, 'w')
-        cia402nodefile.write(plc_cia402node_code % locals())
+        cia402nodefile.write(plc_cia402node_code % str_completion)
         cia402nodefile.close()
 
         return [(Gen_CIA402Nodefile_path, '"-I%s"' % os.path.abspath(self.GetCTRoot().GetIECLibPath()))], "", True
--- a/etherlab/EthercatMaster.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/EthercatMaster.py	Thu Jun 18 10:42:08 2020 +0200
@@ -243,20 +243,20 @@
         config_filepath = self.ConfigFileName()
         config_is_saved = False
         self.Config = None
-        if os.path.isfile(config_filepath):
-            config_xmlfile = open(config_filepath, 'r')
-            try:
-                self.Config, error = \
-                    EtherCATConfigParser.LoadXMLString(config_xmlfile.read())
-                if error is None:
-                    config_is_saved = True
-            except Exception as e:
-                error = str(e)
-            config_xmlfile.close()
-
-            if error is not None:
-                self.GetCTRoot().logger.write_error(
-                    _("Couldn't load %s network configuration file.") % self.CTNName())
+        # if os.path.isfile(config_filepath):
+        #     config_xmlfile = open(config_filepath, 'r')
+        #     try:
+        #         self.Config, error = \
+        #             EtherCATConfigParser.LoadXMLString(config_xmlfile.read())
+        #         if error is None:
+        #             config_is_saved = True
+        #     except Exception as e:
+        #         error = str(e)
+        #     config_xmlfile.close()
+
+        #     if error is not None:
+        #         self.GetCTRoot().logger.write_error(
+        #             _("Couldn't load %s network configuration file.") % self.CTNName())
 
         if self.Config is None:
             self.Config = EtherCATConfigParser.CreateElement("EtherCATConfig")
@@ -282,12 +282,28 @@
         if self.ProcessVariables is None:
             self.ProcessVariables = ProcessVariablesParser.CreateElement("ProcessVariables")
 
-        if config_is_saved and process_is_saved:
+        #if config_is_saved and process_is_saved:
+        if process_is_saved:
             self.CreateBuffer(True)
         else:
             self.CreateBuffer(False)
             self.OnCTNSave()
 
+        if os.path.isfile(config_filepath):
+            config_xmlfile = open(config_filepath, 'r')
+            try:
+                self.Config, error = \
+                    EtherCATConfigParser.LoadXMLString(config_xmlfile.read())
+                if error is None:
+                    config_is_saved = True
+            except Exception, e:
+                error = e.message
+            config_xmlfile.close()
+            
+            if error is not None:
+                self.GetCTRoot().logger.write_error(
+                    _("Couldn't load %s network configuration file.") % CTNName)
+
         # ----------- call ethercat mng. function --------------
         self.CommonMethod = _CommonSlave(self)
 
@@ -306,7 +322,7 @@
             type_infos = dialog.GetValueInfos()
             device, _module_extra_params = self.GetModuleInfos(type_infos)
             if device is not None:
-                if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
+                if HAS_MCL and str(_EthercatCIA402SlaveCTN.NODE_PROFILE) in device.GetProfileNumbers():
                     ConfNodeType = "EthercatCIA402Slave"
                 else:
                     ConfNodeType = "EthercatSlave"
@@ -577,14 +593,62 @@
                 return infos
         return None
 
-    def GetSlaveVariables(self, slave_pos=None, limits=None, device=None):
+    def GetSlaveVariables(self, slave_pos=None, limits=None, device=None, module=None):
+        # add jblee
+        files = os.listdir(self.CTNPath())
+        moduleNames = []
+        modulePos = 1
+        for file in files:
+            filepath = os.path.join(self.CTNPath(), file)
+            if os.path.isdir(filepath):
+                MDPFilePath = os.path.join(filepath, "DataForMDP.txt")
+                CheckConfNodePath = os.path.join(filepath, "baseconfnode.xml")
+
+                try :
+                    moduleDataFile = open(MDPFilePath, 'r')
+                    confNodeFile = open(CheckConfNodePath, 'r')
+
+                    lines = moduleDataFile.readlines()
+                    checklines = confNodeFile.readlines()
+
+                    moduleDataFile.close()
+                    confNodeFile.close()
+
+                    module_info = self.GetModuleEntryList()
+                    # checklines(ex) : <BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="EthercatSlave_0"/>
+                    # checklines[1].split() : [<BaseParams, xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                    #                           IEC_Channel="0", Name="EthercatSlave_0"/>]
+                    # checklines[1].split()[2] : IEC_Channel="0"
+                    # checklines[1].split()[2].split("\"") = [IEC_Channel=, 0, ]
+                    pos_check = int(checklines[1].split()[2].split("\"")[1])
+
+                    if slave_pos != pos_check:
+                        continue
+
+                    for line in lines:
+                        if line == "\n":
+                            continue
+                        # module_name : ST-1214, ST-2314, ...
+                        # if user add module => ST-1214 3EA, ST-2314 3EA
+                        # each result_module_name : 
+                        #    (ST-1214, Module 1), (ST-1214, Module 2), (ST-1214, Module 3)
+                        #    (ST-2314, Module 4), (ST-2314, Module 5), (ST-2314, Module 6)  
+                        module_name = line.split()[0]
+                        result_module_name = module_name + ", Module %d" % modulePos
+                        moduleNames.append(result_module_name)
+                        modulePos += 1
+                except :
+                    pass
+
         if device is None and slave_pos is not None:
             slave = self.GetSlave(slave_pos)
             if slave is not None:
                 type_infos = slave.getType()
                 device, _module_extra_params = self.GetModuleInfos(type_infos)
         if device is not None:
+            # Test OD
             entries = device.GetEntriesList(limits)
+            #entries = self.CTNParent.GetEntriesList()
             entries_list = entries.items()
             entries_list.sort()
             entries = []
@@ -602,6 +666,49 @@
                     current_entry["children"].append(entry)
                 else:
                     entries.append(entry)
+
+            increment = self.CTNParent.GetModuleIncrement()[0]
+            count = 1
+            #print module_info
+            # moduleNameAndPos : (ST-1214, Module 1), (ST-1214, Module 2), ... ,
+            # moduleNameAndPos.split(",") : ["ST-1214", " Module 1"]
+            # moduleNameAndPos.split(",")[0] : "ST-1214"
+            for moduleNameAndPos in moduleNames:
+                moduleName = moduleNameAndPos.split(",")[0]
+                modulePosName = moduleNameAndPos.split(",")[1]
+                idx_increment = int(increment) * count
+                
+                for MDP_entry in module_info.get(moduleName):
+                    LocalMDPEntry = []
+                    #print MDP_entry
+                    local_idx = MDP_entry["Index"]
+                    if ExtractHexDecValue(local_idx) == 0: #and local_idx[0] == "#":
+                        temp_index = ExtractHexDecValue(local_idx)
+                    else :
+                        temp_index = ExtractHexDecValue(MDP_entry["Index"]) + idx_increment
+                    #temp_index = ExtractHexDecValue(MDP_entry["Index"]) + idx_increment
+                    entry_index = hex(temp_index)
+                    entry_subidx = MDP_entry["SubIndex"]
+                    entry_name = MDP_entry["Name"] + ", " + " " + \
+                                 moduleName + " - " + modulePosName
+                    entry_type = MDP_entry["Type"]
+                    entry_bitsize = MDP_entry["BitSize"]
+                    entry_access = MDP_entry["Access"]
+                    mapping_type = MDP_entry["PDOMapping"]
+
+                    LocalMDPEntry.append({
+                        "Index": entry_index,
+                        "SubIndex": entry_subidx,
+                        "Name": entry_name,
+                        "Type": entry_type,
+                        "BitSize": entry_bitsize,
+                        "Access": entry_access, 
+                        "PDOMapping": mapping_type,
+                        "children": ""})
+                    entries.append(LocalMDPEntry[0])
+                count += 1
+            
+            #print entries
             return entries
         return []
 
@@ -634,6 +741,10 @@
     def GetModuleInfos(self, type_infos):
         return self.CTNParent.GetModuleInfos(type_infos)
 
+    # add jblee
+    def GetModuleEntryList(self):
+        return self.CTNParent.GetModuleEntryList()
+    
     def GetSlaveTypesLibrary(self, profile_filter=None):
         return self.CTNParent.GetModulesLibrary(profile_filter)
 
@@ -643,6 +754,53 @@
     def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
         slave = self.GetSlave(slave_pos)
         vars = []
+
+        # add jblee
+        files = os.listdir(self.CTNPath())
+        moduleNames = []
+        modulePos = 1
+        for file in files:
+            filepath = os.path.join(self.CTNPath(), file)
+            if os.path.isdir(filepath):
+                MDPFilePath = os.path.join(filepath, "DataForMDP.txt")
+                CheckConfNodePath = os.path.join(filepath, "baseconfnode.xml")
+
+                try :
+                    moduleDataFile = open(MDPFilePath, 'r')
+                    confNodeFile = open(CheckConfNodePath, 'r')
+
+                    lines = moduleDataFile.readlines()
+                    checklines = confNodeFile.readlines()
+
+                    moduleDataFile.close()
+                    confNodeFile.close()
+
+                    module_info = self.GetModuleEntryList()
+                    # checklines(ex) : <BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="EthercatSlave_0"/>
+                    # checklines[1].split() : [<BaseParams, xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                    #                           IEC_Channel="0", Name="EthercatSlave_0"/>]
+                    # checklines[1].split()[2] : IEC_Channel="0"
+                    # checklines[1].split()[2].split("\"") = [IEC_Channel=, 0, ]
+                    pos_check = int(checklines[1].split()[2].split("\"")[1])
+
+                    if slave_pos != pos_check:
+                        continue
+
+                    for line in lines:
+                        if line == "\n":
+                            continue
+                        # module_name : ST-1214, ST-2314, ...
+                        # if user add module => ST-1214 3EA, ST-2314 3EA
+                        # each result_module_name : 
+                        #    (ST-1214, Module 1), (ST-1214, Module 2), (ST-1214, Module 3)
+                        #    (ST-2314, Module 4), (ST-2314, Module 5), (ST-2314, Module 6)  
+                        module_name = line.split()[0]
+                        result_module_name = module_name + ", Module %d" % modulePos
+                        moduleNames.append(result_module_name)
+                        modulePos += 1
+                except :
+                    pass
+
         if slave is not None:
             type_infos = slave.getType()
 
@@ -681,6 +839,39 @@
                                 "children": [],
                             })
 
+                # add jblee for MDP
+                if not entries :
+                    increment = self.CTNParent.GetModuleIncrement()[0]
+                    count = 1
+                    for moduleNameAndPos in moduleNames:
+                        moduleName = moduleNameAndPos.split(",")[0]
+                        idx_increment = int(increment) * count
+                        for MDP_entry in module_info.get(moduleName):
+                            local_idx = MDP_entry["Index"]
+                            if ExtractHexDecValue(local_idx) != 0 and local_idx[0] == "#":
+                                index = ExtractHexDecValue(local_idx) + idx_increment
+                            else :
+                                index = ExtractHexDecValue(MDP_entry["Index"])
+                            subindex = int(MDP_entry["SubIndex"])
+                            var_class = VARCLASSCONVERSION.get(MDP_entry["PDOMapping"], None)
+                            if var_class is not None:
+                                if var_class == LOCATION_VAR_INPUT:
+                                    var_dir = "%I"
+                                else:
+                                    var_dir = "%Q"
+                            var_size = self.GetSizeOfType(MDP_entry["Type"])
+                            result_name = MDP_entry["Name"] + ", " + moduleNameAndPos
+                            vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, result_name),
+                                         "type": var_class,
+                                         "size": var_size,
+                                         "IEC_type": MDP_entry["Type"],
+                                         "var_name": "%s_%4.4x_%2.2x" % ("_".join(moduleName.split()), index, subindex),
+                                         "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location + 
+                                                                                                    (index, subindex)))),
+                                         "description": "",
+                                         "children": []})
+                        count += 1
+
         return vars
 
     def CTNTestModified(self):
@@ -688,7 +879,6 @@
 
     def OnCTNSave(self, from_project_path=None):
         config_filepath = self.ConfigFileName()
-
         config_xmlfile = open(config_filepath, "w")
         config_xmlfile.write(etree.tostring(
             self.Config,
--- a/etherlab/EthercatSlave.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/EthercatSlave.py	Thu Jun 18 10:42:08 2020 +0200
@@ -73,6 +73,8 @@
     def __init__(self):
         # ----------- call ethercat mng. function --------------
         self.CommonMethod = _CommonSlave(self)
+        self.SelectedRxPDOIndex = []
+        self.SelectedTxPDOIndex = []
 
     def GetIconName(self):
         return "Slave"
--- a/etherlab/entries_list.xslt	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/entries_list.xslt	Thu Jun 18 10:42:08 2020 +0200
@@ -1,1 +1,1 @@
-<xsl:stylesheet xmlns:func="http://exslt.org/functions" xmlns:dyn="http://exslt.org/dynamic" xmlns:str="http://exslt.org/strings" xmlns:math="http://exslt.org/math" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="ns" xmlns:yml="http://fdik.org/yml" xmlns:set="http://exslt.org/sets" version="1.0" xmlns:ns="entries_list_ns" exclude-result-prefixes="ns" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="xml"/><xsl:variable name="space" select="'                                                                                                                                                                                                        '"/><xsl:param name="autoindent" select="4"/><xsl:param name="min_index"/><xsl:param name="max_index"/><xsl:template match="text()"><xsl:param name="_indent" select="0"/></xsl:template><xsl:template match="Device"><xsl:param name="_indent" select="0"/><xsl:apply-templates select="Profile/Dictionary/Objects/Object"><xsl:with-param name="_indent" select="$_indent + (1) * $autoindent"/></xsl:apply-templates><xsl:for-each select="RxPdo"><xsl:call-template name="pdo_entries"><xsl:with-param name="direction" select="'Receive'"/></xsl:call-template></xsl:for-each><xsl:for-each select="TxPdo"><xsl:call-template name="pdo_entries"><xsl:with-param name="direction" select="'Transmit'"/></xsl:call-template></xsl:for-each></xsl:template><xsl:template match="Object"><xsl:param name="_indent" select="0"/><xsl:variable name="index"><xsl:value-of select="ns:HexDecValue(Index/text())"/></xsl:variable><xsl:variable name="entry_name"><xsl:value-of select="ns:EntryName(Name)"/></xsl:variable><xsl:choose><xsl:when test="$index &gt;= $min_index and $index &lt;= $max_index"><xsl:variable name="datatype_name"><xsl:value-of select="Type/text()"/></xsl:variable><xsl:choose><xsl:when test="ancestor::Dictionary/child::DataTypes/DataType[Name/text()=$datatype_name][SubItem]"><xsl:apply-templates select="ancestor::Dictionary/child::DataTypes/DataType[Name/text()=$datatype_name][SubItem]"><xsl:with-param name="_indent" select="$_indent + (1) * $autoindent"/><xsl:with-param name="index"><xsl:value-of select="$index"/></xsl:with-param><xsl:with-param name="entry_name"><xsl:value-of select="$entry_name"/></xsl:with-param></xsl:apply-templates></xsl:when><xsl:otherwise><xsl:variable name="subindex"><xsl:text>0</xsl:text></xsl:variable><xsl:variable name="entry"><xsl:value-of select="ns:AddEntry($index, $subindex, $entry_name, $datatype_name, BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text())"/></xsl:variable></xsl:otherwise></xsl:choose></xsl:when></xsl:choose></xsl:template><xsl:template match="DataType"><xsl:param name="_indent" select="0"/><xsl:param name="index"/><xsl:param name="entry_name"/><xsl:for-each select="SubItem"><xsl:variable name="subindex"><xsl:value-of select="ns:HexDecValue(SubIdx/text())"/></xsl:variable><xsl:variable name="subentry_name"><xsl:value-of select="$entry_name"/><xsl:text> - </xsl:text><xsl:value-of select="ns:EntryName(DisplayName, Name/text())"/></xsl:variable><xsl:variable name="entry"><xsl:value-of select="ns:AddEntry($index, $subindex, $subentry_name, Type/text(), BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text())"/></xsl:variable></xsl:for-each></xsl:template><xsl:template name="pdo_entries"><xsl:param name="_indent" select="0"/><xsl:param name="direction"/><xsl:variable name="pdo_index"><xsl:value-of select="ns:HexDecValue(Index/text())"/></xsl:variable><xsl:variable name="pdo_name"><xsl:value-of select="ns:EntryName(Name)"/></xsl:variable><xsl:for-each select="Entry"><xsl:variable name="index"><xsl:value-of select="ns:HexDecValue(Index/text())"/></xsl:variable><xsl:choose><xsl:when test="$index &gt;= $min_index and $index &lt;= $max_index"><xsl:variable name="subindex"><xsl:value-of select="ns:HexDecValue(SubIndex/text())"/></xsl:variable><xsl:variable name="subentry_name"><xsl:value-of select="ns:EntryName(Name)"/></xsl:variable><xsl:variable name="access"><xsl:choose><xsl:when test="$direction='Transmit'"><xsl:text>ro</xsl:text></xsl:when><xsl:otherwise><xsl:text>wo</xsl:text></xsl:otherwise></xsl:choose></xsl:variable><xsl:variable name="pdo_mapping"><xsl:choose><xsl:when test="$direction='Transmit'"><xsl:text>T</xsl:text></xsl:when><xsl:otherwise><xsl:text>R</xsl:text></xsl:otherwise></xsl:choose></xsl:variable><xsl:variable name="entry"><xsl:value-of select="ns:AddEntry($index, $subindex, $subentry_name, DataType/text(), BitLen/text(), $access, $pdo_mapping, $pdo_index, $pdo_name, $direction)"/></xsl:variable></xsl:when></xsl:choose></xsl:for-each></xsl:template></xsl:stylesheet>
\ No newline at end of file
+<xsl:stylesheet xmlns:func="http://exslt.org/functions" xmlns:dyn="http://exslt.org/dynamic" xmlns:str="http://exslt.org/strings" xmlns:math="http://exslt.org/math" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="ns" xmlns:yml="http://fdik.org/yml" xmlns:set="http://exslt.org/sets" version="1.0" xmlns:ns="entries_list_ns" exclude-result-prefixes="ns" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="xml"/><xsl:variable name="space" select="'                                                                                                                                                                                                        '"/><xsl:param name="autoindent" select="4"/><xsl:param name="min_index"/><xsl:param name="max_index"/><xsl:template match="text()"><xsl:param name="_indent" select="0"/></xsl:template><xsl:template match="Device"><xsl:param name="_indent" select="0"/><xsl:apply-templates select="Profile/Dictionary/Objects/Object"><xsl:with-param name="_indent" select="$_indent + (1) * $autoindent"/></xsl:apply-templates><xsl:for-each select="RxPdo"><xsl:call-template name="pdo_entries"><xsl:with-param name="direction" select="'Receive'"/></xsl:call-template></xsl:for-each><xsl:for-each select="TxPdo"><xsl:call-template name="pdo_entries"><xsl:with-param name="direction" select="'Transmit'"/></xsl:call-template></xsl:for-each></xsl:template><xsl:template match="Object"><xsl:param name="_indent" select="0"/><xsl:variable name="index"><xsl:value-of select="ns:HexDecValue(Index/text())"/></xsl:variable><xsl:variable name="entry_name"><xsl:value-of select="ns:EntryName(Name)"/></xsl:variable><xsl:choose><xsl:when test="$index &gt;= $min_index and $index &lt;= $max_index"><xsl:variable name="datatype_name"><xsl:value-of select="Type/text()"/></xsl:variable><xsl:variable name="default_value"><xsl:value-of select="Info/DefaultData/text()"/></xsl:variable><xsl:choose><xsl:when test="ancestor::Dictionary/child::DataTypes/DataType[Name/text()=$datatype_name][SubItem]"><xsl:apply-templates select="ancestor::Dictionary/child::DataTypes/DataType[Name/text()=$datatype_name][SubItem]"><xsl:with-param name="_indent" select="$_indent + (1) * $autoindent"/><xsl:with-param name="index"><xsl:value-of select="$index"/></xsl:with-param><xsl:with-param name="entry_name"><xsl:value-of select="$entry_name"/></xsl:with-param><xsl:with-param name="sub_default_value"><xsl:value-of select="Info"/></xsl:with-param></xsl:apply-templates></xsl:when><xsl:otherwise><xsl:variable name="subindex"><xsl:text>0</xsl:text></xsl:variable><xsl:variable name="sub_entry_flag"><xsl:text>0</xsl:text></xsl:variable><xsl:variable name="entry"><xsl:value-of select="ns:AddEntry($index, $subindex, $entry_name, $datatype_name, BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text(), $default_value, $sub_entry_flag)"/></xsl:variable></xsl:otherwise></xsl:choose></xsl:when></xsl:choose></xsl:template><xsl:template match="DataType"><xsl:param name="_indent" select="0"/><xsl:param name="index"/><xsl:param name="entry_name"/><xsl:param name="sub_default_value"/><xsl:for-each select="SubItem"><xsl:variable name="subindex"><xsl:value-of select="ns:HexDecValue(SubIdx/text())"/></xsl:variable><xsl:variable name="subentry_name"><xsl:value-of select="$entry_name"/><xsl:text> - </xsl:text><xsl:value-of select="ns:EntryName(DisplayName, Name/text())"/></xsl:variable><xsl:variable name="sub_entry_flag"><xsl:text>1</xsl:text></xsl:variable><xsl:variable name="entry"><xsl:value-of select="ns:AddEntry($index, $subindex, $subentry_name, Type/text(), BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text(), $sub_default_value, $sub_entry_flag)"/></xsl:variable></xsl:for-each></xsl:template><xsl:template name="pdo_entries"><xsl:param name="_indent" select="0"/><xsl:param name="direction"/><xsl:variable name="pdo_index"><xsl:value-of select="ns:HexDecValue(Index/text())"/></xsl:variable><xsl:variable name="pdo_name"><xsl:value-of select="ns:EntryName(Name)"/></xsl:variable><xsl:for-each select="Entry"><xsl:variable name="index"><xsl:value-of select="ns:HexDecValue(Index/text())"/></xsl:variable><xsl:choose><xsl:when test="$index &gt;= $min_index and $index &lt;= $max_index"><xsl:variable name="subindex"><xsl:value-of select="ns:HexDecValue(SubIndex/text())"/></xsl:variable><xsl:variable name="subentry_name"><xsl:value-of select="ns:EntryName(Name)"/></xsl:variable><xsl:variable name="access"><xsl:choose><xsl:when test="$direction='Transmit'"><xsl:text>ro</xsl:text></xsl:when><xsl:otherwise><xsl:text>wo</xsl:text></xsl:otherwise></xsl:choose></xsl:variable><xsl:variable name="pdo_mapping"><xsl:choose><xsl:when test="$direction='Transmit'"><xsl:text>T</xsl:text></xsl:when><xsl:otherwise><xsl:text>R</xsl:text></xsl:otherwise></xsl:choose></xsl:variable><xsl:variable name="entry"><xsl:value-of select="ns:AddEntry($index, $subindex, $subentry_name, DataType/text(), BitLen/text(), $access, $pdo_mapping, $pdo_index, $pdo_name, $direction)"/></xsl:variable></xsl:when></xsl:choose></xsl:for-each></xsl:template></xsl:stylesheet>
\ No newline at end of file
--- a/etherlab/entries_list.ysl2	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/entries_list.ysl2	Thu Jun 18 10:42:08 2020 +0200
@@ -38,17 +38,20 @@
         choose {
             when "$index >= $min_index and $index <= $max_index" {
                 variable "datatype_name" > «Type/text()»
+                variable "default_value" > «Info/DefaultData/text()»
                 choose {
                     when "ancestor::Dictionary/child::DataTypes/DataType[Name/text()=$datatype_name][SubItem]" {
                         apply "ancestor::Dictionary/child::DataTypes/DataType[Name/text()=$datatype_name][SubItem]" {
                             with "index" > «$index»
                             with "entry_name" > «$entry_name»
+                            with "sub_default_value" > «Info/SubItem/Info/DefaultData/text()»
                         }
                     }
                     otherwise {
                         variable "subindex" > 0
+                        variable "sub_entry_flag" > 0
                         variable "entry" {
-                            > «ns:AddEntry($index, $subindex, $entry_name, $datatype_name, BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text())»
+                            > «ns:AddEntry($index, $subindex, $entry_name, $datatype_name, BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text(), $default_value, $sub_entry_flag)»
                         }
                     }
                 }
@@ -59,11 +62,13 @@
     template "DataType" {
         param "index";
         param "entry_name";
+        param "sub_default_value"
         foreach "SubItem" {
             variable "subindex" > «ns:HexDecValue(SubIdx/text())»
             variable "subentry_name" > «$entry_name» - «ns:EntryName(DisplayName, Name/text())»
+            variable "sub_entry_flag" > 1
             variable "entry" {
-                > «ns:AddEntry($index, $subindex, $subentry_name, Type/text(), BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text())»
+                > «ns:AddEntry($index, $subindex, $subentry_name, Type/text(), BitSize/text(), Flags/Access/text(), Flags/PdoMapping/text(), $sub_default_value, $sub_entry_flag)»
             }
         }
     }
--- a/etherlab/etherlab.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/etherlab.py	Thu Jun 18 10:42:08 2020 +0200
@@ -39,6 +39,13 @@
     return etree.XPath(xpath)
 
 
+EtherCATBaseParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATBase.xsd"))
+
+
+def EtherCATBase_XPath(xpath):
+    return etree.XPath(xpath)
+
+
 def HexDecValue(context, *args):
     return str(ExtractHexDecValue(args[0][0]))
 
@@ -59,6 +66,18 @@
     ("PDO name", str, ""),
     ("PDO type", str, "")]
 
+# Read DefaultValue from ESI file
+# Add by jblee 151229
+ENTRY_INFOS_KEYS_FOR_DV = [
+    ("Index", lambda x: "#x%4.4X" % int(x), "#x0000"),
+    ("SubIndex", str, "0"),
+    ("Name", str, ""),
+    ("Type", str, ""),
+    ("BitSize", int, 0),
+    ("Access", str, ""),
+    ("PDOMapping", str, ""),
+    ("DefaultValue", str, ""),
+    ("Sub_entry_flag", str, "0")]
 
 class EntryListFactory(object):
 
@@ -67,11 +86,17 @@
 
     def AddEntry(self, context, *args):
         index, subindex = map(lambda x: int(x[0]), args[:2])
-        new_entry_infos = {
+        if len(args) > 9:
+		    new_entry_infos = {
             key: translate(arg[0]) if len(arg) > 0 else default
             for (key, translate, default), arg
             in zip(ENTRY_INFOS_KEYS, args)}
-
+        else:
+            new_entry_infos = {
+            key: translate(arg[0]) if len(arg) > 0 else default
+            for (key, translate, default), arg
+            in zip(ENTRY_INFOS_KEYS_FOR_DV, args)}			
+			
         if (index, subindex) != (0, 0):
             entry_infos = self.Entries.get((index, subindex))
             if entry_infos is not None:
@@ -88,7 +113,8 @@
 
 cls = EtherCATInfoParser.GetElementClass("DeviceType")
 if cls:
-
+    cls.DataTypes = None
+    
     profile_numbers_xpath = EtherCATInfo_XPath("Profile/ProfileNo")
 
     def GetProfileNumbers(self):
@@ -102,25 +128,151 @@
         return None
     setattr(cls, "getCoE", getCoE)
 
+    # Modify by jblee
+    def ExtractDataTypes(self):
+        #self.DataTypes = {}
+        #self.DT = {}
+        DT = {}
+        objects = []
+
+        # get Profile Field
+        for profile in self.getProfile():
+            # get each (ProfileNo, Dictionary) Field as child
+            for child in profile.getchildren():
+                # child.text is not None -> ProfileNo, is None -> Dictionary
+                if child.text is None:
+                    # get each (DataTypes, Objects) Field 
+                    dataTypes = child.getDataTypes()
+                    objects = child.getObjects()
+                                
+                    for dataType in dataTypes.getDataType():
+                        #if dataType.getName() is not None:
+                        #    print dataType.getName(), dataType
+                        DT[dataType.getName()] = dataType
+
+        return DT, objects
+    setattr(cls, "ExtractDataTypes", ExtractDataTypes)
+
     def GetEntriesList(self, limits=None):
+        DataTypes, objects = self.ExtractDataTypes()
+
         entries = {}
 
-        factory = EntryListFactory(entries)
-
-        entries_list_xslt_tree = etree.XSLT(
-            entries_list_xslt, extensions={
-                ("entries_list_ns", "AddEntry"): factory.AddEntry,
-                ("entries_list_ns", "HexDecValue"): HexDecValue,
-                ("entries_list_ns", "EntryName"): EntryName})
-        entries_list_xslt_tree(self, **dict(zip(
-            ["min_index", "max_index"],
-            map(lambda x: etree.XSLT.strparam(str(x)),
-                limits if limits is not None else [0x0000, 0xFFFF])
-            )))
-
+        # get each Object Field
+        for object in objects:
+            # Object Field mendatory : Index, Name, Type, BitSize
+            # Frequently Use : Info, Flags
+            # Info Field -> DefaultData, SubItem
+            # Flags Field -> Access, Category, PdoMapping
+            object_index = object.getIndex().getcontent()
+            index = ExtractHexDecValue(object_index)
+            if limits is None or limits[0] <= index <= limits[1]:
+                object_type = object.getType()
+                object_name = ExtractName(object.getName())
+                object_size = object.getBitSize()
+                defaultData = ""
+                object_access = ""
+                object_PDOMapping_data = ""
+
+                object_type_infos = DataTypes.get(object_type, None)
+                subItem_infos = object_type_infos.getchildren()
+                countSubIndex = 0
+                if len(subItem_infos) > 2:
+                    for subItem_info in subItem_infos:
+                        if subItem_info.tag == "SubItem" : 
+                            subItemName = subItem_info.getName()
+                            subIdx = subItem_info.getSubIdx()
+                            if subIdx is not None:
+                                object_subidx = ExtractHexDecValue(subIdx)
+                            else:
+                                object_subidx = ExtractHexDecValue(countSubIndex)
+                            subType = subItem_info.getType()
+                            subBitSize = subItem_info.getBitSize()
+                            subFlags = subItem_info.getFlags()
+                            subAccess = ""
+                            subPDOMapping_data = ""
+                            if subFlags is not None:
+                                subAccess = subFlags.getAccess().getcontent()
+                                subPDOMapping = subFlags.getPdoMapping()                                                        
+                                if subPDOMapping is not None:
+                                    subPDOMapping_data = subFlags.getPdoMapping().upper()
+
+                            entries[(index, object_subidx)] = {
+                                "Index": object_index,
+                                "SubIndex": subIdx,
+                                "Name": "%s - %s" % 
+                                    (object_name.decode("utf-8"),
+                                     subItemName.decode("utf-8")),
+                                "Type": subType,
+                                "BitSize": subBitSize,
+                                "Access": subAccess, 
+                                "PDOMapping": subPDOMapping_data}
+
+                            countSubIndex += 1
+
+                    info = object.getInfo()
+                    # subItemTest : check subItem 
+                    countSubIndex = 0
+                    if info is not None:
+                        subItems = info.getchildren()
+                        if len(subItems) > 1:
+                            for subItem in subItems:
+                                defaultdata_subidx = ExtractHexDecValue(countSubIndex)
+                                defaultData = subItem.getchildren()[1].findtext("DefaultData")
+                                entry = entries.get((index, defaultdata_subidx), None)
+                                if entry is not None:
+                                    entry["DefaultData"] = defaultData
+                                countSubIndex += 1
+
+                else :
+                    info = object.getInfo()
+                    if info is not None:
+                        subItems = info.getchildren()
+                        if len(subItems) <= 1:
+                            defaultData = subItems[0].text
+                                
+                    object_flag = object.getFlags()
+                    object_access = object_flag.getAccess().getcontent()
+                    object_PDOMapping = object_flag.getPdoMapping()
+                    if object_PDOMapping is not None:
+                        object_PDOMapping_data = object_flag.getPdoMapping().upper()
+                    entries[(index, 0)] = {
+                        "Index": object_index,
+                        "SubIndex": "0",
+                        "Name": object_name,                                                               
+                        "Type": object_type,
+                        "BitSize": object_size,
+                        "DefaultData" : defaultData,
+                        "Access": object_access, 
+                        "PDOMapping": object_PDOMapping_data}
+
+        for TxPdo in self.getTxPdo():
+            ExtractPdoInfos(TxPdo, "Transmit", entries, limits)
+        for RxPdo in self.getRxPdo():
+            ExtractPdoInfos(RxPdo, "Receive", entries, limits)
+        
         return entries
     setattr(cls, "GetEntriesList", GetEntriesList)
 
+#    def GetEntriesList(self, limits=None):
+#        entries = {}
+        
+#        factory = EntryListFactory(entries)
+        
+#        entries_list_xslt_tree = etree.XSLT(
+#            entries_list_xslt, extensions = {
+#                ("entries_list_ns", "AddEntry"): factory.AddEntry,
+#                ("entries_list_ns", "HexDecValue"): HexDecValue,
+#                ("entries_list_ns", "EntryName"): EntryName})
+#        entries_list_xslt_tree(self, **dict(zip(
+#            ["min_index", "max_index"], 
+#            map(lambda x: etree.XSLT.strparam(str(x)),
+#                limits if limits is not None else [0x0000, 0xFFFF])
+#            )))
+#        
+#        return entries
+#    setattr(cls, "GetEntriesList", GetEntriesList)
+
     def GetSyncManagers(self):
         sync_managers = []
         for sync_manager in self.getSm():
@@ -155,6 +307,40 @@
             SortGroupItems(item)
     group["children"].sort(GroupItemCompare)
 
+def ExtractPdoInfos(pdo, pdo_type, entries, limits=None):
+    pdo_index = pdo.getIndex().getcontent()
+    pdo_name = ExtractName(pdo.getName())
+    exclude = pdo.getExclude()
+    for pdo_entry in pdo.getEntry():
+        entry_index = pdo_entry.getIndex().getcontent()
+        entry_subindex = pdo_entry.getSubIndex()
+        index = ExtractHexDecValue(entry_index)
+        subindex = ExtractHexDecValue(entry_subindex)
+        object_size = pdo_entry.getBitLen()
+
+        if limits is None or limits[0] <= index <= limits[1]:
+            entry = entries.get((index, subindex), None)
+            if entry is not None:
+                entry["PDO index"] = pdo_index
+                entry["PDO name"] = pdo_name
+                entry["PDO type"] = pdo_type
+            else:
+                entry_type = pdo_entry.getDataType()
+                if entry_type is not None:
+                    if pdo_type == "Transmit":
+                        access = "ro"
+                        pdomapping = "T"
+                    else:
+                        access = "wo"
+                        pdomapping = "R"
+                    entries[(index, subindex)] = {
+                        "Index": entry_index,
+                        "SubIndex": entry_subindex,
+                        "Name": ExtractName(pdo_entry.getName()),
+                        "Type": entry_type.getcontent(),
+                        "BitSize": object_size,
+                        "Access": access,
+                        "PDOMapping": pdomapping}
 
 class ModulesLibrary(object):
 
@@ -211,10 +397,21 @@
 
     groups_xpath = EtherCATInfo_XPath("Descriptions/Groups/Group")
     devices_xpath = EtherCATInfo_XPath("Descriptions/Devices/Device")
+    module_xpath = EtherCATBase_XPath("Descriptions/Modules/Module")
 
     def LoadModules(self):
         self.Library = {}
-
+        # add by jblee for Modular Device Profile
+        self.MDPList = []
+        self.ModuleList = []
+        self.MDPEntryList = {}
+        dtDic = {}
+        self.idxIncrement = 0
+        self.slotIncrement = 0
+        # add by jblee for PDO Mapping
+        self.DataTypes = {}
+        self.ObjectDictionary = {}
+        
         files = os.listdir(self.Path)
         for file in files:
             filepath = os.path.join(self.Path, file)
@@ -224,9 +421,9 @@
                 xmlfile = open(filepath, 'r')
                 try:
                     self.modules_infos, error = EtherCATInfoParser.LoadXMLString(xmlfile.read())
-                    if error is not None:
-                        self.GetCTRoot().logger.write_warning(
-                            XSDSchemaErrorMessage % (filepath + error))
+                    # if error is not None:
+                    #     self.GetCTRoot().logger.write_warning(
+                    #         XSDSchemaErrorMessage % (filepath + error))
                 except Exception as exc:
                     self.modules_infos, error = None, text(exc)
                 xmlfile.close()
@@ -241,6 +438,9 @@
 
                     for group in self.groups_xpath(self.modules_infos):
                         group_type = group.getType()
+                        # add for XmlToEeprom Func by jblee.
+                        self.LcId_data = group.getchildren()[1]
+                        self.Image16x14_data = group.getchildren()[2]
 
                         vendor_category["groups"].setdefault(
                             group_type,
@@ -248,8 +448,9 @@
                                 "name": ExtractName(group.getName(), group_type),
                                 "parent": group.getParentGroup(),
                                 "order": group.getSortOrder(),
-                                # "value": group.getcontent()["value"],
                                 "devices": [],
+                                # add jblee for support Moduler Device Profile (MDP)
+                                "modules": []})
                             })
 
                     for device in self.devices_xpath(self.modules_infos):
@@ -259,13 +460,98 @@
                         vendor_category["groups"][device_group]["devices"].append(
                             (device.getType().getcontent(), device))
 
-                else:
-
-                    self.GetCTRoot().logger.write_error(
-                        _("Couldn't load {a1} XML file:\n{a2}").format(a1=filepath, a2=error))
+                        # ------------------ Test Section --------------------#
+                        slots = device.getSlots()
+                        if slots is not None:
+                            for slot in slots.getSlot():
+                                self.idxIncrement = slot.getSlotIndexIncrement()
+                                self.slotIncrement = slot.getSlotPdoIncrement()
+                                for child in slot.getchildren():
+                                    if child.tag == "ModuleClass":
+                                        child_class = child.getClass()
+                                        child_name = child.getName()
+
+                    # -------------------- Test Section ----------------------------------# 
+                        LocalMDPList = []
+                        for module in self.module_xpath(self.modules_infos):
+                            module_type = module.getType().getModuleClass()
+                            module_name = module.getName()
+                            LocalMDPData = ExtractName(module_name) + " (" + module_type + ")"
+                            
+                            self.ModuleList.append(module)
+                            try :
+                                module_pdos = module.getTxPdo()
+                                module_pdos += module.getRxPdo()
+                                for module_pdo in module_pdos:
+                                    device_name = ExtractName(module_name)
+                                    pdo_index = module_pdo.getIndex().getcontent()
+                                    pdo_name = ExtractName(module_pdo.getName())
+                                    pdo_entry = module_pdo.getEntry()
+                                    if module_pdo.tag == "TxPdo":
+                                        mapping_type = "T"
+                                    else :
+                                        mapping_type = "R"
+
+                                    LocalMDPEntry = []
+                                    for entry in pdo_entry:
+                                        entry_index = entry.getIndex().getcontent()
+                                        entry_subidx = entry.getSubIndex()
+                                        entry_name = ExtractName(entry.getName())
+                                        entry_bitsize = entry.getBitLen()
+                                        try :
+                                           entry_type = entry.getDataType().getcontent()
+                                        except :
+                                           entry_type = ""
+
+                                        LocalMDPEntry.append({
+                                            "Index": entry_index,
+                                            "SubIndex": entry_subidx,
+                                            "Name": "%s - %s" % (pdo_name, entry_name),
+                                            "Type": entry_type,
+                                            "BitSize": entry_bitsize,
+                                            "Access": "", 
+                                            "PDOMapping": mapping_type})
+                                
+                                    self.MDPEntryList[device_name] = LocalMDPEntry
+
+                                LocalMDPList.append([LocalMDPData, module, LocalMDPEntry])
+                            except :
+                                LocalMDPList.append([LocalMDPData, module, []])
+                           
+                        if LocalMDPList:
+                            vendor_category["groups"][device_group]["modules"].append(
+                                (device.getType().getcontent(), LocalMDPList, self.idxIncrement, self.slotIncrement))
+                            #self.MDPList.append([device.getType().getcontent(), LocalMDPList,
+                            #                     self.idxIncrement, self.slotIncrement])
+
+                    # --------------------------------------------------------------------- #
+
+                # else:
+                #     self.GetCTRoot().logger.write_error(
+                #         _("Couldn't load {a1} XML file:\n{a2}").format(a1=filepath, a2=error))
 
         return self.Library
 
+    # add jblee
+    def GetMDPList(self):
+        return self.MDPList
+
+    # add jblee
+    def GetSelectModule(self, idx):
+        return self.ModuleList[idx]
+
+    # add jblee
+    def GetModuleEntryList(self):
+        return self.MDPEntryList
+
+    # add jblee
+    def GetModuleIncrement(self):
+        return (self.idxIncrement, self.slotIncrement)
+
+    # add jblee
+    #def GetEntriesList(self):
+    #    return self.ObjectDictionary
+    
     def GetModulesLibrary(self, profile_filter=None):
         if self.Library is None:
             self.LoadModules()
@@ -332,6 +618,18 @@
                         return device_infos, self.GetModuleExtraParams(vendor, product_code, revision_number)
         return None, None
 
+    # add jblee for MDP
+    def GetMDPInfos(self, module_infos):
+        vendor = ExtractHexDecValue(module_infos["vendor"])
+        vendor_infos = self.Library.get(vendor)
+        if vendor_infos is not None:
+            for group_name, group_infos in vendor_infos["groups"].iteritems():
+                return group_infos["modules"]
+                #for device_type, module_list, idx_inc, slot_inc in group_infos["modules"]:
+                #    return module_list, idx_inc, slot_inc
+
+        #return None, None, None
+    
     def ImportModuleLibrary(self, filepath):
         if os.path.isfile(filepath):
             shutil.copy(filepath, self.Path)
@@ -452,8 +750,31 @@
     def GetModulesLibrary(self, profile_filter=None):
         return self.ModulesLibrary.GetModulesLibrary(profile_filter)
 
+    # add jblee
+    def GetMDPList(self):
+        return self.ModulesLibrary.GetMDPList()
+
+    # add jblee
+    def GetSelectModule(self, idx):
+        return self.ModulesLibrary.GetSelectModule(idx)
+
+    # add jblee
+    def GetModuleEntryList(self):
+        return self.ModulesLibrary.GetModuleEntryList()
+
+    # add jblee
+    def GetModuleIncrement(self):
+        return self.ModulesLibrary.GetModuleIncrement()
+
+    # add jblee
+    #def GetEntriesList(self, limits = None):
+    #    return self.ModulesLibrary.GetEntriesList()
+
     def GetVendors(self):
         return self.ModulesLibrary.GetVendors()
 
     def GetModuleInfos(self, module_infos):
         return self.ModulesLibrary.GetModuleInfos(module_infos)
+
+    def GetMDPInfos(self, module_infos):
+        return self.ModulesLibrary.GetMDPInfos(module_infos)
--- a/etherlab/plc_cia402node.c	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/plc_cia402node.c	Thu Jun 18 10:42:08 2020 +0200
@@ -3,6 +3,8 @@
 Template C code used to produce target Ethercat C CIA402 code
 
 Copyright (C) 2011-2014: Laurent BESSARD, Edouard TISSERANT
+                         RTES Lab : CRKim, JBLee, youcu
+                         Higen Motor : Donggu Kang
 
 Distributed under the terms of the GNU Lesser General Public License as
 published by the Free Software Foundation; either version 2 of the License, or
@@ -33,7 +35,25 @@
 xxxx xxxx x0xx 1111 | Fault reaction active
 xxxx xxxx x0xx 1000 | Fault
 */
+
+//ssh_add
+/* From CiA402, Page 63 Statusword for homing mode
+
+		Table 106 - Definition of bit 10, bit 12, bit 13
+
+xx00 x0xx xxxx xxxx | Homing procedure is in progress
+xx00 x1xx xxxx xxxx | Homing procedure is interrupted or not started
+xx01 x0xx xxxx xxxx | Homing is attained, but target is not reached
+xx01 x1xx xxxx xxxx | Homing procedure is completed successfully
+xx10 x0xx xxxx xxxx | Homing error occurred, velocity is not 0
+xx10 x1xx xxxx xxxx | Homing error occurred, velocity is 0
+xx11 xxxx xxxx xxxx | reserved
+*/
+
 #define FSAFromStatusWord(SW) (SW & 0x006f)
+//ssh_add
+#define HomingStatusWord(SW) (SW & 0x3400)
+#define FaultFromStatusWord(SW) (SW & 0x0008)
 #define NotReadyToSwitchOn  0b00000000 FSA_sep 0b00100000
 #define SwitchOnDisabled    0b01000000 FSA_sep 0b01100000
 #define ReadyToSwitchOn     0b00100001
@@ -43,7 +63,16 @@
 #define FaultReactionActive 0b00001111 FSA_sep 0b00101111
 #define Fault               0b00001000 FSA_sep 0b00101000
 
-// SatusWord bits :
+//ssh_add
+#define HomingInProgress	0b0000000000000000
+#define HomingNotRunning	0b0000010000000000
+#define HomingNotReached	0b0001000000000000
+#define Homing_Completed	0b0001010000000000
+#define HomingErrorInVelo	0b0010000000000000
+#define HomingErrorNotVelo	0b0010010000000000
+#define HomingReserved		0b0011000000000000 FSA_sep 0b0011010000000000
+
+// StatusWord bits :
 #define SW_ReadyToSwitchOn     0x0001
 #define SW_SwitchedOn          0x0002
 #define SW_OperationEnabled    0x0004
@@ -56,6 +85,10 @@
 #define SW_TargetReached       0x0400
 #define SW_InternalLimitActive 0x0800
 
+//ssh_add
+#define SW_HomingAttained		0x1000
+#define SW_HomingError			0x2000
+
 // ControlWord bits :
 #define SwitchOn        0x0001
 #define EnableVoltage   0x0002
@@ -64,11 +97,15 @@
 #define FaultReset      0x0080
 #define Halt            0x0100
 
-
-IEC_INT beremiz__IW%(location_str)s = %(slave_pos)s;
-IEC_INT *__IW%(location_str)s = &beremiz__IW%(location_str)s;
-IEC_INT beremiz__IW%(location_str)s_402;
-IEC_INT *__IW%(location_str)s_402 = &beremiz__IW%(location_str)s_402;
+//ssh_add
+//#define Homing_OperationStart 0x0010
+#define Homing_OperationStart_Origin 0x0010
+#define Homing_OperationStart_Edit 0x001F
+
+IEC_INT beremiz__IW%(location)s = %(slave_pos)s;
+IEC_INT *__IW%(location)s = &beremiz__IW%(location)s;
+IEC_INT beremiz__IW%(location)s_402;
+IEC_INT *__IW%(location)s_402 = &beremiz__IW%(location)s_402;
 
 %(MCL_headers)s
 
@@ -104,7 +141,7 @@
     axis_s* axis;
 } __CIA402Node;
 
-#define AxsPub __CIA402Node_%(location_str)s
+#define AxsPub __CIA402Node_%(location)s
 
 static __CIA402Node AxsPub;
 
@@ -112,25 +149,24 @@
 
 %(fieldbus_interface_declaration)s
 
-int __init_%(location_str)s()
+int __init_%(location)s()
 {
     __FirstTick = 1;
 %(init_entry_variables)s
-	*(AxsPub.ModesOfOperation) = 0x08;
     return 0;
 }
 
-void __cleanup_%(location_str)s()
-{
-}
-
-void __retrieve_%(location_str)s()
+void __cleanup_%(location)s()
+{
+}
+
+void __retrieve_%(location)s()
 {
 	if (__FirstTick) {
-		*__IW%(location_str)s_402 = __MK_Alloc_AXIS_REF();
+		*__IW%(location)s_402 = __MK_Alloc_AXIS_REF();
 		AxsPub.axis = 
-            __MK_GetPublic_AXIS_REF(*__IW%(location_str)s_402);
-		AxsPub.axis->NetworkPosition = beremiz__IW%(location_str)s;
+            __MK_GetPublic_AXIS_REF(*__IW%(location)s_402);
+		AxsPub.axis->NetworkPosition = beremiz__IW%(location)s;
 %(init_axis_params)s
 %(fieldbus_interface_definition)s
 		__FirstTick = 0;
@@ -146,15 +182,13 @@
         AxsPub.axis->PowerFeedback = FSA == OperationEnabled;
     }
 #undef FSA_sep 
-	AxsPub.axis->ActualRawPosition = *(AxsPub.ActualPosition);
-	AxsPub.axis->ActualRawVelocity = *(AxsPub.ActualVelocity);
-	AxsPub.axis->ActualRawTorque = *(AxsPub.ActualTorque);
+%(default_variables_retrieve)s
 
 	// Extra variables retrieve
 %(extra_variables_retrieve)s
 }
 
-void __publish_%(location_str)s()
+void __publish_%(location)s()
 {
 	IEC_BOOL power = 
         ((*(AxsPub.StatusWord) & SW_VoltageEnabled) != 0) 
@@ -181,37 +215,61 @@
                 CW |= SwitchOn | EnableVoltage | QuickStop | EnableOperation;
 	    	}
 	    	break;
-	    case Fault :
-            /* TODO reset fault only when MC_Reset */
-            CW &= ~(SwitchOn | EnableVoltage | QuickStop | EnableOperation);
-            CW |= FaultReset;
-	    	break;
+			//ssh_check
+//	    case Fault :
+//            /* TODO reset fault only when MC_Reset */
+//	    	AxsPub.axis->DriveFault = 1;
+//            CW &= ~(SwitchOn | EnableVoltage | QuickStop | EnableOperation);
+//            CW |= FaultReset;
+//	    	break;
 	    default:
 	    	break;
 	}
+	//ssh_add
+	if(FaultFromStatusWord(*(AxsPub.StatusWord)) == SW_Fault)
+		AxsPub.axis->DriveFault = 1;
+	else{
+		AxsPub.axis->DriveFault = 0;
+		AxsPub.axis->DriveFaultReset = 0;
+	}
+	if(AxsPub.axis->DriveFaultReset){
+		CW &= ~(SwitchOn | EnableVoltage | QuickStop | EnableOperation);
+		CW |= FaultReset;
+	}
+
+	//ssh_add
+	switch (HomingStatusWord(*(AxsPub.StatusWord))) {
+		case HomingInProgress:
+			break;
+		case HomingNotRunning:
+			break;
+		case HomingNotReached:
+			break;
+		case Homing_Completed:
+			if(!AxsPub.axis->HomingCompleted)
+				AxsPub.axis->HomingCompleted = 1;
+			break;
+		case HomingErrorInVelo:
+		case HomingErrorNotVelo:
+			if(!AxsPub.axis->HomingCompleted)
+				AxsPub.axis->HomingCompleted = 1;
+			break;
+		case HomingReserved:
+			break;
+	}
 #undef FSA_sep 
-    *(AxsPub.ControlWord) = CW;
+
+	//ssh_add
+%(modeofop_homing_method)s
+
+	*(AxsPub.ControlWord) = CW;
+
 
 	// CIA402 node modes of operation computation according to axis motion mode
-	switch (AxsPub.axis->AxisMotionMode) {
-		case mc_mode_cst:
-			*(AxsPub.ModesOfOperation) = 0x0a;
-			break;
-		case mc_mode_csv:
-			*(AxsPub.ModesOfOperation) = 0x09;
-			break;
-		default:
-			*(AxsPub.ModesOfOperation) = 0x08;
-			break;
-	}
+%(modeofop_computation_mode)s
 
 	// Default variables publish
-	*(AxsPub.TargetPosition) = 
-            AxsPub.axis->RawPositionSetPoint;
-	*(AxsPub.TargetVelocity) = 
-            AxsPub.axis->RawVelocitySetPoint;
-	*(AxsPub.TargetTorque) = 
-            AxsPub.axis->RawTorqueSetPoint;
+%(default_variables_publish)s
 
 	// Extra variables publish
 %(extra_variables_publish)s
--- a/etherlab/plc_etherlab.c	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/plc_etherlab.c	Thu Jun 18 10:42:08 2020 +0200
@@ -32,6 +32,43 @@
 %(used_pdo_entry_configuration)s
     {}
 };
+
+// Distributed Clock variables;
+%(dc_variable)s
+unsigned long long comp_period_ns = 500000ULL;
+
+int comp_count = 1;
+int comp_count_max;
+
+#define DC_FILTER_CNT          1024
+
+// EtherCAT slave-time-based DC Synchronization variables.
+static uint64_t dc_start_time_ns = 0LL;
+static uint64_t dc_time_ns = 0;
+static uint8_t  dc_started = 0;
+static int32_t  dc_diff_ns = 0;
+static int32_t  prev_dc_diff_ns = 0;
+static int64_t  dc_diff_total_ns = 0LL;
+static int64_t  dc_delta_total_ns = 0LL;
+static int      dc_filter_idx = 0;
+static int64_t  dc_adjust_ns;
+static int64_t  system_time_base = 0LL;
+
+static uint64_t dc_first_app_time = 0LL;
+
+unsigned long long frame_period_ns = 0ULL;
+
+int debug_count = 0;
+int slave_dc_used = 0;
+
+void dc_init(void);
+uint64_t system_time_ns(void);
+RTIME system2count(uint64_t time);
+void sync_distributed_clocks(void);
+void update_master_clock(void);
+RTIME calculate_sleeptime(uint64_t wakeup_time);
+uint64_t calculate_first(void);
+
 /*****************************************************************************/
 
 %(pdos_configuration_declaration)s
@@ -50,7 +87,7 @@
     LogMessage(level, sbuf, slen);\
 }
 
-/* Beremiz plugin functions */
+/* EtherCAT plugin functions */
 int __init_%(location)s(int argc,char **argv)
 {
     uint32_t abort_code;
@@ -81,10 +118,34 @@
     ecrt_master_set_send_interval(master, common_ticktime__);
 
     // slaves initialization
+/*
 %(slaves_initialization)s
+*/
+    // configure DC SYNC0/1 Signal
+%(config_dc)s
+
+    // select reference clock
+#if DC_ENABLE
+    {
+        int ret;
+        
+        ret = ecrt_master_select_reference_clock(master, slave0);
+        if (ret <0) {
+            fprintf(stderr, "Failed to select reference clock : %%s\n",
+                strerror(-ret));
+            return ret;
+        }
+    }
+#endif
 
     // extracting default value for not mapped entry in output PDOs
+/*
 %(slaves_output_pdos_default_values_extraction)s
+*/
+
+#if DC_ENABLE
+    dc_init();
+#endif
 
     if (ecrt_master_activate(master)){
         SLOGF(LOG_CRITICAL, "EtherCAT Master activation failed");
@@ -126,17 +187,20 @@
 
 }
 
+/*
 static RTIME _last_occur=0;
 static RTIME _last_publish=0;
 RTIME _current_lag=0;
 RTIME _max_jitter=0;
 static inline RTIME max(RTIME a,RTIME b){return a>b?a:b;}
+*/
 
 void __publish_%(location)s(void)
 {
 %(publish_variables)s
     ecrt_domain_queue(domain1);
     {
+        /*
         RTIME current_time = rt_timer_read();
         // Limit spining max 1/5 of common_ticktime
         RTIME maxdeadline = current_time + (common_ticktime__ / 5);
@@ -162,7 +226,281 @@
             //Consuming security margin ?
             _last_occur = current_time; //Drift forward
         }
-    }
+        */
+    }
+
+#if DC_ENABLE
+    if (comp_count == 0)
+        sync_distributed_clocks();
+#endif
+
     ecrt_master_send(master);
     first_sent = 1;
-}
+
+#if DC_ENABLE
+    if (comp_count == 0)
+        update_master_clock();
+
+    comp_count++;
+    
+    if (comp_count == comp_count_max)
+        comp_count = 0;
+#endif
+
+}
+
+/* Test Function For Parameter (SDO) Set */
+
+/*
+void GetSDOData(void){
+    uint32_t abort_code, test_value;
+    size_t result_size;
+    uint8_t value[4];
+
+    abort_code = 0;
+    result_size = 0;
+    test_value = 0;
+
+    if (ecrt_master_sdo_upload(master, 0, 0x1000, 0x0, (uint8_t *)value, 4, &result_size, &abort_code)) {
+        SLOGF(LOG_CRITICAL, "EtherCAT failed to get SDO Value");
+        }
+        test_value = EC_READ_S32((uint8_t *)value);
+        SLOGF(LOG_INFO, "SDO Value %%d", test_value);
+}
+*/
+
+int GetMasterData(void){
+    master = ecrt_open_master(0);
+    if (!master) {
+        SLOGF(LOG_CRITICAL, "EtherCAT master request failed!");
+        return -1;
+    }
+    return 0;
+}
+
+void ReleaseMasterData(void){
+    ecrt_release_master(master);
+}
+
+uint32_t GetSDOData(uint16_t slave_pos, uint16_t idx, uint8_t subidx, int size){
+    uint32_t abort_code, return_value;
+    size_t result_size;
+    uint8_t value[size];
+
+    abort_code = 0;
+    result_size = 0;
+
+    if (ecrt_master_sdo_upload(master, slave_pos, idx, subidx, (uint8_t *)value, size, &result_size, &abort_code)) {
+        SLOGF(LOG_CRITICAL, "EtherCAT failed to get SDO Value %%d %%d", idx, subidx);
+    }
+
+    return_value = EC_READ_S32((uint8_t *)value);
+    //SLOGF(LOG_INFO, "SDO Value %%d", return_value);
+
+    return return_value;
+}
+
+/*****************************************************************************/
+
+void dc_init(void)
+{
+    slave_dc_used = 1;
+
+    frame_period_ns = common_ticktime__;
+    if (frame_period_ns <= comp_period_ns) {
+        comp_count_max = comp_period_ns / frame_period_ns;
+        comp_count = 0;
+    } else  {
+        comp_count_max = 1;
+        comp_count = 0;
+    }
+
+    /* Set the initial master time */
+    dc_start_time_ns = system_time_ns();
+    dc_time_ns = dc_start_time_ns;
+
+    /* by woonggy */
+    dc_first_app_time = dc_start_time_ns;
+
+    /*
+     * Attention : The initial application time is also used for phase
+     * calculation for the SYNC0/1 interrupts. Please be sure to call it at
+     * the correct phase to the realtime cycle.
+     */
+    ecrt_master_application_time(master, dc_start_time_ns);
+}
+
+/****************************************************************************/
+
+/*
+ * Get the time in ns for the current cpu, adjusted by system_time_base.
+ *
+ * \attention Rather than calling rt_timer_read() directly, all application
+ * time calls should use this method instead.
+ *
+ * \ret The time in ns.
+ */
+uint64_t system_time_ns(void)
+{
+    RTIME time = rt_timer_read();   // wkk
+
+    if (unlikely(system_time_base > (SRTIME) time)) {
+        fprintf(stderr, "%%s() error: system_time_base greater than"
+                " system time (system_time_base: %%ld, time: %%llu\n",
+                __func__, system_time_base, time);
+        return time;
+    }
+    else {
+        return time - system_time_base;
+    }
+}
+
+/****************************************************************************/
+
+// Convert system time to Xenomai time in counts (via the system_time_base).
+RTIME system2count(uint64_t time)
+{
+    RTIME ret;
+
+    if ((system_time_base < 0) &&
+            ((uint64_t) (-system_time_base) > time)) {
+        fprintf(stderr, "%%s() error: system_time_base less than"
+                " system time (system_time_base: %%I64d, time: %%ld\n",
+                __func__, system_time_base, time);
+        ret = time;
+    }
+    else {
+        ret = time + system_time_base;
+    }
+
+    return (RTIME) rt_timer_ns2ticks(ret); // wkk
+}
+
+/*****************************************************************************/
+
+// Synchronise the distributed clocks
+void sync_distributed_clocks(void)
+{
+    uint32_t ref_time = 0;
+    RTIME prev_app_time = dc_time_ns;
+
+    // get reference clock time to synchronize master cycle
+    if(!ecrt_master_reference_clock_time(master, &ref_time)) {
+        dc_diff_ns = (uint32_t) prev_app_time - ref_time;
+    }
+    // call to sync slaves to ref slave
+    ecrt_master_sync_slave_clocks(master);
+    // set master time in nano-seconds
+    dc_time_ns = system_time_ns();
+    ecrt_master_application_time(master, dc_time_ns);
+}
+
+/*****************************************************************************/
+
+/*
+ * Return the sign of a number
+ * ie -1 for -ve value, 0 for 0, +1 for +ve value
+ * \ret val the sign of the value
+ */
+#define sign(val) \
+        ({ typeof (val) _val = (val); \
+        ((_val > 0) - (_val < 0)); })
+
+/*****************************************************************************/
+
+/*
+ * Update the master time based on ref slaves time diff
+ * called after the ethercat frame is sent to avoid time jitter in
+ * sync_distributed_clocks()
+ */
+void update_master_clock(void)
+{
+    // calc drift (via un-normalised time diff)
+    int32_t delta = dc_diff_ns - prev_dc_diff_ns;
+    prev_dc_diff_ns = dc_diff_ns;
+
+    // normalise the time diff
+    dc_diff_ns = dc_diff_ns >= 0 ?
+            ((dc_diff_ns + (int32_t)(frame_period_ns / 2)) %%
+                    (int32_t)frame_period_ns) - (frame_period_ns / 2) :
+                    ((dc_diff_ns - (int32_t)(frame_period_ns / 2)) %%
+                            (int32_t)frame_period_ns) - (frame_period_ns / 2) ;
+
+    // only update if primary master
+    if (dc_started) {
+        // add to totals
+        dc_diff_total_ns += dc_diff_ns;
+        dc_delta_total_ns += delta;
+        dc_filter_idx++;
+
+        if (dc_filter_idx >= DC_FILTER_CNT) {
+            dc_adjust_ns += dc_delta_total_ns >= 0 ?
+                    ((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / DC_FILTER_CNT) :
+                    ((dc_delta_total_ns - (DC_FILTER_CNT / 2)) / DC_FILTER_CNT) ;
+
+            // and add adjustment for general diff (to pull in drift)
+            dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT);
+
+            // limit crazy numbers (0.1%% of std cycle time)
+            if (dc_adjust_ns < -1000) {
+                dc_adjust_ns = -1000;
+            }
+            if (dc_adjust_ns > 1000) {
+                dc_adjust_ns =  1000;
+            }
+            // reset
+            dc_diff_total_ns = 0LL;
+            dc_delta_total_ns = 0LL;
+            dc_filter_idx = 0;
+        }
+        // add cycles adjustment to time base (including a spot adjustment)
+        system_time_base += dc_adjust_ns + sign(dc_diff_ns);
+    }
+    else {
+        dc_started = (dc_diff_ns != 0);
+
+        if (dc_started) {
+#if DC_ENABLE && DEBUG_MODE
+            // output first diff
+            fprintf(stderr, "First master diff: %%d\n", dc_diff_ns);
+#endif
+            // record the time of this initial cycle
+            dc_start_time_ns = dc_time_ns;
+        }
+    }
+}
+
+/*****************************************************************************/
+
+/*
+ * Calculate the sleeptime
+ */
+RTIME calculate_sleeptime(uint64_t wakeup_time)
+{
+    RTIME wakeup_count = system2count (wakeup_time);
+    RTIME current_count = rt_timer_read();
+
+    if ((wakeup_count < current_count) || (wakeup_count > current_count + (50 * frame_period_ns)))  {
+        fprintf(stderr, "%%s(): unexpected wake time! wc = %%lld\tcc = %%lld\n", __func__, wakeup_count, current_count);
+    }
+
+    return wakeup_count;
+}
+
+/*****************************************************************************/
+
+/*
+ * Calculate the sleeptime
+ */
+uint64_t calculate_first(void)
+{
+    uint64_t dc_remainder = 0LL;
+    uint64_t dc_phase_set_time = 0LL;
+    
+    dc_phase_set_time = system_time_ns()+ frame_period_ns * 10;
+    dc_remainder = (dc_phase_set_time - dc_first_app_time) %% frame_period_ns;
+
+    return dc_phase_set_time + frame_period_ns - dc_remainder;
+}
+
+/*****************************************************************************/
--- a/etherlab/pous.xml	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/pous.xml	Thu Jun 18 10:42:08 2020 +0200
@@ -110,13 +110,13 @@
       VALUE := 'None';
     END_IF;
   1:
-    {if (AcquireSDOLock()) __SET_VAR(data__->,STATE,, 2)}
+    {if (AcquireSDOLock()) __SET_VAR(data__->,STATE, 2)}
   2:
     IF PY0.ACK THEN
       STATE := 3;
     END_IF;
   3:
-    {if (HasAnswer()) __SET_VAR(data__->,STATE,, 4)}
+    {if (HasAnswer()) __SET_VAR(data__->,STATE, 4)}
   4:
     IF PY1.ACK THEN
       ACK := 1;
@@ -231,13 +231,13 @@
       ERROR := 0;
     END_IF;
   1:
-    {if (AcquireSDOLock()) __SET_VAR(data__->,STATE,, 2)}
+    {if (AcquireSDOLock()) __SET_VAR(data__->,STATE, 2)}
   2:
     IF PY0.ACK THEN
       STATE := 3;
     END_IF;
   3:
-    {if (HasAnswer()) __SET_VAR(data__->,STATE,, 4)}
+    {if (HasAnswer()) __SET_VAR(data__->,STATE, 4)}
   4:
     IF PY1.ACK THEN
       ACK := 1;
--- a/etherlab/runtime_etherlab.py	Thu Jun 04 11:14:21 2020 +0200
+++ b/etherlab/runtime_etherlab.py	Thu Jun 18 10:42:08 2020 +0200
@@ -1,3 +1,14 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# this file is part of beremiz
+#
+# copyright (c) 2011-2014: laurent bessard, edouard tisserant
+#                          rtes lab : crkim, jblee, youcu
+#                          higen motor : donggu kang
+#
+# see copying file for copyrights details.
+
 from __future__ import absolute_import
 import os
 import signal