# HG changeset patch # User Edouard Tisserant # Date 1574265435 -3600 # Node ID c9deff128c3781f5bd5cbfb4a4e3cb1c51705fa7 # Parent 09d5d14566165b431760fdafa51748422c2d783c EtherCat master plugin : commit changes recovered from KOSMOS 2018 installer, unkown author(s). diff -r 09d5d1456616 -r c9deff128c37 etherlab/CommonEtherCATFunction.py --- a/etherlab/CommonEtherCATFunction.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/CommonEtherCATFunction.py Wed Nov 20 16:57:15 2019 +0100 @@ -10,8 +10,9 @@ import os import wx - -mailbox_protocols = ["AoE", "EoE", "CoE", "FoE", "SoE", "VoE"] +import re + +from lxml import objectify def ExtractHexDecValue(value): """ @@ -77,18 +78,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) @@ -112,6 +116,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 @@ -136,6 +159,13 @@ returnVal =result """ +# ethercat pdos +PDOS = """ +import commands +result = commands.getoutput("ethercat pdos -p 0") +returnVal =result +""" + #-------------------------------------------------- # Common Method For EtherCAT Management #-------------------------------------------------- @@ -144,12 +174,38 @@ # ----- 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 = [] + # 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 @@ -180,7 +236,7 @@ self.Controler = controler self.ClearSDODataSet() - + #------------------------------------------------------------------------------- # Used Master State #------------------------------------------------------------------------------- @@ -189,7 +245,6 @@ Execute "ethercat master" command and parse the execution result @return MasterState """ - # exectute "ethercat master" command error, return_val = self.Controler.RemoteExec(MASTER_STATE, return_val = None) master_state = {} @@ -226,20 +281,11 @@ """ error, return_val = self.Controler.RemoteExec(GET_SLAVE, return_val = None) self.SlaveState = return_val - return return_val + return return_val #------------------------------------------------------------------------------- # 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. @@ -248,8 +294,11 @@ @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): """ @@ -268,14 +317,318 @@ Clear the specified SDO entry information. """ for count in range(6): - self.SaveSDOData.append([]) - + self.SDOVariables.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()) @@ -292,7 +645,7 @@ def SavePDOData(self, device): """ Parse PDO data and store the results in TXPDOCategory and RXPDOCategory - Tx(Rx)PDOCategory : index, name, entry number + Tx(Rx)PDOCategory : index, name, entry number, exclude list, Sm Tx(Rx)Info : entry index, sub index, name, length, type @param device : Slave information extracted from ESI XML file """ @@ -302,10 +655,17 @@ 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 - + # Parse entries for entry in entries: # Save index and subindex @@ -317,13 +677,14 @@ "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 @@ -332,7 +693,14 @@ 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 @@ -347,13 +715,14 @@ "entry_index" : index, "subindex" : subindex, "name" : ExtractName(entry.getName()), - "bitlen" : str(entry.getBitLen()), - "type" : entry.getDataType().getcontent() - } + "bitlen" : 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} + 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): @@ -392,10 +761,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 @@ -460,23 +829,33 @@ type_infos = slave.getType() device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos) + #from decimal import Decimal + # 'device' represents current slave device selected by user if device is not None: - for eeprom_element in device.getEeprom().getcontent(): + # dir() method print available method list + #print dir(device.getEeprom().getchildren()[1]) + + # success get subitem second object + #print objectify.fromstring(device.getEeprom().getchildren()[1].tostring()).text + + for eeprom_element in device.getEeprom().getchildren(): # get EEPROM size; -- - if eeprom_element["name"] == "ByteSize": - smartview_infos["eeprom_size"] = eeprom_element + if eeprom_element.tag == "ByteSize": + smartview_infos["eeprom_size"] = objectify.fromstring(eeprom_element.tostring()).text - elif eeprom_element["name"] == "ConfigData": - configData_data = self.DecimalToHex(eeprom_element) + elif eeprom_element.tag == "ConfigData": + # ConfigData Field Datatype??? + #print type(objectify.fromstring(eeprom_element.tostring()).text) + configData_data = self.DecimalToHex(objectify.fromstring(eeprom_element.tostring()).text) # get PDI type; -- address 0x00 smartview_infos["pdi_type"] = int(configData_data[0:2], 16) # get state of device emulation; -- address 0x01 - if "{:0>8b}".format(int(configData_data[2:4], 16))[7] == '1': + if len(configData_data) > 3 and "{:0>8b}".format(int(configData_data[2:4], 16))[7] == '1': smartview_infos["device_emulation"] = "True" - elif eeprom_element["name"] == "BootStrap": - bootstrap_data = "{:0>16x}".format(eeprom_element) + elif eeprom_element.tag == "BootStrap": + bootstrap_data = "{:0>16x}".format(int(objectify.fromstring(eeprom_element.tostring()).text, 16)) # get bootstrap configuration; -- for cfg, iter in [("mailbox_bootstrapconf_outstart", 0), ("mailbox_bootstrapconf_outlength", 1), @@ -484,12 +863,34 @@ ("mailbox_bootstrapconf_inlength", 3)]: smartview_infos[cfg] = str(int(bootstrap_data[4*iter+2:4*(iter+1)]+bootstrap_data[4*iter:4*iter+2], 16)) + """ + for eeprom_element in device.getEeprom().getcontent()["value"]: + # get EEPROM size; -- + if eeprom_element["name"] == "ByteSize": + smartview_infos["eeprom_size"] = eeprom_element["value"] + + elif eeprom_element["name"] == "ConfigData": + configData_data = self.DecimalToHex(eeprom_element["value"]) + # get PDI type; -- address 0x00 + smartview_infos["pdi_type"] = int(configData_data[0:2], 16) + # get state of device emulation; -- address 0x01 + if "{:0>8b}".format(int(configData_data[2:4], 16))[7] == '1': + smartview_infos["device_emulation"] = "True" + + elif eeprom_element["name"] == "BootStrap": + bootstrap_data = "{:0>16x}".format(eeprom_element["value"]) + # get bootstrap configuration; -- + for cfg, iter in [("mailbox_bootstrapconf_outstart", 0), + ("mailbox_bootstrapconf_outlength", 1), + ("mailbox_bootstrapconf_instart", 2), + ("mailbox_bootstrapconf_inlength", 3)]: + 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; - - 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 + for mailbox_protocol in ["VoE", "SoE", "FoE", "CoE", "EoE", "AoE"]: + if device.getMailbox() is not None and eval("device.getMailbox().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; - @@ -504,24 +905,24 @@ pass # get device identity from - - # 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)) @@ -537,14 +938,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 @@ -668,12 +1069,13 @@ 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: # get ConfigData for EEPROM offset 0x0000-0x000d; -- - for eeprom_element in device.getEeprom().getcontent(): - if eeprom_element["name"] == "ConfigData": - data = self.DecimalToHex(eeprom_element) + # Modify by jblee because of update IDE module (minidom -> lxml) + for eeprom_element in device.getEeprom().getchildren(): + if eeprom_element.tag == "ConfigData": + data = self.DecimalToHex(objectify.fromstring(eeprom_element.tostring()).text) eeprom += self.GenerateEEPROMList(data, 0, 28) # calculate CRC for EEPROM offset 0x000e-0x000f @@ -732,19 +1134,20 @@ eeprom.append("00") eeprom.append("00") + data = "" # get BootStrap for EEPROM offset 0x0028-0x002e; -- - data = "" - for eeprom_element in device.getEeprom().getcontent(): - if eeprom_element["name"] == "BootStrap": - data = "{:0>16x}".format(eeprom_element) + # Modify by jblee because of update IDE module (minidom -> lxml) + for eeprom_element in device.getEeprom().getchildren(): + if eeprom_element.tag == "BootStrap": + data = "{:0>16x}".format(int(objectify.fromstring(eeprom_element.tostring()).text, 16)) eeprom += self.GenerateEEPROMList(data, 0, 16) # get Standard Mailbox for EEPROM offset 0x0030-0x0037; - data = "" + standard_receive_mailbox_offset = None + standard_receive_mailbox_size = None standard_send_mailbox_offset = None standard_send_mailbox_size = None - standard_receive_mailbox_offset = None - standard_receive_mailbox_size = None for sm_element in device.getSm(): if sm_element.getcontent() == "MBoxOut": standard_receive_mailbox_offset = "{:0>4x}".format(ExtractHexDecValue(sm_element.getStartAddress())) @@ -780,10 +1183,14 @@ # 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: + if device.getMailbox() is not None: + for mbox, bit in [(device.getMailbox().getAoE(), 0), + (device.getMailbox().getEoE(), 1), + (device.getMailbox().getCoE(), 2), + (device.getMailbox().getFoE(), 3), + (device.getMailbox().getSoE(), 4), + (device.getMailbox().getVoE(), 5)]: + if mbox is not None: data += 1< 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") eeprom.append("00") @@ -808,7 +1216,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") @@ -877,14 +1285,16 @@ imageflag = False # vendor specific data - # element1; ---- - # vendor_specific_data : vendor specific data (binary type) + # element1; ---- + # vendor_specific_data : vendor specific data (binary type) + # Modify by jblee because of update IDE module (minidom -> lxml) vendor_specific_data = "" - # vendor_spec_strings : list of vendor specific "strings" for preventing duplicated strings + # vendor_spec_strings : list of vendor specific "strings" for preventing duplicated strings vendor_spec_strings = [] for element in device.getType().getcontent(): data += element - if data is not "" and type(data) == unicode: + # retrun type change unicode -> str + if data is not "" and type(data) == str: for vendor_spec_string in vendor_spec_strings: if data == vendor_spec_string: self.OrderIdx = vendor_spec_strings.index(data)+1 @@ -901,9 +1311,11 @@ vendor_specific_data += "{:0>2x}".format(ord(data[character])) data = "" - # element2-1; ---- + # element2-1; ---- + # Modify by jblee because of update IDE module (minidom -> lxml) data = device.getGroupType() - if data is not None and type(data) == unicode: + # retrun type change unicode -> str + if data is not None and type(data) == str: for vendor_spec_string in vendor_spec_strings: if data == vendor_spec_string: self.GroupIdx = vendor_spec_strings.index(data)+1 @@ -920,7 +1332,7 @@ for character in range(len(data)): vendor_specific_data += "{:0>2x}".format(ord(data[character])) - # element2-2; --- + # element2-2; --- if grouptypeflag is False: if self.Controler.CTNParent.CTNParent.ModulesLibrary.Library is not None: for vendor_id, vendor in self.Controler.CTNParent.CTNParent.ModulesLibrary.Library.iteritems(): @@ -945,8 +1357,30 @@ for character in range(len(data)): vendor_specific_data += "{:0>2x}".format(ord(data[character])) data = "" - - # element3; ---- + + # element3; ---- + # Modify by jblee because of update IDE module (minidom -> lxml) + if self.Controler.CTNParent.CTNParent.ModulesLibrary.Library is not None: + LcId_obj = self.Controler.CTNParent.CTNParent.ModulesLibrary.LcId_data + data = LcId_obj.getcontent() + + # retrun type change unicode -> str + if data is not "" and type(data) == str: + for vendor_spec_string in vendor_spec_strings: + if data == vendor_spec_string: + groupnameflag = True + break + if groupnameflag is False: + count += 1 + self.Strings.append(data) + vendor_spec_strings.append(data) + groupnameflag = True + vendor_specific_data += "{:0>2x}".format(len(data)) + for character in range(len(data)): + vendor_specific_data += "{:0>2x}".format(ord(data[character])) + + """ + # element3; ---- if self.Controler.CTNParent.CTNParent.ModulesLibrary.Library is not None: for vendorId, vendor in self.Controler.CTNParent.CTNParent.ModulesLibrary.Library.iteritems(): for group_type, group_etc in vendor["groups"].iteritems(): @@ -966,13 +1400,17 @@ 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; ---- + # element4; ---- + # Modify by jblee because of update IDE module (minidom -> lxml) for element in device.getName(): if element.getLcId() == 1 or element.getLcId()==1033: data = element.getcontent() - if data is not "" and type(data) == unicode: + # retrun type change unicode -> str + if data is not "" and type(data) == str: for vendor_spec_string in vendor_spec_strings: if data == vendor_spec_string: self.NameIdx = vendor_spec_strings.index(data)+1 @@ -987,12 +1425,18 @@ vendor_specific_data += "{:0>2x}".format(len(data)) for character in range(len(data)): vendor_specific_data += "{:0>2x}".format(ord(data[character])) + data = "" - # element5-1; ---- + # element5-1; ---- if device.getcontent() is not None: - data = device.getcontent() - if data is not None and type(data) == unicode: + #data = device.getcontent()["value"] + # mod by jblee 151224 + # xml module change minidom -> lxml + # use lxml objectify module + data = objectify.fromstring(device.getcontent().tostring()).text + # retrun type change unicode -> str + if data is not None and type(data) == str: for vendor_spec_string in vendor_spec_strings: if data == vendor_spec_string: self.ImgIdx = vendor_spec_strings.index(data)+1 @@ -1007,16 +1451,43 @@ vendor_specific_data += "{:0>2x}".format(len(data)) for character in range(len(data)): vendor_specific_data += "{:0>2x}".format(ord(data[character])) - - # element5-2; ---- + + # element5-2; ---- + # Modify by jblee because of update IDE module (minidom -> lxml) + if imageflag is False: + if self.Controler.CTNParent.CTNParent.ModulesLibrary.Library is not None: + data_obj = self.Controler.CTNParent.CTNParent.ModulesLibrary.Image16x14_data + data = data_obj.text + + # retrun type change unicode -> str + if data is not None and type(data) == str: + for vendor_spec_string in vendor_spec_strings: + if data == vendor_spec_string: + self.ImgIdx = vendor_spec_strings.index(data)+1 + imageflag = True + break + if imageflag is False: + count += 1 + self.Strings.append(data) + vendor_spec_strings.append(data) + imageflag = True + self.ImgIdx = count + vendor_specific_data += "{:0>2x}".format(len(data)) + for character in range(len(data)): + vendor_specific_data += "{:0>2x}".format(ord(data[character])) + + """ + # element5-2; ---- if imageflag is False: if self.Controler.CTNParent.CTNParent.ModulesLibrary.Library is not None: for vendor_id, vendor in self.Controler.CTNParent.CTNParent.ModulesLibrary.Library.iteritems(): for group_type, group_etc in vendor["groups"].iteritems(): for device_item in group_etc["devices"]: if device == device_item[1]: - data = group_etc - if data is not None and type(data) == unicode: + print group_etc + data = group_etc["value"] + # retrun type change unicode -> str + if data is not None and type(data) == str: for vendor_spec_string in vendor_spec_strings: if data == vendor_spec_string: self.ImgIdx = vendor_spec_strings.index(data)+1 @@ -1031,10 +1502,11 @@ vendor_specific_data += "{:0>2x}".format(len(data)) for character in range(len(data)): vendor_specific_data += "{:0>2x}".format(ord(data[character])) + """ + data = "" - # DC related elements - # ------ + # ------ dc_related_elements = "" if device.getDc() is not None: for element in device.getDc().getOpMode(): @@ -1048,7 +1520,7 @@ data = "" # Input elements(TxPDO) - # ----; Name + # ----; Name input_elements = "" inputs = [] for element in device.getTxPdo(): @@ -1081,7 +1553,7 @@ data = "" # Output elements(RxPDO) - # ----; Name + # ----; Name output_elements = "" outputs = [] for element in device.getRxPdo(): @@ -1114,10 +1586,10 @@ data = "" # form eeprom data - # category header + # category header eeprom.append("0a") eeprom.append("00") - # category length (word); 1 word is 4 bytes. "+2" is the length of string's total number + # category length (word); 1 word is 4 bytes. "+2" is the length of string's total number length = len(vendor_specific_data + dc_related_elements + input_elements + output_elements) + 2 if length%4 == 0: pass @@ -1126,7 +1598,7 @@ padflag = True eeprom.append("{:0>4x}".format(length/4)[2:4]) eeprom.append("{:0>4x}".format(length/4)[0:2]) - # total numbers of strings + # total numbers of strings eeprom.append("{:0>2x}".format(count)) for element in [vendor_specific_data, dc_related_elements, @@ -1171,48 +1643,48 @@ # word 3 : Physical Layer Port info. and CoE Details eeprom.append("01") # Physical Layer Port info - assume 01 - # CoE Details; ----- + # CoE Details; ----- 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<2x}".format(coe_details)) # word 4 : FoE Details and EoE Details - # FoE Details; ----- - if mb is not None and mb.getFoE() is not None: + # FoE Details; ----- + if device.getMailbox() is not None and device.getMailbox().getFoE() is not None: eeprom.append("01") else: eeprom.append("00") - # EoE Details; ----- - if mb is not None and mb.getEoE() is not None: + # EoE Details; ----- + if device.getMailbox() is not None and device.getMailbox().getEoE() is not None: eeprom.append("01") else: eeprom.append("00") # word 5 : SoE Channels(reserved) and DS402 Channels - # SoE Details; ----- - if mb is not None and mb.getSoE() is not None: + # SoE Details; ----- + if device.getMailbox() is not None and device.getMailbox().getSoE() is not None: eeprom.append("01") else: eeprom.append("00") - # DS402Channels; -----: 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") + # DS402Channels; -----: DS402Channels + if device.getMailbox() is not None and \ + (device.getMailbox().getCoE().getDS402Channels() == True \ + or device.getMailbox().getCoE().getDS402Channels() == 1): + eeprom.append("01") + else: + eeprom.append("00") # word 6 : SysmanClass(reserved) and Flags eeprom.append("00") # reserved - # Flags + # Flags en_safeop = False en_lrw = False if device.getType().getTcCfgModeSafeOp() == True \ @@ -1272,10 +1744,10 @@ # construct of EEPROM data if data is not "": - # category header + # category header eeprom.append("28") eeprom.append("00") - # category length + # category length if count%2 == 1: padflag = True eeprom.append("{:0>4x}".format((count+1)/2)[2:4]) @@ -1289,7 +1761,7 @@ else: eeprom.append(data[0:2]) data = data[2:len(data)] - # padding if length is odd bytes + # padding if length is odd bytes if padflag is True: eeprom.append("ff") @@ -1321,10 +1793,10 @@ data += number[sm.getcontent()] if data is not "": - # category header + # category header eeprom.append("29") eeprom.append("00") - # category length + # category length eeprom.append("{:0>4x}".format(len(data)/4)[2:4]) eeprom.append("{:0>4x}".format(len(data)/4)[0:2]) for i in range(len(data)/2): @@ -1351,19 +1823,19 @@ en_virtual = False for element in eval("device.get%s()"%pdotype): - # PDO Index + # PDO Index data += "{:0>4x}".format(ExtractHexDecValue(element.getIndex().getcontent()))[2:4] data += "{:0>4x}".format(ExtractHexDecValue(element.getIndex().getcontent()))[0:2] - # Number of Entries + # Number of Entries data += "{:0>2x}".format(len(element.getEntry())) - # About Sync Manager + # About Sync Manager if element.getSm() is not None: data += "{:0>2x}".format(element.getSm()) else: data += "ff" - # Reference to DC Synch (according to ET1100 documentation) - assume 0 + # Reference to DC Synch (according to ET1100 documentation) - assume 0 data += "00" - # Name Index + # Name Index objname = "" for name in element.getName(): objname = name.getcontent() @@ -1376,7 +1848,7 @@ else: data += "{:0>2x}".format(count) count = 0 - # Flags; by Fixed, Mandatory, Virtual attributes ? + # Flags; by Fixed, Mandatory, Virtual attributes ? if element.getFixed() == True or 1: en_fixed = True if element.getMandatory() == True or 1: @@ -1386,12 +1858,12 @@ data += str(int(en_fixed)) + str(int(en_mandatory)) + str(int(en_virtual)) + "0" for entry in element.getEntry(): - # Entry Index + # Entry Index data += "{:0>4x}".format(ExtractHexDecValue(entry.getIndex().getcontent()))[2:4] data += "{:0>4x}".format(ExtractHexDecValue(entry.getIndex().getcontent()))[0:2] - # Subindex + # Subindex data += "{:0>2x}".format(int(entry.getSubIndex())) - # Entry Name Index + # Entry Name Index objname = "" for name in entry.getName(): objname = name.getcontent() @@ -1404,7 +1876,7 @@ else: data += "{:0>2x}".format(count) count = 0 - # DataType + # DataType if entry.getDataType() is not None: if entry.getDataType().getcontent() in self.BaseDataTypeDict: data += self.BaseDataTypeDict[entry.getDataType().getcontent()] @@ -1412,19 +1884,19 @@ data += "00" else: data += "00" - # BitLen + # BitLen if entry.getBitLen() is not None: data += "{:0>2x}".format(int(entry.getBitLen())) else: data += "00" - # Flags; by Fixed attributes ? + # Flags; by Fixed attributes ? en_fixed = False if entry.getFixed() == True or entry.getFixed() == 1: en_fixed = True data += str(int(en_fixed)) + "000" if data is not "": - # category header + # category header if pdotype == "TxPdo": eeprom.append("32") elif pdotype == "RxPdo": @@ -1432,7 +1904,7 @@ else: eeprom.append("00") eeprom.append("00") - # category length + # category length eeprom.append("{:0>4x}".format(len(data)/4)[2:4]) eeprom.append("{:0>4x}".format(len(data)/4)[0:2]) data = str(data.lower()) @@ -1459,7 +1931,7 @@ if device.getDc() is not None: for element in device.getDc().getOpMode(): count += 1 - # assume that word 1-7 are 0x0000 + # assume that word 1-7 are 0x0000 data += "0000" data += "0000" data += "0000" @@ -1467,14 +1939,14 @@ data += "0000" data += "0000" data += "0000" - # word 8-10 - # AssignActivate + # word 8-10 + # AssignActivate if element.getAssignActivate() is not None: data += "{:0>4x}".format(ExtractHexDecValue(element.getAssignActivate()))[2:4] data += "{:0>4x}".format(ExtractHexDecValue(element.getAssignActivate()))[0:2] else: data += "0000" - # Factor of CycleTimeSync0 ? and default is 1? + # Factor of CycleTimeSync0 ? and default is 1? if element.getCycleTimeSync0() is not None: if element.getCycleTimeSync0().getFactor() is not None: data += "{:0>2x}".format(int(element.getCycleTimeSync0().getFactor())) @@ -1483,8 +1955,8 @@ data += "0100" else: data += "0100" - # Index of Name in STRINGS Category - # Name Index + # Index of Name in STRINGS Category + # Name Index objname = "" for name in element.getName(): objname += name @@ -1498,15 +1970,15 @@ data += "{:0>2x}".format(namecount) namecount = 0 data += "00" - # assume that word 11-12 are 0x0000 + # assume that word 11-12 are 0x0000 data += "0000" data += "0000" if data is not "": - # category header + # category header eeprom.append("3c") eeprom.append("00") - # category length + # category length eeprom.append("{:0>4x}".format(len(data)/4)[2:4]) eeprom.append("{:0>4x}".format(len(data)/4)[0:2]) data = str(data.lower()) @@ -1533,6 +2005,24 @@ error, return_val = self.Controler.RemoteExec(REG_READ%(self.Controler.GetSlavePos(), offset, length), 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. @@ -1550,6 +2040,40 @@ Command example : "ethercat rescan -p 0" """ 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 @@ -1559,7 +2083,7 @@ Check connection status (1) between Beremiz and the master (2) between the master and the slave. @param cyclic_flag: 0 - one shot, 1 - periodic @return True or False - """ + """ if self.Controler.GetCTRoot()._connector is not None: # Check connection between the master and the slave. # Command example : "ethercat xml -p 0" diff -r 09d5d1456616 -r c9deff128c37 etherlab/ConfigEditor.py --- a/etherlab/ConfigEditor.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/ConfigEditor.py Wed Nov 20 16:57:15 2019 +0100 @@ -25,9 +25,9 @@ from controls.CustomStyledTextCtrl import NAVIGATION_KEYS # ----------------------------------------------------------------------- -from EtherCATManagementEditor import EtherCATManagementTreebook, MasterStatePanelClass -# ----------------------------------------------------------------------- - +from EtherCATManagementEditor import EtherCATManagementTreebook, MasterStatePanelClass +# ----------------------------------------------------------------------- + [ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE] = range(3) def AppendMenu(parent, help, id, kind, text): @@ -141,6 +141,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) @@ -284,7 +287,7 @@ # add Contoler for use EthercatSlave.py Method self.Controler = controler - + def GetBufferState(self): return False, False @@ -299,17 +302,35 @@ style=wx.TAB_TRAVERSAL|wx.HSCROLL|wx.VSCROLL) self.EtherCATManagementEditor.Bind(wx.EVT_SIZE, self.OnResize) + #self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged) + self.EtherCATManagermentEditor_Main_Sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) self.EtherCATManagermentEditor_Main_Sizer.AddGrowableCol(0) self.EtherCATManagermentEditor_Main_Sizer.AddGrowableRow(0) self.EtherCATManagementTreebook = EtherCATManagementTreebook(self.EtherCATManagementEditor, self.Controler, self) - + #except: + #try: + # self.EtherCATManagementTreebook = EtherCATManagementTreebook(self.EtherCATManagementEditor, self.Controler, self) + #except: + # self.EtherCATManagementTreebook = wx.Treebook(self.EtherCATManagementEditor, -1) + # self.Controler.CommonMethod.CreateErrorDialog(\ + # "failed to open EtherCAT Management.\nplease remove the slave and try it again.") + self.EtherCATManagermentEditor_Main_Sizer.AddSizer(self.EtherCATManagementTreebook, border=10, flag=wx.GROW) self.EtherCATManagementEditor.SetSizer(self.EtherCATManagermentEditor_Main_Sizer) return self.EtherCATManagementEditor + """ + def OnPageChanged(self, event): + try: + print "OnPageChanged" + self.EtherCATManagementTreebook.SDOPanel.SDOMonitorThread.Stop(); + except: + pass + """ + def OnResize(self, event): self.EtherCATManagementEditor.GetBestSize() xstart, ystart = self.EtherCATManagementEditor.GetViewStart() @@ -595,20 +616,18 @@ def _create_MasterStateEditor(self, prnt): 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.Bind(wx.EVT_SIZE, self.OnResize2) + + self.MasterStateEditor_Panel_Main_Sizer = wx.BoxSizer(wx.VERTICAL) + self.MasterStateEditor_Panel = MasterStatePanelClass(self.MasterStateEditor, self.Controler) - self.MasterStateEditor_Panel_Main_Sizer.AddSizer(self.MasterStateEditor_Panel, border=10, flag=wx.GROW) + self.MasterStateEditor_Panel_Main_Sizer.AddSizer(self.MasterStateEditor_Panel, border=10, flag=wx.EXPAND) self.MasterStateEditor.SetSizer(self.MasterStateEditor_Panel_Main_Sizer) return self.MasterStateEditor - def OnResize(self, event): + def OnResize2(self, event): self.MasterStateEditor.GetBestSize() xstart, ystart = self.MasterStateEditor.GetViewStart() window_size = self.MasterStateEditor.GetClientSize() @@ -1019,6 +1038,7 @@ if value not in self.Controler.GetSlaves(): message = _("No slave defined at position %d!") % value old_value = self.StartupCommandsTable.GetOldValue() + command = self.StartupCommandsTable.GetRow(row) if message is None and old_value != command["Position"]: self.Controler.RemoveStartupCommand( @@ -1054,33 +1074,6 @@ maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) event.Skip() - #def OnButtonClick(self, event): - # self.MasterState = self.Controler.getMasterState() - # if self.MasterState: - # self.Phase.SetValue(self.MasterState["phase"]) - # self.Active.SetValue(self.MasterState["active"]) - # self.SlaveCount.SetValue(self.MasterState["slave"]) - # self.MacAddress.SetValue(self.MasterState["MAC"]) - # self.LinkState.SetValue(self.MasterState["link"]) - # self.TxFrames.SetValue(self.MasterState["TXframe"]) - # self.RxFrames.SetValue(self.MasterState["RXframe"]) - # self.TxByte.SetValue(self.MasterState["TXbyte"]) - # self.TxError.SetValue(self.MasterState["TXerror"]) - # self.LostFrames.SetValue(self.MasterState["lost"]) - - # self.TxFrameRate1.SetValue(self.MasterState["TXframerate1"]) - # self.TxFrameRate2.SetValue(self.MasterState["TXframerate2"]) - # self.TxFrameRate3.SetValue(self.MasterState["TXframerate3"]) - # self.TxRate1.SetValue(self.MasterState["TXrate1"]) - # self.TxRate2.SetValue(self.MasterState["TXrate2"]) - # self.TxRate3.SetValue(self.MasterState["TXrate3"]) - # self.LossRate1.SetValue(self.MasterState["loss1"]) - # self.LossRate2.SetValue(self.MasterState["loss2"]) - # self.LossRate3.SetValue(self.MasterState["loss3"]) - # self.FrameLoss1.SetValue(self.MasterState["frameloss1"]) - # self.FrameLoss2.SetValue(self.MasterState["frameloss2"]) - # self.FrameLoss3.SetValue(self.MasterState["frameloss3"]) - class LibraryEditorSizer(wx.FlexGridSizer): def __init__(self, parent, module_library, buttons): diff -r 09d5d1456616 -r c9deff128c37 etherlab/EtherCATManagementEditor.py --- a/etherlab/EtherCATManagementEditor.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/EtherCATManagementEditor.py Wed Nov 20 16:57:15 2019 +0100 @@ -21,6 +21,18 @@ # ------------ for SDO Management -------------------- import string import wx.grid as gridlib +try: + from agw import pyprogress as PP +except ImportError: + import wx.lib.agw.pyprogress as PP +try: + from agw import genericmessagedialog as GMD +except ImportError: + import wx.lib.agw.genericmessagedialog as GMD + +from threading import Timer, Thread, Lock +#from time import localtime +import time #------------------------------------------------------------- # ------------ for register management --------------- @@ -60,37 +72,42 @@ self.parent = parent self.Controler = controler self.NodeEditor = node_editor - self.EtherCATManagementClassObject = {} # fill EtherCAT Management Treebook + #for pname, pclass, subs in [ + # ("Slave State", SlaveStatePanelClass, []), + # ("SDO Management", SDOPanelClass, []), + # ("PDO Mapping", PDOPanelClass, []), + # ("ESC Management", EEPROMAccessPanel, [ + # ("Smart View", SlaveSiiSmartView), + # ("Hex View", HexView)]), + # ("Register Access", RegisterAccessPanel, [])]: + # pclass = pclass(self, self.Controler) + # self.AddPage(pclass, pname) + # for spname, spclass in subs: + # spclass = spclass(self, self.Controler) + # self.AddSubPage(spclass, spname) + for pname, pclass, subs in [ ("Slave State", SlaveStatePanelClass, []), ("SDO Management", SDOPanelClass, []), - ("PDO Monitoring", PDOPanelClass, []), - ("ESC Management", EEPROMAccessPanel, [ - ("Smart View", SlaveSiiSmartView), - ("Hex View", HexView)]), - ("Register Access", RegisterAccessPanel, [])]: - self.AddPage(pclass(self, self.Controler), pname) + ("PDO Mapping", PDOPanelClass, [ + ("Rx PDO", RxPDOPanelClass), + ("Tx PDO", TxPDOPanelClass)]), + ("MDP Setting", MDPPanel, []), + ("ESC Management", EEPROMAccessPanel, [ + ("Smart View", SlaveSiiSmartView), + ("Hex View", HexView)]), + ("Register Access", RegisterAccessPanel, []), + ("DC Configuration", DCConfigPanel, []) + ]: + pclass = pclass(self, self.Controler) + self.AddPage(pclass, pname) for spname, spclass in subs: - self.AddSubPage(spclass(self, self.Controler), spname) - - self.Bind(wx.EVT_TREEBOOK_PAGE_CHANGED, self.OnPageChanged) - self.Bind(wx.EVT_TREEBOOK_PAGE_CHANGING, self.OnPageChanging) - - def OnPageChanged(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = event.GetSelection() - event.Skip() - - def OnPageChanging(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = event.GetSelection() - event.Skip() - + spclass = spclass(self, self.Controler) + self.AddSubPage(spclass, spname) + #------------------------------------------------------------------------------- # For SlaveState Panel #------------------------------------------------------------------------------- @@ -184,7 +201,7 @@ self.SetSizer(self.SizerDic["SlaveState_main_sizer"]) - # register a timer for periodic exectuion of slave state update (period: 1000 ms) + # register a timer for periodic exectuion of slave state update (period: 2000 ms) self.Bind(wx.EVT_TIMER, self.GetCurrentState) self.CreateSyncManagerTable() @@ -199,14 +216,14 @@ self.SyncManagersTable = SyncManagersTable(self, [], GetSyncManagersTableColnames()) self.SyncManagersGrid.SetTable(self.SyncManagersTable) # set grid alignment attr. (CENTER) - self.SyncManagersGridColAlignements = [wx.ALIGN_CENTRE, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE, - wx.ALIGN_CENTRE, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE] + self.SyncManagersGridColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_CENTER, wx.ALIGN_CENTER, + wx.ALIGN_CENTER, wx.ALIGN_CENTER, wx.ALIGN_CENTER] # set grid size self.SyncManagersGridColSizes = [40, 150, 100, 100, 100, 100] self.SyncManagersGrid.SetRowLabelSize(0) for col in range(self.SyncManagersTable.GetNumberCols()): attr = wx.grid.GridCellAttr() - attr.SetAlignment(self.SyncManagersGridColAlignements[col], wx.ALIGN_CENTRE) + attr.SetAlignment(self.SyncManagersGridColAlignements[col], wx.ALIGN_CENTER) self.SyncManagersGrid.SetColAttr(col, attr) self.SyncManagersGrid.SetColMinimalWidth(col, self.SyncManagersGridColSizes[col]) self.SyncManagersGrid.AutoSizeColumn(col, False) @@ -236,7 +253,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"] @@ -258,10 +277,15 @@ def GetCurrentState(self, event): """ - Timer event handler for periodic slave state monitoring (Default period: 1 sec = 1000 msec). + Timer event handler for periodic slave state monitoring (Default period: 1 sec = 2000 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") @@ -291,9 +315,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): """ @@ -309,7 +339,7 @@ #------------------------------------------------------------------------------- # For SDO Management Panel #------------------------------------------------------------------------------- -class SDOPanelClass(wx.Panel): +class SDOPanelClass(wx.ScrolledWindow): def __init__(self, parent, controler): """ Constructor @@ -322,141 +352,217 @@ self.ProfileSpecific, self.Reserved, self.AllSDOData = range(6) 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 + 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] - # 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 : - 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 (keep_going == False): - 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 - + 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) @@ -468,14 +574,14 @@ @param parent: Reference to the parent SDOPanelClass class @param controler: _EthercatSlaveCTN class in EthercatSlave.py """ - wx.Notebook.__init__(self, parent, id = -1, size=(850,500)) + wx.Notebook.__init__(self, parent, id = -1, size=(850, 350)) self.Controler = controler self.parent = parent self.CreateNoteBook() - - self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged) - self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging) + + self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged) + self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging) def CreateNoteBook(self): """ @@ -500,48 +606,57 @@ "All SDO Object"] 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() + 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 @param data: SDO data after parsing "SDOParser" method """ wx.grid.Grid.__init__(self, parent, -1, size=(830,490), - style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) + style=wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) self.Controler = parent.Controler self.parent = parent self.SDOFlag = True if data is None : self.SDOs = [] + 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) @@ -549,17 +664,16 @@ 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(gridlib.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) @@ -572,55 +686,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]]) + for col_idx in range(len(self.SDOs[row_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) + self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_CENTER, wx.ALIGN_CENTER) 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): """ @@ -631,7 +735,7 @@ 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" @@ -640,32 +744,100 @@ 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) + 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') - + 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 Monitoring Panel +# For PDO Mapping Panel # PDO Class UI : Panel -> Choicebook (RxPDO, TxPDO) -> # Notebook (PDO Index) -> Grid (PDO entry) #------------------------------------------------------------------------------- @@ -678,16 +850,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): """ @@ -695,43 +1003,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") - - self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged) - self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging) - - def OnPageChanged(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = self.GetSelection() - event.Skip() - - def OnPageChanging(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = self.GetSelection() - event.Skip() - + + # 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) @@ -744,14 +1151,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) @@ -774,22 +1179,6 @@ self.AddPage(win, txt) count += 1 - self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGED, self.OnPageChanged) - self.Bind(wx.EVT_CHOICEBOOK_PAGE_CHANGING, self.OnPageChanging) - - def OnPageChanged(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = self.GetSelection() - event.Skip() - - def OnPageChanging(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = self.GetSelection() - event.Skip() - - #------------------------------------------------------------------------------- # For PDO Grid (fill entry index, subindex etc...) #------------------------------------------------------------------------------- @@ -803,7 +1192,7 @@ @param count : page number """ wx.grid.Grid.__init__(self, parent, -1, size=(500, 400), pos=wx.Point(0,0), - style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) + style=wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) self.Controler = parent.Controler @@ -854,13 +1243,190 @@ else : self.SetCellValue(row_idx, col_idx, str(self.PDOEntry[start_value][pdo_list[col_idx]])) if col_idx != 4 : - self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) + self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_CENTER, wx.ALIGN_CENTER) else : - self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_LEFT, wx.ALIGN_CENTRE) + self.SetCellAlignment(row_idx, col_idx, wx.ALIGN_LEFT, wx.ALIGN_CENTER) self.SetReadOnly(row_idx, col_idx, True) self.SetRowSize(row_idx, 25) start_value += 1 +#------------------------------------------------------------------------------- +# For MDP Main Panel +#------------------------------------------------------------------------------- +class MDPPanel(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.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 @@ -899,7 +1465,9 @@ self.parent = parent self.Controler = controler + # 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'], @@ -907,6 +1475,7 @@ 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'], @@ -944,8 +1513,9 @@ Open binary file (user select) and write the selected binary data to EEPROM @param event : wx.EVT_BUTTON object """ - # Check whether beremiz connected or not, and whether status is "Started" or not. - 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: status, count = self.Controler.GetCTRoot()._connector.GetPLCstatus() if status is not "Started": @@ -975,7 +1545,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() @@ -1002,7 +1573,7 @@ if cnt_pdi_type == i: cnt_pdi_type = self.PDIType[i][0] break - # Set Config Data + # Set Config Data for treelist, data in [("EEPROM Size (Bytes)", str(self.Controler.CommonMethod.SmartViewInfosFromXML["eeprom_size"])), ("PDI Type", @@ -1012,7 +1583,7 @@ self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.ConfigData[treelist], data, 1) # Device Identity: Vendor ID, Product Code, Revision No., Serial No. - # Set Device Identity + # Set Device Identity for treelist, data in [("Vendor ID", self.Controler.CommonMethod.SmartViewInfosFromXML["vendor_id"]), ("Product Code", self.Controler.CommonMethod.SmartViewInfosFromXML["product_code"]), ("Revision No.", self.Controler.CommonMethod.SmartViewInfosFromXML["revision_no"]), @@ -1020,18 +1591,18 @@ self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.DeviceIdentity[treelist], data, 1) # Mailbox: Supported Mailbox, Bootstrap Configuration, Standard Configuration - # Set Mailbox + # Set Mailbox for treelist, data in [("Supported Mailbox", self.Controler.CommonMethod.SmartViewInfosFromXML["supported_mailbox"]), ("Bootstrap Configuration", ""), ("Standard Configuration", "")]: self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.Mailbox[treelist], data, 1) - # Set Bootstrap Configuration: Receive Offset, Receive Size, Send Offset, Send Size + # Set Bootstrap Configuration: Receive Offset, Receive Size, Send Offset, Send Size for treelist, data in [("Receive Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_outstart"]), ("Receive Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_outlength"]), ("Send Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_instart"]), ("Send Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_bootstrapconf_inlength"])]: self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.BootstrapConfig[treelist], data, 1) - # Set Standard Configuration: Receive Offset, Receive Size, Send Offset, Send Size + # Set Standard Configuration: Receive Offset, Receive Size, Send Offset, Send Size for treelist, data in [("Receive Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_outstart"]), ("Receive Size", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_outlength"]), ("Send Offset", self.Controler.CommonMethod.SmartViewInfosFromXML["mailbox_standardconf_instart"]), @@ -1042,7 +1613,6 @@ """ Set data based on slave EEPROM. """ - # sii_dict = { Parameter : (WordAddress, WordSize) } sii_dict= { 'PDIControl' : ( '0', 1), 'PDIConfiguration' : ( '1', 1), 'PulseLengthOfSYNCSignals' : ( '2', 1), @@ -1081,16 +1651,16 @@ if cnt_pdi_type == i: cnt_pdi_type = self.PDIType[i][0] break - # Get Device Emulation + # Get Device Emulation device_emulation = str(bool(int("{:0>16b}".format(int(self.GetWordAddressData( sii_dict.get('PDIControl'),16 ), 16))[7]))) - # Set Config Data + # Set Config Data for treelist, data in [("EEPROM Size (Bytes)", eeprom_size), ("PDI Type", cnt_pdi_type), ("Device Emulation", device_emulation)]: self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.ConfigData[treelist], data, 1) # Device Identity: Vendor ID, Product Code, Revision No., Serial No. - # Set Device Identity + # Set Device Identity for treelist, data in [("Vendor ID", self.GetWordAddressData( sii_dict.get('VendorID'),16 )), ("Product Code", self.GetWordAddressData( sii_dict.get('ProductCode'),16 )), ("Revision No.", self.GetWordAddressData( sii_dict.get('RevisionNumber'),16 )), @@ -1108,18 +1678,18 @@ if mailbox_data[protocol+2] == '1': supported_mailbox += mailbox_protocol[protocol] supported_mailbox = supported_mailbox.strip(", ") - # Set Mailbox + # Set Mailbox for treelist, data in [("Supported Mailbox", supported_mailbox), ("Bootstrap Configuration", ""), ("Standard Configuration", "")]: self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.Mailbox[treelist], data, 1) - # Set Bootstrap Configuration: Receive Offset, Receive Size, Send Offset, Send Size + # Set Bootstrap Configuration: Receive Offset, Receive Size, Send Offset, Send Size for treelist, data in [("Receive Offset", self.GetWordAddressData( sii_dict.get('BootstrapReceiveMailboxOffset'),10 )), ("Receive Size", self.GetWordAddressData( sii_dict.get('BootstrapReceiveMailboxSize'),10 )), ("Send Offset", self.GetWordAddressData( sii_dict.get('BootstrapSendMailboxOffset'),10 )), ("Send Size", self.GetWordAddressData( sii_dict.get('BootstrapSendMailboxSize'),10 ))]: self.TreeListCtrl.Tree.SetItemText(self.TreeListCtrl.BootstrapConfig[treelist], data, 1) - # Set Standard Configuration: Receive Offset, Receive Size, Send Offset, Send Size + # Set Standard Configuration: Receive Offset, Receive Size, Send Offset, Send Size for treelist, data in [("Receive Offset", self.GetWordAddressData( sii_dict.get('StandardReceiveMailboxOffset'),10 )), ("Receive Size", self.GetWordAddressData( sii_dict.get('StandardReceiveMailboxSize'),10 )), ("Send Offset", self.GetWordAddressData( sii_dict.get('StandardSendMailboxOffset'),10 )), @@ -1190,31 +1760,31 @@ self.Root = self.Tree.AddRoot("") # Add item - # Level 1 nodes + # Level 1 nodes self.Level1Nodes = {} for lv1 in ["Config Data", "Device Identity", "Mailbox"]: self.Level1Nodes[lv1] = self.Tree.AppendItem(self.Root, lv1) - # Level 2 nodes - # Config Data + # Level 2 nodes + # Config Data self.ConfigData = {} for lv2 in ["EEPROM Size (Bytes)", "PDI Type", "Device Emulation"]: self.ConfigData[lv2] = self.Tree.AppendItem(self.Level1Nodes["Config Data"], lv2) - # Device Identity + # Device Identity self.DeviceIdentity = {} for lv2 in ["Vendor ID", "Product Code", "Revision No.", "Serial No."]: self.DeviceIdentity[lv2] = self.Tree.AppendItem(self.Level1Nodes["Device Identity"], lv2) - # Mailbox + # Mailbox self.Mailbox = {} for lv2 in ["Supported Mailbox", "Bootstrap Configuration", "Standard Configuration"]: self.Mailbox[lv2] = self.Tree.AppendItem(self.Level1Nodes["Mailbox"], lv2) - # Level 3 nodes - # Children of Bootstrap Configuration + # Level 3 nodes + # Children of Bootstrap Configuration self.BootstrapConfig = {} for lv3 in ["Receive Offset", "Receive Size", "Send Offset", "Send Size"]: self.BootstrapConfig[lv3] = self.Tree.AppendItem(self.Mailbox["Bootstrap Configuration"], lv3) - # Children of Standard Configuration + # Children of Standard Configuration self.StandardConfig = {} for lv3 in ["Receive Offset", "Receive Size", "Send Offset", "Send Size"]: self.StandardConfig[lv3] = self.Tree.AppendItem(self.Mailbox["Standard Configuration"], lv3) @@ -1292,7 +1862,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() @@ -1307,9 +1878,9 @@ Binded to 'Sii Download' button. @param event : wx.EVT_BUTTON object """ - # Check whether beremiz connected or not, - # and whether status is "Started" or not. - 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: status, count = self.Controler.GetCTRoot()._connector.GetPLCstatus() if status is not "Started": @@ -1387,7 +1958,7 @@ self.Col = col wx.grid.Grid.__init__(self, parent, -1, size=(830,450), - style=wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) + style=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) def SetValue(self, value): """ @@ -1415,7 +1986,7 @@ if col == 16: self.SetCellAlignment(row, col, wx.ALIGN_LEFT, wx.ALIGN_CENTER) else: - self.SetCellAlignment(row, col, wx.ALIGN_CENTRE, wx.ALIGN_CENTER) + self.SetCellAlignment(row, col, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self.SetReadOnly(row, col, True) col = col + 1 @@ -1428,10 +1999,10 @@ class RegisterAccessPanel(wx.Panel): def __init__(self, parent, controler): """ - Constructor - @param parent: EEPROMAccessPanel object - @param controler: _EthercatSlaveCTN class in EthercatSlave.py - """ + Constructor + @param parent: EEPROMAccessPanel object + @param controler: _EthercatSlaveCTN class in EthercatSlave.py + """ self.parent = parent self.Controler = controler self.__init_data() @@ -1474,7 +2045,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 @@ -1483,7 +2054,7 @@ for index in range(4): self.PageRange.append([512*index, 512*(index+1)]) - # Previous value of register data for register description configuration + # Previous value of register data for register description configuration self.PreRegSpec = {"ESCType": "", "FMMUNumber": "", "SMNumber": "", @@ -1494,9 +2065,9 @@ Get data from the register. """ self.Controler.CommonMethod.RegData = "" - #ethercat reg_read - #ex : ethercat reg_read -p 0 0x0000 0x0001 - #return value : 0x11 + # ethercat reg_read + # ex : ethercat reg_read -p 0 0x0000 0x0001 + # return value : 0x11 for index in range(4): self.Controler.CommonMethod.RegData = self.Controler.CommonMethod.RegData + " " + self.Controler.CommonMethod.RegRead("0x"+"{:0>4x}".format(index*1024), "0x0400") @@ -1709,7 +2280,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() @@ -1809,21 +2381,6 @@ self.AddPage(self.RegPage[index], "0x"+"{:0>4x}".format(index*1024)+" - 0x"+"{:0>4x}".format((index+1)*1024-1)) - self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged) - self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging) - - def OnPageChanged(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = self.GetSelection() - event.Skip() - - def OnPageChanging(self, event): - old = event.GetOldSelection() - new = event.GetSelection() - sel = self.GetSelection() - event.Skip() - #------------------------------------------------------------------------------- # For Register Access Notebook Panel @@ -1900,20 +2457,19 @@ class RegisterMainTable(wx.grid.Grid): def __init__(self, parent, row, col, controler): """ - Constructor - @param parent: RegisterNotebook object - @param row, col: size of the table - @param controler: _EthercatSlaveCTN class in EthercatSlave.py - """ + Constructor + @param parent: RegisterNotebook object + @param row, col: size of the table + @param controler: _EthercatSlaveCTN class in EthercatSlave.py + """ self.parent = parent self.Data = {} self.Row = row self.Col = col self.Controler = controler self.RegisterAccessPanel = self.parent.parent.parent - wx.grid.Grid.__init__(self, parent, -1, size=(820,300), - style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) + style=wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) for evt, mapping_method in [(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.OnSelectCell), (gridlib.EVT_GRID_CELL_LEFT_CLICK, self.OnSelectCell), @@ -1922,12 +2478,12 @@ def SetValue(self, parent, reg_monitor_data, low_index, high_index): """ - Set the RegMonitorData into the main table. - @param parent: RegisterNotebook object - @param reg_monitor_data: data - @param low_index: the lowest index of the page - @param high_index: the highest index of the page - """ + Set the RegMonitorData into the main table. + @param parent: RegisterNotebook object + @param reg_monitor_data: data + @param low_index: the lowest index of the page + @param high_index: the highest index of the page + """ self.RegMonitorData = reg_monitor_data # set label name and size @@ -1950,16 +2506,16 @@ self.SetRowLabelValue(row, row_index[0]) for data_index in range(4): self.SetCellValue(row, col, row_index[data_index+1]) - self.SetCellAlignment(row, col, wx.ALIGN_CENTRE, wx.ALIGN_CENTER) + self.SetCellAlignment(row, col, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self.SetReadOnly(row, col, True) col = col + 1 row = row + 1 def OnSelectCell(self, event): """ - Handles the event of the cell of the main table. - @param event: gridlib object (left click) - """ + Handles the event of the cell of the main table. + @param event: gridlib object (left click) + """ # if reg_monitor_data is 0, it is initialization of register access. if self.RegMonitorData == 0: event.Skip() @@ -2051,9 +2607,9 @@ class RegisterSubTable(wx.grid.Grid): def __init__(self, parent, row, col): """ - Constructor - @param parent: RegisterNotebook object - @param row, col: size of the table + Constructor + @param parent: RegisterNotebook object + @param row, col: size of the table """ self.parent = parent self.Data = {} @@ -2061,14 +2617,14 @@ self.Col = col wx.grid.Grid.__init__(self, parent, -1, size=(820,150), - style=wx.EXPAND|wx.ALIGN_CENTRE_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) + style=wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL) def SetValue(self, parent, data): """ - Set the data into the subtable. - @param parent: RegisterNotebook object - @param data: data - """ + Set the data into the subtable. + @param parent: RegisterNotebook object + @param data: data + """ # lset label name and size Register_SubTable_Label = [(0, "Bits"), (1, "Name"), (2, "Value"), (3, "Enum")] @@ -2085,7 +2641,7 @@ col = 0 for element in rowData: self.SetCellValue(row, col, element) - self.SetCellAlignment(row, col, wx.ALIGN_CENTRE, wx.ALIGN_CENTER) + self.SetCellAlignment(row, col, wx.ALIGN_CENTER, wx.ALIGN_CENTER) self.SetReadOnly(row, col, True) col = col + 1 row = row + 1 @@ -2099,97 +2655,107 @@ """ Constructor @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) + @Param controler: _EthercatCTN class in EthercatMaster.py + """ + wx.Panel.__init__(self, parent) self.Controler = controler self.parent = parent self.StaticBox = {} self.StaticText = {} self.TextCtrl = {} - # ----------------------- Main Sizer and Update Button -------------------------------------------- + # ---------------------------- Main Sizer and Buttons -------------------------------------------- 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]), + ("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])]: + ("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) - + 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')]: + ("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]) - # ----------------------- Master State ----------------------------------------------------------- for key, label in [ - ('Phase', 'Phase:'), - ('Active', 'Active:'), - ('Slaves', 'Slave Count:')]: + ("Phase", "Phase:"), + ("Active", "Active:"), + ("Slaves", "Slave Count:")]: self.StaticText[key] = wx.StaticText(self, label=_(label)) self.TextCtrl[key] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) - self.MasterStateSizer['innerMasterState'].AddMany([self.StaticText[key], self.TextCtrl[key]]) - - self.MasterStateSizer['masterState'].AddSizer(self.MasterStateSizer['innerMasterState']) + self.MasterStateSizer["innerMasterState"].AddMany([self.StaticText[key], self.TextCtrl[key]]) + + self.MasterStateSizer["masterState"].AddSizer(self.MasterStateSizer["innerMasterState"]) # ----------------------- Ethernet Network Card Information --------------------------------------- for key, label in [ - ('Main', 'MAC Address:'), - ('Link', 'Link State:'), - ('Tx frames', 'Tx Frames:'), - ('Rx frames', 'Rx Frames:'), - ('Lost frames', 'Lost Frames:')]: + ("Main", "MAC Address:"), + ("Link", "Link State:"), + ("Tx frames", "Tx Frames:"), + ("Rx frames", "Rx Frames:"), + ("Lost frames", "Lost Frames:")]: self.StaticText[key] = wx.StaticText(self, label=_(label)) self.TextCtrl[key] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) - self.MasterStateSizer['innerDeviceInfo'].AddMany([self.StaticText[key], self.TextCtrl[key]]) - - self.MasterStateSizer['deviceInfo'].AddSizer(self.MasterStateSizer['innerDeviceInfo']) + self.MasterStateSizer["innerDeviceInfo"].AddMany([self.StaticText[key], self.TextCtrl[key]]) + + self.MasterStateSizer["deviceInfo"].AddSizer(self.MasterStateSizer["innerDeviceInfo"]) # ----------------------- 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.MasterStateSizer["innerFrameInfo"].Add(self.StaticText[key]) self.TextCtrl[key] = {} - for index in ['0', '1', '2']: + for index in ["0", "1", "2"]: self.TextCtrl[key][index] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) - self.MasterStateSizer['innerFrameInfo'].Add(self.TextCtrl[key][index]) - - self.MasterStateSizer['frameInfo'].AddSizer(self.MasterStateSizer['innerFrameInfo']) - + self.MasterStateSizer["innerFrameInfo"].Add(self.TextCtrl[key][index]) + + 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): - """ - Handle the event of the 'Update' button. + self.SetSizer(self.MasterStateSizer["main"]) + + def OnMSUpdateButtonClick(self, event): + """ + Handle the event of the "Update" button. Update the data of the master state. @param event: wx.EVT_BUTTON object """ @@ -2203,5 +2769,821 @@ self.TextCtrl[key][index].SetValue(self.MasterState[key][int(index)]) else: 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() + + + + + + + + + + diff -r 09d5d1456616 -r c9deff128c37 etherlab/EthercatCFileGenerator.py --- a/etherlab/EthercatCFileGenerator.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/EthercatCFileGenerator.py Wed Nov 20 16:57:15 2019 +0100 @@ -68,6 +68,38 @@ } """ +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) if entry_infos["data_type"] is None: @@ -167,7 +199,6 @@ raise ValueError, _("Definition conflict for location \"%s\"") % name def GenerateCFile(self, filepath, location_str, master_number): - # Extract etherlab master code template plc_etherlab_filepath = os.path.join(os.path.split(__file__)[0], "plc_etherlab.c") plc_etherlab_file = open(plc_etherlab_filepath, 'r') @@ -184,10 +215,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 @@ -199,6 +235,9 @@ self.Slaves.sort() # 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: @@ -221,6 +260,7 @@ # Extract slave device object dictionary entries device_entries = device.GetEntriesList() + #device_entries = self.Controler.CTNParent.GetEntriesList() # Adding code for declaring slave in master code template strings for element in ["vendor", "product_code", "revision_number"]: @@ -230,15 +270,17 @@ # Extract slave device CoE informations device_coe = device.getCoE() if device_coe is not None: - # 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"] @@ -264,11 +306,11 @@ PdoAssign = PdoConfig = False # Test if slave has a configuration or need one - if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 and PdoConfig and PdoAssign: - + #if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 and PdoConfig and PdoAssign: + if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 or device.getSlots() is not None: str_completion["slaves_declaration"] += "static ec_slave_config_t *slave%(slave)d = NULL;\n" % type_infos str_completion["slaves_configuration"] += SLAVE_CONFIGURATION_TEMPLATE % type_infos - + # Initializing pdos_infos = { "pdos_entries_infos": [], @@ -304,28 +346,122 @@ 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) : + # checklines[1].split() : [] + # 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] for excluded in excluded_list: exclusion_list.append(ExtractHexDecValue(excluded.getcontent())) - exclusion_list.sort() - - exclusion_scope = exclusive_pdos.setdefault(tuple(exclusion_list), []) - + exclusion_list.sort() + 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: @@ -352,30 +488,57 @@ excluded_pdos.extend([pdo["index"] 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: + continue 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"]: @@ -399,7 +562,7 @@ raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"] ConfigureVariable(entry_infos, str_completion) - + elif pdo_type == "Outputs" and entry.getDataType() is not None and device_coe is not None: data_type = entry.getDataType().getcontent() entry_infos["dir"] = "Q" @@ -409,16 +572,34 @@ entry_infos["real_var"] = "slave%(slave)d_%(index).4x_%(subindex).2x_default" % entry_infos 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()) if excluded_index not in excluded_pdos: excluded_pdos.append(excluded_index) + ############################################################ sm = pdo.getSm() if sm is None: for sm_idx, sync_manager in enumerate(sync_managers): @@ -426,7 +607,7 @@ sm = sm_idx if sm is None: raise ValueError, _("No sync manager available for %s pdo!") % pdo_type - + sync_managers[sm]["pdos_number"] += 1 sync_managers[sm]["pdos"].append( {"slave": slave_idx, @@ -436,6 +617,10 @@ "entries": entries_infos, "entries_number": len(entries_infos), "fixed": pdo.getFixed() == True}) + ############################################################# + + # for MDP + slotNumber += 1 if PdoConfig and PdoAssign: dynamic_pdos = {} @@ -459,7 +644,7 @@ if entry is None: raise ValueError, _("Unknown entry index 0x%4.4x, subindex 0x%2.2x for device %s") % \ (index, subindex, type_infos["device_type"]) - + entry_infos = { "index": index, "subindex": subindex, @@ -526,8 +711,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"] @@ -550,11 +735,11 @@ 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%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 (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", diff -r 09d5d1456616 -r c9deff128c37 etherlab/EthercatCIA402Slave.py --- a/etherlab/EthercatCIA402Slave.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/EthercatCIA402Slave.py Wed Nov 20 16:57:15 2019 +0100 @@ -13,28 +13,35 @@ import wx -from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY +from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, \ + LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY from MotionLibrary import Headers, AxisXSD from EthercatSlave import _EthercatSlaveCTN, _CommonSlave from ConfigEditor import CIA402NodeEditor -# Definition of node variables that have to be mapped in PDO -# [(name, index, subindex, type, -# direction for master ('I': input, 'Q': output)),...] +#HSAHN 2015.07.26 +#remove NODE_VARIABLES's optional items. +#As for now, optional items will be added dynamicaly. NODE_VARIABLES = [ - ("ControlWord", 0x6040, 0x00, "UINT", "Q"), - ("TargetPosition", 0x607a, 0x00, "DINT", "Q"), - ("TargetVelocity", 0x60ff, 0x00, "DINT", "Q"), - ("TargetTorque", 0x6071, 0x00, "INT", "Q"), - ("ModesOfOperation", 0x6060, 0x00, "SINT", "Q"), - ("StatusWord", 0x6041, 0x00, "UINT", "I"), + ("ControlWord", 0x6040, 0x00, "UINT", "Q"), + ("ModesOfOperation", 0x6060, 0x00, "SINT", "Q"), + ("StatusWord", 0x6041, 0x00, "UINT", "I"), ("ModesOfOperationDisplay", 0x6061, 0x00, "SINT", "I"), - ("ActualPosition", 0x6064, 0x00, "DINT", "I"), - ("ActualVelocity", 0x606c, 0x00, "DINT", "I"), - ("ActualTorque", 0x6077, 0x00, "INT", "I"), ] +#HSAHN 2015.07.26 +#reference variable +ADD_NODE_VARIABLES = ({'name':"TargetPosition" , 'index':0x607a, 'sub-index':0x00, 'type':"DINT", 'direction':"Q"}, + {'name':"TargetVelocity" , 'index':0x60ff, 'sub-index':0x00, 'type':"DINT", 'direction':"Q"}, + {'name':"TargetTorque" , 'index':0x6071, 'sub-index':0x00, 'type':"INT", 'direction':"Q"}, + {'name':"ActualPosition" , 'index':0x6064, 'sub-index':0x00, 'type':"DINT", 'direction':"I"}, + {'name':"ActualVelocity" , 'index':0x606c, 'sub-index':0x00, 'type':"DINT", 'direction':"I"}, + {'name':"ActualTorque" , 'index':0x6077, 'sub-index':0x00, 'type':"INT", 'direction':"I"}) + +DEFAULT_RETRIEVE = " __CIA402Node_%(location)s.axis->%(name)s = *(__CIA402Node_%(location)s.%(name)s);" +DEFAULT_PUBLISH = " *(__CIA402Node_%(location)s.%(name)s) = __CIA402Node_%(location)s.axis->%(name)s;" + # Definition of optional node variables that can be added to PDO mapping. # A checkbox will be displayed for each section in node configuration panel to # enable them @@ -46,6 +53,7 @@ # 'publish', string_template_for_publish_variable (None: not published, # default string template if not defined), # },...] + EXTRA_NODE_VARIABLES = [ ("ErrorCode", [ {"description": ("ErrorCode", 0x603F, 0x00, "UINT", "I"), @@ -56,8 +64,8 @@ "publish": None} ]), ("DigitalOutputs", [ - {"description": ("DigitalOutputs", 0x60FE, 0x00, "UDINT", "Q"), - "retrieve": None} + {"description": ("DigitalOutputs", 0x60FE, 0x01, "UDINT", "I"), + "publish": None} ]), ("TouchProbe", [ {"description": ("TouchProbeFunction", 0x60B8, 0x00, "UINT", "Q"), @@ -77,7 +85,23 @@ "Enable" + name: params for name, params in EXTRA_NODE_VARIABLES} -# List of block to define to interface MCL to fieldbus for specific functions +BLOCK_INPUT_TEMPLATE = " __SET_VAR(%(blockname)s->,%(input_name)s, %(input_value)s);" +BLOCK_OUTPUT_TEMPLATE = " __SET_VAR(data__->,%(output_name)s, __GET_VAR(%(blockname)s->%(output_name)s));" + +BLOCK_FUNCTION_TEMPLATE = """ +extern void ETHERLAB%(ucase_blocktype)s_body__(ETHERLAB%(ucase_blocktype)s* data__); +void __%(blocktype)s_%(location)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 +} +""" + +BLOCK_FUNTION_DEFINITION_TEMPLATE = " __CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location)s;" + FIELDBUS_INTERFACE_GLOBAL_INSTANCES = [ {"blocktype": "GetTorqueLimit", "inputs": [], @@ -89,6 +113,44 @@ "outputs": []}, ] +# add jblee +MODEOFOP_HOMING_METHOD_TEMPLATE = """ + if(*(AxsPub.ModesOfOperation) == 0x06){ + IEC_BOOL homing = AxsPub.axis->HomingOperationStart; + if(power){ + if (homing) + CW |= Homing_OperationStart_Origin; + else + CW &= ~(Homing_OperationStart_Origin); + } + else{ + if (homing) + CW |= Homing_OperationStart_Edit; + else + CW &= ~(EnableOperation); + } + + } +""" + +MODEOFOP_COMPUTATION_MODE_TEMPLATE = """ + switch (AxsPub.axis->AxisMotionMode) { + //ssh_add + case mc_mode_hm: + *(AxsPub.ModesOfOperation) = 0x06; + break; + case mc_mode_cst: + *(AxsPub.ModesOfOperation) = 0x0a; + break; + case mc_mode_csv: + *(AxsPub.ModesOfOperation) = 0x09; + break; + default: + *(AxsPub.ModesOfOperation) = 0x08; + break; + } +""" + #-------------------------------------------------- # Ethercat CIA402 Node #-------------------------------------------------- @@ -131,6 +193,13 @@ # ----------- call ethercat mng. function -------------- self.CommonMethod = _CommonSlave(self) + #HSAHN + # Select PDO Mapping + self.SelectedPDOIndex = [] + self.SelectedRxPDOIndex = [] + self.SelectedTxPDOIndex = [] + #HSAHN END + def GetIconName(self): return "CIA402Slave" @@ -186,147 +255,274 @@ self.StartDragNDrop( ("%%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(lambda x:str(x), current_location)) - slave_pos = self.GetSlavePos() - MCL_headers = Headers - - # Open CIA402 node code template file - plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0], - "plc_cia402node.c") + + plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_cia402node.c") 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]) + 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 - 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"]] - ]) + 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) - return_outputs = "\n".join(["""\ - __SET_VAR(data__->,%(output_name)s,, - __GET_VAR(%(blockname)s->%(output_name)s));""" % locals() - for output_name in ["DONE", "BUSY", "ERROR"] + [ - output["name"].upper() - for output in blocktype_infos["outputs"]] - ]) + 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) - 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 -}""" % locals()) - - fieldbus_interface_definition.append("""\ - AxsPub.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location_str)s;\ -""" % locals()) - - # 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 - for var_exchange_dir, str_list, default_template in [ - ("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;")]: - - 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 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_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;""" % locals()) - - # 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) - var_name = """\ -__%(dir)s%(var_size)s%(location_str)s_%(index)d_%(subindex)d""" % locals() - - extern_located_variables_declaration.append( - "IEC_%(var_type)s *%(var_name)s;" % locals()) - entry_variables.append( - " IEC_%(var_type)s *%(name)s;" % locals()) - init_entry_variables.append( - " AxsPub.%(name)s = %(var_name)s;" % locals()) + param_infos = { + "location": location_str, + "param_name": param["name"], + } + 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(lambda l: "\n".join(l), [ - 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 + diff -r 09d5d1456616 -r c9deff128c37 etherlab/EthercatMaster.py --- a/etherlab/EthercatMaster.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/EthercatMaster.py Wed Nov 20 16:57:15 2019 +0100 @@ -85,9 +85,12 @@ ethelabfile.write(etherlab_ext_code) ethelabfile.close() - runtimefile_path = os.path.join(os.path.split(__file__)[0], "runtime_etherlab.py") - return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "", + try: + return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "", ("runtime_etherlab.py", file(GetLocalPath("runtime_etherlab.py")))) + except: + return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "", + ("runtime_etherlab.pyc", file(GetLocalPath("runtime_etherlab.pyc")))) #-------------------------------------------------- # Ethercat MASTER @@ -221,7 +224,7 @@ ProcessVariablesParser = GenerateParserFromXSDstring(ProcessVariablesXSD) class _EthercatCTN: - + CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")] if HAS_MCL: CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave")) @@ -231,20 +234,21 @@ 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, e: - error = e.message - config_xmlfile.close() + + #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) + # if error is not None: + # self.GetCTRoot().logger.write_error( + # _("Couldn't load %s network configuration file.") % CTNName) if self.Config is None: self.Config = EtherCATConfigParser.CreateElement("EtherCATConfig") @@ -270,14 +274,32 @@ 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) + + ###################################### Test Section ######################################### def GetIconName(self): return "Ethercat" @@ -293,7 +315,10 @@ 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(): + # device.GetProfileNumbers() return type is string + # _EthercatCIA402SlaveCTN.NODE_PROFILE change to string + # 151224 jblee + if HAS_MCL and str(_EthercatCIA402SlaveCTN.NODE_PROFILE) in device.GetProfileNumbers(): ConfNodeType = "EthercatCIA402Slave" else: ConfNodeType = "EthercatSlave" @@ -563,6 +588,7 @@ return infos return None + """ def GetSlaveVariables(self, slave_pos=None, limits=None, device=None): if device is None and slave_pos is not None: slave = self.GetSlave(slave_pos) @@ -571,7 +597,6 @@ device, module_extra_params = self.GetModuleInfos(type_infos) if device is not None: entries = device.GetEntriesList(limits) - #print entries entries_list = entries.items() entries_list.sort() entries = [] @@ -592,6 +617,128 @@ entries.append(entry) return entries return [] + #""" + + 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) : + # checklines[1].split() : [] + # 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 = [] + current_index = None + current_entry = None + for (index, subindex), entry in entries_list: + entry["children"] = [] + if slave_pos is not None: + entry["Position"] = str(slave_pos) + entry + if index != current_index: + current_index = index + current_entry = entry + entries.append(entry) + elif current_entry is not None: + current_entry["children"].append(entry) + else: + entries.append(entry) + + 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 [] def GetSlaveVariableDataType(self, slave_pos, index, subindex): slave = self.GetSlave(slave_pos) @@ -599,6 +746,7 @@ device, module_extra_params = self.GetModuleInfos(slave.getType()) if device is not None: entries = device.GetEntriesList() + #entries = self.CTNParent.GetEntriesList() entry_infos = entries.get((index, subindex)) if entry_infos is not None: return entry_infos["Type"] @@ -621,6 +769,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) @@ -630,7 +782,54 @@ def GetDeviceLocationTree(self, slave_pos, current_location, device_name): slave = self.GetSlave(slave_pos) - vars = [] + 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) : + # checklines[1].split() : [] + # 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() @@ -645,8 +844,11 @@ else: sync_managers.append(LOCATION_VAR_INPUT) + # Test OD entries = device.GetEntriesList().items() + #entries = self.CTNParent.GetEntriesList().items() entries.sort() + for (index, subindex), entry in entries: var_size = self.GetSizeOfType(entry["Type"]) if var_size is not None: @@ -666,7 +868,40 @@ (index, subindex)))), "description": "", "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): @@ -674,7 +909,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, diff -r 09d5d1456616 -r c9deff128c37 etherlab/EthercatSlave.py --- a/etherlab/EthercatSlave.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/EthercatSlave.py Wed Nov 20 16:57:15 2019 +0100 @@ -18,6 +18,7 @@ #------------------------------------------ from CommonEtherCATFunction import _CommonSlave +from dialogs import BrowseValuesLibraryDialog #------------------------------------------ @@ -70,6 +71,8 @@ def __init__(self): # ----------- call ethercat mng. function -------------- self.CommonMethod = _CommonSlave(self) + self.SelectedRxPDOIndex = [] + self.SelectedTxPDOIndex = [] def GetIconName(self): return "Slave" @@ -119,7 +122,7 @@ def SetParamsAttribute(self, path, value): self.GetSlaveInfos() position = self.BaseParams.getIEC_Channel() - + if path == "SlaveParams.Type": self.CTNParent.SetSlaveType(position, value) slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos()) diff -r 09d5d1456616 -r c9deff128c37 etherlab/entries_list.xslt --- a/etherlab/entries_list.xslt Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/entries_list.xslt Wed Nov 20 16:57:15 2019 +0100 @@ -1,1 +1,1 @@ -0 - rowoTR \ No newline at end of file +00 - 1rowoTR \ No newline at end of file diff -r 09d5d1456616 -r c9deff128c37 etherlab/entries_list.ysl2 --- a/etherlab/entries_list.ysl2 Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/entries_list.ysl2 Wed Nov 20 16:57:15 2019 +0100 @@ -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)» } } } diff -r 09d5d1456616 -r c9deff128c37 etherlab/etherlab.py --- a/etherlab/etherlab.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/etherlab.py Wed Nov 20 16:57:15 2019 +0100 @@ -10,6 +10,8 @@ # See COPYING file for copyrights details. import os, shutil + +#from xml.dom import minidom from lxml import etree import wx @@ -33,13 +35,16 @@ EtherCATInfoParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) EtherCATInfo_XPath = lambda xpath: etree.XPath(xpath) +EtherCATBaseParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATBase.xsd")) +EtherCATBase_XPath = lambda xpath1: etree.XPath(xpath1) + def HexDecValue(context, *args): return str(ExtractHexDecValue(args[0][0])) def EntryName(context, *args): return ExtractName(args[0], args[1][0] if len(args) > 1 else None) - + ENTRY_INFOS_KEYS = [ ("Index", lambda x: "#x%4.4X" % int(x), "#x0000"), ("SubIndex", str, "0"), @@ -52,6 +57,19 @@ ("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: def __init__(self, entries): @@ -59,11 +77,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: @@ -79,6 +103,7 @@ cls = EtherCATInfoParser.GetElementClass("DeviceType") if cls: + cls.DataTypes = None profile_numbers_xpath = EtherCATInfo_XPath("Profile/ProfileNo") def GetProfileNumbers(self): @@ -92,25 +117,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(): @@ -127,6 +278,8 @@ return sync_managers setattr(cls, "GetSyncManagers", GetSyncManagers) +cls2 = EtherCATInfoParser.GetElementClass("DeviceType") + def GroupItemCompare(x, y): if x["type"] == y["type"]: if x["type"] == ETHERCAT_GROUP: @@ -143,6 +296,50 @@ 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} + +#cls3 = EtherCATBaseParser.GetElementClass("ModuleType") +#if cls3: +# module_xpath = EtherCATBase_XPath("Descriptions/Modules/Module") +# def test(self): +# print module_xpath + +# setattr(cls, "test", test) + + class ModulesLibrary: MODULES_EXTRA_PARAMS = [ @@ -180,7 +377,7 @@ else: self.Library = None self.LoadModulesExtraParams() - + def GetPath(self): return self.Path @@ -189,8 +386,20 @@ 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: @@ -201,13 +410,13 @@ 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, exc: self.modules_infos, error = None, unicode(exc) xmlfile.close() - + if self.modules_infos is not None: vendor = self.modules_infos.getVendor() @@ -218,13 +427,17 @@ 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, {"name": ExtractName(group.getName(), group_type), "parent": group.getParentGroup(), "order": group.getSortOrder(), - #"value": group.getcontent()["value"], - "devices": []}) + "devices": [], + # add jblee for support Moduler Device Profile (MDP) + "modules": []}) for device in self.devices_xpath(self.modules_infos): device_group = device.getGroupType() @@ -232,14 +445,101 @@ raise ValueError, "Not such group \"%\"" % device_group vendor_category["groups"][device_group]["devices"].append( (device.getType().getcontent(), device)) - + + # ------------------ 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 %s XML file:\n%s") % (filepath, error)) - - return self.Library - + pass + #self.GetCTRoot().logger.write_error( + # _("Couldn't load %s XML file:\n%s") % (filepath, error)) + + #print self.ObjectDictionary + return self.Library ## add jblee + + # 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() @@ -301,10 +601,22 @@ revision_number = ExtractHexDecValue(device_infos.getType().getRevisionNo()) if (product_code == ExtractHexDecValue(module_infos["product_code"]) and revision_number == ExtractHexDecValue(module_infos["revision_number"])): - self.cntdevice = device_infos - self.cntdeviceType = device_type + self.cntdevice = device_infos ## add by hwang 13.05.01. + self.cntdeviceType = device_type ## add by hwang 13.05.01. 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): @@ -387,7 +699,6 @@ CTNChildrenTypes = [("EthercatNode",_EthercatCTN,"Ethercat Master")] EditorType = LibraryEditor - def __init__(self): self.ModulesLibrary = None @@ -424,10 +735,32 @@ 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) diff -r 09d5d1456616 -r c9deff128c37 etherlab/plc_cia402node.c --- a/etherlab/plc_cia402node.c Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/plc_cia402node.c Wed Nov 20 16:57:15 2019 +0100 @@ -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 @@ -79,7 +116,7 @@ axis_s* axis; } __CIA402Node; -#define AxsPub __CIA402Node_%(location_str)s +#define AxsPub __CIA402Node_%(location)s static __CIA402Node AxsPub; @@ -87,25 +124,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; @@ -121,15 +157,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) @@ -156,37 +190,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 diff -r 09d5d1456616 -r c9deff128c37 etherlab/plc_etherlab.c --- a/etherlab/plc_etherlab.c Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/plc_etherlab.c Wed Nov 20 16:57:15 2019 +0100 @@ -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; +} + +/*****************************************************************************/ diff -r 09d5d1456616 -r c9deff128c37 etherlab/pous.xml --- a/etherlab/pous.xml Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/pous.xml Wed Nov 20 16:57:15 2019 +0100 @@ -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; diff -r 09d5d1456616 -r c9deff128c37 etherlab/runtime_etherlab.py --- a/etherlab/runtime_etherlab.py Sat Jun 23 09:17:20 2018 +0200 +++ b/etherlab/runtime_etherlab.py Wed Nov 20 16:57:15 2019 +0100 @@ -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. + import os,subprocess,sys,ctypes from threading import Thread import ctypes,time,re @@ -42,7 +53,7 @@ PLCObject.LogMessage( LogLevelsDict["WARNING"], "%s : %s"%(command,output)) - + def EthercatSDOUpload(pos, index, subindex, var_type): global SDOThread SDOThread = Thread(target=SDOThreadProc, args=["upload", pos, var_type, index, subindex])