diff -r bcf346f558bd -r 02a2b5dee5e3 etherlab/CommonEtherCATFunction.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etherlab/CommonEtherCATFunction.py Sat Jun 23 09:08:13 2018 +0200 @@ -0,0 +1,1591 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +# This file is part of Beremiz +# +# Copyright (C) 2013: Real-Time & Embedded Systems (RTES) Lab. University of Seoul, Korea +# +# See COPYING file for copyrights details. + +import os +import wx + +mailbox_protocols = ["AoE", "EoE", "CoE", "FoE", "SoE", "VoE"] + +def ExtractHexDecValue(value): + """ + convert numerical value in string format into decimal or hex format. + @param value : hex or decimal data + @return integer data + """ + try: + return int(value) + except: + pass + try: + return int(value.replace("#", "0"), 16) + + except: + raise ValueError, "Invalid value for HexDecValue \"%s\"" % value + +def ExtractName(names, default=None): + """ + Extract "name" field from XML entries. + @param names : XML entry + @default : if it fails to extract from the designated XML entry, return the default value ("None"). + @return default or the name extracted + """ + if len(names) == 1: + return names[0].getcontent() + else: + for name in names: + if name.getLcId() == 1033: + return name.getcontent() + return default + +#-------------------------------------------------- +# Remote Exec Etherlab Commands +#-------------------------------------------------- + +# --------------------- for master --------------------------- +MASTER_STATE = """ +import commands +result = commands.getoutput("ethercat master") +returnVal =result +""" + +# --------------------- for slave ---------------------------- +# ethercat state -p (slave position) (state (INIT, PREOP, SAFEOP, OP)) +SLAVE_STATE = """ +import commands +result = commands.getoutput("ethercat state -p %d %s") +returnVal = result +""" + +# ethercat slave +GET_SLAVE = """ +import commands +result = commands.getoutput("ethercat slaves") +returnVal =result +""" + +# ethercat xml -p (slave position) +SLAVE_XML = """ +import commands +result = commands.getoutput("ethercat xml -p %d") +returnVal = result +""" + +# ethercat sdos -p (slave position) +SLAVE_SDO = """ +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 +""" + +# ethercat download -p (slave position) (main index) (sub index) (value) +SDO_DOWNLOAD = """ +import commands +result = commands.getoutput("ethercat download --type %s -p %d %s %s %s") +returnVal =result +""" + +# ethercat sii_read -p (slave position) +SII_READ = """ +import commands +result = commands.getoutput("ethercat sii_read -p %d") +returnVal =result +""" + +# ethercat reg_read -p (slave position) (address) (size) +REG_READ = """ +import commands +result = commands.getoutput("ethercat reg_read -p %d %s %s") +returnVal =result +""" + +# ethercat sii_write -p (slave position) - (contents) +SII_WRITE = """ +import subprocess +process = subprocess.Popen( + ["ethercat", "-f", "sii_write", "-p", "%d", "-"], + stdin=subprocess.PIPE) +process.communicate(sii_data) +returnVal = process.returncode +""" + +# ethercat reg_write -p (slave position) -t (uinit16) (address) (data) +REG_WRITE = """ +import commands +result = commands.getoutput("ethercat reg_write -p %d -t uint16 %s %s") +returnVal =result +""" + +# ethercat rescan -p (slave position) +RESCAN = """ +import commands +result = commands.getoutput("ethercat rescan -p %d") +returnVal =result +""" + +#-------------------------------------------------- +# Common Method For EtherCAT Management +#-------------------------------------------------- +class _CommonSlave: + + # ----- Data Structure for ethercat management ---- + SlaveState = "" + + # category of SDO data + DatatypeDescription, CommunicationObject, ManufacturerSpecific, \ + ProfileSpecific, Reserved, AllSDOData = range(6) + + # store the execution result of "ethercat sdos" command into SaveSDOData. + SaveSDOData = [] + + # Flags for checking "write" permission of OD entries + CheckPREOP = False + CheckSAFEOP = False + CheckOP = False + + # Save PDO Data + TxPDOInfo = [] + TxPDOCategory = [] + RxPDOInfo = [] + RxPDOCategory = [] + + # Save EEPROM Data + SiiData = "" + + # Save Register Data + RegData = "" + CrtRegSpec = {"ESCType": "", + "FMMUNumber": "", + "SMNumber": "", + "PDIType": ""} + + def __init__(self, controler): + """ + Constructor + @param controler: _EthercatSlaveCTN class in EthercatSlave.py + """ + self.Controler = controler + + self.ClearSDODataSet() + + #------------------------------------------------------------------------------- + # Used Master State + #------------------------------------------------------------------------------- + def GetMasterState(self): + """ + 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 = {} + # parse the reslut + for each_line in return_val.splitlines(): + if len(each_line) > 0 : + chunks = each_line.strip().split(':', 1) + key = chunks[0] + value = [] + if len(chunks) > 1 : + value = chunks[1].split() + if '(attached)' in value: + value.remove('(attached)') + master_state[key] = value + + return master_state + + #------------------------------------------------------------------------------- + # Used Slave State + #------------------------------------------------------------------------------- + def RequestSlaveState(self, command): + """ + Set slave state to the specified one using "ethercat states -p %d %s" command. + Command example : "ethercat states -p 0 PREOP" (target slave position and target state are given.) + @param command : target slave state + """ + error, return_val = self.Controler.RemoteExec(SLAVE_STATE%(self.Controler.GetSlavePos(), command), return_val = None) + + def GetSlaveStateFromSlave(self): + """ + Get slave information using "ethercat slaves" command and store the information into internal data structure + (self.SlaveState) for "Slave State" + return_val example : 0 0:0 PREOP + EL9800 (V4.30) (PIC24, SPI, ET1100) + """ + error, return_val = self.Controler.RemoteExec(GET_SLAVE, return_val = None) + self.SlaveState = 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. + Command example : "ethercat download --type int32 -p 0 0x8020 0x12 0x00000000" + @param data_type : data type of SDO entry + @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) + + def BackupSDODataSet(self): + """ + Back-up current SDO entry information to restore the SDO data + in case that the user cancels SDO update operation. + """ + self.BackupDatatypeDescription = self.SaveDatatypeDescription + self.BackupCommunicationObject = self.SaveCommunicationObject + self.BackupManufacturerSpecific = self.SaveManufacturerSpecific + self.BackupProfileSpecific = self.SaveProfileSpecific + self.BackupReserved = self.SaveReserved + self.BackupAllSDOData = self.SaveAllSDOData + + def ClearSDODataSet(self): + """ + Clear the specified SDO entry information. + """ + for count in range(6): + self.SaveSDOData.append([]) + + #------------------------------------------------------------------------------- + # Used PDO Monitoring + #------------------------------------------------------------------------------- + def RequestPDOInfo(self): + """ + 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()) + + type_infos = slave.getType() + device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos) + # Initialize PDO data set + self.ClearDataSet() + + # if 'device' object is valid, call SavePDOData() to parse PDO data + if device is not None : + self.SavePDOData(device) + + def SavePDOData(self, device): + """ + Parse PDO data and store the results in TXPDOCategory and RXPDOCategory + Tx(Rx)PDOCategory : index, name, entry number + Tx(Rx)Info : entry index, sub index, name, length, type + @param device : Slave information extracted from ESI XML file + """ + # Parsing TXPDO entries + for pdo, pdo_info in ([(pdo, "Inputs") for pdo in device.getTxPdo()]): + # Save pdo_index, entry, and name of each entry + pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent()) + entries = pdo.getEntry() + pdo_name = ExtractName(pdo.getName()) + + # Initialize entry number count + count = 0 + + # Parse entries + for entry in entries: + # Save index and subindex + index = ExtractHexDecValue(entry.getIndex().getcontent()) + subindex = ExtractHexDecValue(entry.getSubIndex()) + # if entry name exists, save entry data + if ExtractName(entry.getName()) is not None : + entry_infos = { + "entry_index" : index, + "subindex" : subindex, + "name" : ExtractName(entry.getName()), + "bitlen" : entry.getBitLen(), + "type" : entry.getDataType().getcontent() + } + self.TxPDOInfo.append(entry_infos) + count += 1 + + categorys = {"pdo_index" : pdo_index, "name" : pdo_name, "number_of_entry" : count} + self.TxPDOCategory.append(categorys) + + # Parsing RxPDO entries + for pdo, pdo_info in ([(pdo, "Outputs") for pdo in device.getRxPdo()]): + # Save pdo_index, entry, and name of each entry + pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent()) + entries = pdo.getEntry() + pdo_name = ExtractName(pdo.getName()) + + # Initialize entry number count + count = 0 + + # Parse entries + for entry in entries: + # Save index and subindex + index = ExtractHexDecValue(entry.getIndex().getcontent()) + subindex = ExtractHexDecValue(entry.getSubIndex()) + # if entry name exists, save entry data + if ExtractName(entry.getName()) is not None : + entry_infos = { + "entry_index" : index, + "subindex" : subindex, + "name" : ExtractName(entry.getName()), + "bitlen" : str(entry.getBitLen()), + "type" : entry.getDataType().getcontent() + } + self.RxPDOInfo.append(entry_infos) + count += 1 + + categorys = {"pdo_index" : pdo_index, "name" : pdo_name, "number_of_entry" : count} + self.RxPDOCategory.append(categorys) + + def GetTxPDOCategory(self): + """ + Get TxPDOCategory data structure (Meta informaton of TxPDO). + TxPDOCategorys : index, name, number of entries + @return TxPDOCategorys + """ + return self.TxPDOCategory + + def GetRxPDOCategory(self): + """ + Get RxPDOCategory data structure (Meta information of RxPDO). + RxPDOCategorys : index, name, number of entries + @return RxPDOCategorys + """ + return self.RxPDOCategory + + def GetTxPDOInfo(self): + """ + Get TxPDOInfo data structure (Detailed information on TxPDO entries). + TxPDOInfos : entry index, sub index, name, length, type + @return TxPDOInfos + """ + return self.TxPDOInfo + + def GetRxPDOInfo(self): + """ + Get RxPDOInfo data structure (Detailed information on RxPDO entries). + RxPDOInfos : entry index, sub index, name, length, type + @return RxPDOInfos + """ + return self.RxPDOInfo + + def ClearDataSet(self): + """ + Initialize PDO management data structure. + """ + self.TxPDOInfos = [] + self.TxPDOCategorys = [] + self.RxPDOInfos = [] + self.RxPDOCategorys = [] + + #------------------------------------------------------------------------------- + # Used EEPROM Management + #------------------------------------------------------------------------------- + # Base data types in ETG2000; format = {"Name": "BitSize"} + BaseDataTypeDict = {"BOOL": "01", + "SINT": "02", + "INT": "03", + "DINT": "04", + "USINT": "05", + "UINT": "06", + "UDINT": "07", + "REAL": "08", + "INT24": "10", + "LREAL": "11", + "INT40": "12", + "INT48": "13", + "INT56": "14", + "LINT": "15", + "UINT24": "16", + "UINT40": "18", + "UINT48": "19", + "UINT56": "1a", + "ULINT": "1b", + "USINT": "1e", + "BITARR8": "2d", + "BITARR16": "2e", + "BITARR32": "2f", + "BIT1": "30", + "BIT2": "31", + "BIT3": "32", + "BIT4": "33", + "BIT5": "34", + "BIT6": "35", + "BIT7": "36", + "BIT8": "37"} + + def GetSmartViewInfos(self): + """ + Parse XML data for "Smart View" of EEPROM contents. + @return smartview_infos : EEPROM contents dictionary + """ + + smartview_infos = {"eeprom_size": 128, + "pdi_type": 0, + "device_emulation": "False", + "vendor_id": '0x00000000', + "product_code": '0x00000000', + "revision_no": '0x00000000', + "serial_no": '0x00000000', + "supported_mailbox": "", + "mailbox_bootstrapconf_outstart": '0', + "mailbox_bootstrapconf_outlength": '0', + "mailbox_bootstrapconf_instart": '0', + "mailbox_bootstrapconf_inlength": '0', + "mailbox_standardconf_outstart": '0', + "mailbox_standardconf_outlength": '0', + "mailbox_standardconf_instart": '0', + "mailbox_standardconf_inlength": '0'} + + slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos()) + type_infos = slave.getType() + device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos) + + # 'device' represents current slave device selected by user + if device is not None: + for eeprom_element in device.getEeprom().getcontent(): + # get EEPROM size; -- + if eeprom_element["name"] == "ByteSize": + smartview_infos["eeprom_size"] = eeprom_element + + elif eeprom_element["name"] == "ConfigData": + configData_data = self.DecimalToHex(eeprom_element) + # 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) + # 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 + smartview_infos["supported_mailbox"] = smartview_infos["supported_mailbox"].strip(", ") + + # get standard configuration of mailbox; - + for sm_element in device.getSm(): + if sm_element.getcontent() == "MBoxOut": + smartview_infos["mailbox_standardconf_outstart"] = str(ExtractHexDecValue(sm_element.getStartAddress())) + smartview_infos["mailbox_standardconf_outlength"] = str(ExtractHexDecValue(sm_element.getDefaultSize())) + elif sm_element.getcontent() == "MBoxIn": + smartview_infos["mailbox_standardconf_instart"] = str(ExtractHexDecValue(sm_element.getStartAddress())) + smartview_infos["mailbox_standardconf_inlength"] = str(ExtractHexDecValue(sm_element.getDefaultSize())) + else: + 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. + 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; + 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; + 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; + if device.getType().getSerialNo() is not None: + serial_no = device.getType().getSerialNo() + smartview_infos["serial_no"] = "0x"+"{:0>8x}".format(ExtractHexDecValue(serial_no)) + + return smartview_infos + + else: + return None + + def DecimalToHex(self, decnum): + """ + Convert decimal value into hexadecimal representation. + @param decnum : decimal value + @return hex_data : hexadecimal representation of input value in decimal + """ + value = "%x" % decnum + 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) + + return hex_data + + def SiiRead(self): + """ + Get slave EEPROM contents maintained by master device using "ethercat sii_read -p %d" command. + Command example : "ethercat sii_read -p 0" + @return return_val : result of "ethercat sii_read" (binary data) + """ + error, return_val = self.Controler.RemoteExec(SII_READ%(self.Controler.GetSlavePos()), return_val = None) + self.SiiData = return_val + return return_val + + def SiiWrite(self, binary): + """ + Overwrite slave EEPROM contents using "ethercat sii_write -p %d" command. + Command example : "ethercat sii_write -p 0 - (binary contents)" + @param binary : EEPROM contents in binary data format + @return return_val : result of "ethercat sii_write" (If it succeeds, the return value is NULL.) + """ + error, return_val = self.Controler.RemoteExec(SII_WRITE%(self.Controler.GetSlavePos()), return_val = None, sii_data = binary) + return return_val + + def LoadData(self): + """ + Loading data from EEPROM use Sii_Read Method + @return self.BinaryCode : slave EEPROM data in binary format (zero-padded) + """ + return_val = self.Controler.CommonMethod.SiiRead() + self.BinaryCode = return_val + self.Controler.SiiData = self.BinaryCode + + # append zero-filled padding data up to EEPROM size + for index in range(self.SmartViewInfosFromXML["eeprom_size"] - len(self.BinaryCode)): + self.BinaryCode = self.BinaryCode +'ff'.decode('hex') + + return self.BinaryCode + + def HexRead(self, binary): + """ + Convert binary digit representation into hexadecimal representation for "Hex View" menu. + @param binary : binary digits + @return hexCode : hexadecimal digits + @return hexview_table_row, hexview_table_col : Grid size for "Hex View" UI + """ + row_code = [] + row_text = "" + row = 0 + hex_code = [] + + hexview_table_col = 17 + + for index in range(0, len(binary)) : + if len(binary[index]) != 1: + break + else: + digithexstr = hex(ord(binary[index])) + + tempvar2 = digithexstr[2:4] + if len(tempvar2) == 1: + tempvar2 = "0" + tempvar2 + row_code.append(tempvar2) + + if int(digithexstr, 16)>=32 and int(digithexstr, 16)<=126: + row_text = row_text + chr(int(digithexstr, 16)) + else: + row_text = row_text + "." + + if index != 0 : + if len(row_code) == (hexview_table_col - 1): + row_code.append(row_text) + hex_code.append(row_code) + row_text = "" + row_code = [] + row = row + 1 + + hexview_table_row = row + + return hex_code, hexview_table_row, hexview_table_col + + def GenerateEEPROMList(self, data, direction, length): + """ + Generate EEPROM data list by reconstructing 'data' string. + example : data="12345678", direction=0, length=8 -> eeprom_list=['12', '34', '56', '78'] + data="12345678", direction=1, length=8 -> eeprom_list=['78', '56', '34', '12'] + @param data : string to be reconstructed + @param direction : endianness + @param length : data length + @return eeprom_list : reconstructed list data structure + """ + eeprom_list = [] + + if direction is 0 or 1: + for i in range(length/2): + if data == "": + eeprom_list.append("00") + else: + eeprom_list.append(data[direction*(length-2):direction*(length-2)+2]) + data = data[(1-direction)*2:length-direction*2] + length -= 2 + return eeprom_list + + def XmlToEeprom(self): + """ + Extract slave EEPROM contents using slave ESI XML file. + - Mandatory parts + - String category : ExtractEEPROMStringCategory() + - General category : ExtractEEPROMGeneralCategory() + - FMMU category : ExtractEEPROMFMMUCategory + - SyncM category : ExtractEEPROMSyncMCategory() + - Tx/RxPDO category : ExtractEEPROMPDOCategory() + - DC category : ExtractEEPROMDCCategory() + @return eeprom_binary + """ + eeprom = [] + data = "" + eeprom_size = 0 + eeprom_binary = "" + + # 'device' is the slave device of the current EtherCAT slave plugin + 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) + eeprom += self.GenerateEEPROMList(data, 0, 28) + + # calculate CRC for EEPROM offset 0x000e-0x000f + crc = 0x48 + for segment in eeprom: + for i in range(8): + bit = crc & 0x80 + crc = (crc << 1) | ((int(segment, 16) >> (7 - i)) & 0x01) + if bit: + crc ^= 0x07 + for k in range(8): + bit = crc & 0x80 + crc <<= 1 + if bit: + crc ^= 0x07 + eeprom.append(hex(crc)[len(hex(crc))-3:len(hex(crc))-1]) + eeprom.append("00") + + # get VendorID for EEPROM offset 0x0010-0x0013; + data = "" + 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"]: + data = "{:0>8x}".format(vendor_id) + eeprom += self.GenerateEEPROMList(data, 1, 8) + + # get Product Code for EEPROM offset 0x0014-0x0017; + data = "" + if device.getType().getProductCode() is not None: + data = "{:0>8x}".format(ExtractHexDecValue(device.getType().getProductCode())) + eeprom += self.GenerateEEPROMList(data, 1, 8) + + # get Revision Number for EEPROM offset 0x0018-0x001b; + data = "" + if device.getType().getRevisionNo() is not None: + data = "{:0>8x}".format(ExtractHexDecValue(device.getType().getRevisionNo())) + eeprom += self.GenerateEEPROMList(data, 1, 8) + + # get Serial Number for EEPROM 0x001c-0x001f; + data = "" + if device.getType().getSerialNo() is not None: + data = "{:0>8x}".format(ExtractHexDecValue(device.getType().getSerialNo())) + eeprom += self.GenerateEEPROMList(data, 1, 8) + + # get Execution Delay for EEPROM 0x0020-0x0021; not analyzed yet + eeprom.append("00") + eeprom.append("00") + + # get Port0/1 Delay for EEPROM offset 0x0022-0x0025; not analyzed yet + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + + # reserved for EEPROM offset 0x0026-0x0027; + eeprom.append("00") + eeprom.append("00") + + # 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) + eeprom += self.GenerateEEPROMList(data, 0, 16) + + # get Standard Mailbox for EEPROM offset 0x0030-0x0037; - + data = "" + 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())) + standard_receive_mailbox_size = "{:0>4x}".format(ExtractHexDecValue(sm_element.getDefaultSize())) + elif sm_element.getcontent() == "MBoxIn": + standard_send_mailbox_offset = "{:0>4x}".format(ExtractHexDecValue(sm_element.getStartAddress())) + standard_send_mailbox_size = "{:0>4x}".format(ExtractHexDecValue(sm_element.getDefaultSize())) + + if standard_receive_mailbox_offset is None: + eeprom.append("00") + eeprom.append("00") + else: + eeprom.append(standard_receive_mailbox_offset[2:4]) + eeprom.append(standard_receive_mailbox_offset[0:2]) + if standard_receive_mailbox_size is None: + eeprom.append("00") + eeprom.append("00") + else: + eeprom.append(standard_receive_mailbox_size[2:4]) + eeprom.append(standard_receive_mailbox_size[0:2]) + if standard_send_mailbox_offset is None: + eeprom.append("00") + eeprom.append("00") + else: + eeprom.append(standard_send_mailbox_offset[2:4]) + eeprom.append(standard_send_mailbox_offset[0:2]) + if standard_send_mailbox_size is None: + eeprom.append("00") + eeprom.append("00") + else: + eeprom.append(standard_send_mailbox_size[2:4]) + eeprom.append(standard_send_mailbox_size[0:2]) + + # get supported mailbox protocols for EEPROM offset 0x0038-0x0039; + data = 0 + mb = device.getMailbox() + if mb is not None : + for bit,mbprot in enumerate(mailbox_protocols): + if getattr(mb,"get%s"%mbprot)() is not None: + data += 1<---- + # vendor_specific_data : vendor specific data (binary type) + vendor_specific_data = "" + # 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: + for vendor_spec_string in vendor_spec_strings: + if data == vendor_spec_string: + self.OrderIdx = vendor_spec_strings.index(data)+1 + typeflag = True + break + if typeflag is False: + count += 1 + self.Strings.append(data) + vendor_spec_strings.append(data) + typeflag = True + self.OrderIdx = count + vendor_specific_data += "{:0>2x}".format(len(data)) + for character in range(len(data)): + vendor_specific_data += "{:0>2x}".format(ord(data[character])) + data = "" + + # element2-1; ---- + data = device.getGroupType() + if data is not None and type(data) == unicode: + for vendor_spec_string in vendor_spec_strings: + if data == vendor_spec_string: + self.GroupIdx = vendor_spec_strings.index(data)+1 + grouptypeflag = True + break + if grouptypeflag is False: + grouptype = data + count += 1 + self.Strings.append(data) + vendor_spec_strings.append(data) + grouptypeflag = True + self.GroupIdx = count + vendor_specific_data += "{:0>2x}".format(len(data)) + for character in range(len(data)): + vendor_specific_data += "{:0>2x}".format(ord(data[character])) + + # 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(): + for group_type, group_etc in vendor["groups"].iteritems(): + for device_item in group_etc["devices"]: + if device == device_item[1]: + data = group_type + if data is not None and type(data) == unicode: + for vendor_spec_string in vendor_spec_strings: + if data == vendor_spec_string: + self.GroupIdx = vendor_spec_strings.index(data)+1 + grouptypeflag = True + break + if grouptypeflag is False: + grouptype = data + count += 1 + self.Strings.append(data) + vendor_spec_strings.append(data) + grouptypeflag = True + self.GroupIdx = count + vendor_specific_data += "{:0>2x}".format(len(data)) + for character in range(len(data)): + vendor_specific_data += "{:0>2x}".format(ord(data[character])) + data = "" + + # 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(): + for device_item in group_etc["devices"]: + if device == device_item[1]: + data = group_etc["name"] + if data is not "" and type(data) == unicode: + 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])) + data = "" + + # element4; ---- + for element in device.getName(): + if element.getLcId() == 1 or element.getLcId()==1033: + data = element.getcontent() + if data is not "" and type(data) == unicode: + for vendor_spec_string in vendor_spec_strings: + if data == vendor_spec_string: + self.NameIdx = vendor_spec_strings.index(data)+1 + devnameflag = True + break + if devnameflag is False: + count += 1 + self.Strings.append(data) + vendor_spec_strings.append(data) + devnameflag = True + self.NameIdx = count + 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; ---- + if device.getcontent() is not None: + data = device.getcontent() + if data is not None and type(data) == unicode: + 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: + 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])) + data = "" + + # DC related elements + # ------ + dc_related_elements = "" + if device.getDc() is not None: + for element in device.getDc().getOpMode(): + data = element.getName() + if data is not "": + count += 1 + self.Strings.append(data) + dc_related_elements += "{:0>2x}".format(len(data)) + for character in range(len(data)): + dc_related_elements += "{:0>2x}".format(ord(data[character])) + data = "" + + # Input elements(TxPDO) + # ----; Name + input_elements = "" + inputs = [] + for element in device.getTxPdo(): + for name in element.getName(): + data = name.getcontent() + for input in inputs: + if data == input: + data = "" + if data is not "": + count += 1 + self.Strings.append(data) + inputs.append(data) + input_elements += "{:0>2x}".format(len(data)) + for character in range(len(data)): + input_elements += "{:0>2x}".format(ord(data[character])) + data = "" + for entry in element.getEntry(): + for name in entry.getName(): + data = name.getcontent() + for input in inputs: + if data == input: + data = "" + if data is not "": + count += 1 + self.Strings.append(data) + inputs.append(data) + input_elements += "{:0>2x}".format(len(data)) + for character in range(len(data)): + input_elements += "{:0>2x}".format(ord(data[character])) + data = "" + + # Output elements(RxPDO) + # ----; Name + output_elements = "" + outputs = [] + for element in device.getRxPdo(): + for name in element.getName(): + data = name.getcontent() + for output in outputs: + if data == output: + data = "" + if data is not "": + count += 1 + self.Strings.append(data) + outputs.append(data) + output_elements += "{:0>2x}".format(len(data)) + for character in range(len(data)): + output_elements += "{:0>2x}".format(ord(data[character])) + data = "" + for entry in element.getEntry(): + for name in entry.getName(): + data = name.getcontent() + for output in outputs: + if data == output: + data = "" + if data is not "": + count += 1 + self.Strings.append(data) + outputs.append(data) + output_elements += "{:0>2x}".format(len(data)) + for character in range(len(data)): + output_elements += "{:0>2x}".format(ord(data[character])) + data = "" + + # form eeprom data + # 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 + length = len(vendor_specific_data + dc_related_elements + input_elements + output_elements) + 2 + if length%4 == 0: + pass + else: + length +=length%4 + padflag = True + eeprom.append("{:0>4x}".format(length/4)[2:4]) + eeprom.append("{:0>4x}".format(length/4)[0:2]) + # total numbers of strings + eeprom.append("{:0>2x}".format(count)) + for element in [vendor_specific_data, + dc_related_elements, + input_elements, + output_elements]: + for iter in range(len(element)/2): + if element == "": + eeprom.append("00") + else: + eeprom.append(element[0:2]) + element = element[2:len(element)] + # padding if length is odd bytes + if padflag is True: + eeprom.append("ff") + + return eeprom + + def ExtractEEPROMGeneralCategory(self, device): + """ + Extract "General" category data from slave ESI XML and generate EEPROM image data. + @param device : 'device' object in the slave ESI XML + @return eeprom : "Strings" category EEPROM image data + """ + eeprom = [] + data = "" + + # category header + eeprom.append("1e") + eeprom.append("00") + + # category length + eeprom.append("10") + eeprom.append("00") + + # word 1 : Group Type index and Image index in STRINGS Category + eeprom.append("{:0>2x}".format(self.GroupIdx)) + eeprom.append("{:0>2x}".format(self.ImgIdx)) + + # word 2 : Device Type index and Device Name index in STRINGS Category + eeprom.append("{:0>2x}".format(self.OrderIdx)) + eeprom.append("{:0>2x}".format(self.NameIdx)) + + # word 3 : Physical Layer Port info. and CoE Details + eeprom.append("01") # Physical Layer Port info - assume 01 + # 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: + eeprom.append("01") + else: + eeprom.append("00") + # EoE Details; ----- + if mb is not None and mb.getEoE() is not None: + eeprom.append("01") + else: + eeprom.append("00") + + # word 5 : SoE Channels(reserved) and DS402 Channels + # SoE Details; ----- + if mb is not None and mb.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") + + # word 6 : SysmanClass(reserved) and Flags + eeprom.append("00") # reserved + # Flags + en_safeop = False + en_lrw = False + if device.getType().getTcCfgModeSafeOp() == True \ + or device.getType().getTcCfgModeSafeOp() == 1: + en_safeop = True + if device.getType().getUseLrdLwr() == True \ + or device.getType().getUseLrdLwr() == 1: + en_lrw = True + + flags = "0b"+"000000"+str(int(en_lrw))+str(int(en_safeop)) + eeprom.append("{:0>2x}".format(int(flags, 2))) + + # word 7 : Current On EBus (assume 0x0000) + eeprom.append("00") + eeprom.append("00") + # after word 7; couldn't analyze yet + eeprom.append("03") + eeprom.append("00") + eeprom.append("11") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + eeprom.append("00") + + return eeprom + + def ExtractEEPROMFMMUCategory(self, device): + """ + Extract "FMMU" category data from slave ESI XML and generate EEPROM image data. + @param device : 'device' object in the slave ESI XML + @return eeprom : "Strings" category EEPROM image data + """ + eeprom = [] + data = "" + count = 0 # number of FMMU + padflag = False + + for fmmu in device.getFmmu(): + count += 1 + if fmmu.getcontent() == "Outputs": + data += "01" + if fmmu.getcontent() == "Inputs": + data += "02" + if fmmu.getcontent() == "MBoxState": + data += "03" + + # construct of EEPROM data + if data is not "": + # category header + eeprom.append("28") + eeprom.append("00") + # category length + if count%2 == 1: + padflag = True + eeprom.append("{:0>4x}".format((count+1)/2)[2:4]) + eeprom.append("{:0>4x}".format((count+1)/2)[0:2]) + else: + eeprom.append("{:0>4x}".format((count)/2)[2:4]) + eeprom.append("{:0>4x}".format((count)/2)[0:2]) + for i in range(count): + if data == "": + eeprom.append("00") + else: + eeprom.append(data[0:2]) + data = data[2:len(data)] + # padding if length is odd bytes + if padflag is True: + eeprom.append("ff") + + return eeprom + + def ExtractEEPROMSyncMCategory(self, device): + """ + Extract "SyncM" category data from slave ESI XML and generate EEPROM image data. + @param device : 'device' object in the slave ESI XML + @return eeprom : "Strings" category EEPROM image data + """ + eeprom = [] + data = "" + number = {"MBoxOut":"01", "MBoxIn":"02", "Outputs":"03", "Inputs":"04"} + + for sm in device.getSm(): + for attr in [sm.getStartAddress(), + sm.getDefaultSize(), + sm.getControlByte()]: + if attr is not None: + data += "{:0>4x}".format(ExtractHexDecValue(attr))[2:4] + data += "{:0>4x}".format(ExtractHexDecValue(attr))[0:2] + else: + data += "0000" + if sm.getEnable() == "1" or sm.getEnable() == True: + data += "01" + else: + data += "00" + data += number[sm.getcontent()] + + if data is not "": + # category header + eeprom.append("29") + eeprom.append("00") + # 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): + if data == "": + eeprom.append("00") + else: + eeprom.append(data[0:2]) + data = data[2:len(data)] + + return eeprom + + def ExtractEEPROMPDOCategory(self, device, pdotype): + """ + Extract ""PDO (Tx, Rx)"" category data from slave ESI XML and generate EEPROM image data. + @param device : 'device' object in the slave ESI XML + @param pdotype : identifier whether "TxPDO" or "RxPDO". + @return eeprom : "Strings" category EEPROM image data + """ + eeprom = [] + data = "" + count = 0 + en_fixed = False + en_mandatory = False + en_virtual = False + + for element in eval("device.get%s()"%pdotype): + # 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 + data += "{:0>2x}".format(len(element.getEntry())) + # 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 + data += "00" + # Name Index + objname = "" + for name in element.getName(): + objname = name.getcontent() + for name in self.Strings: + count += 1 + if objname == name: + break + if len(self.Strings)+1 == count: + data += "00" + else: + data += "{:0>2x}".format(count) + count = 0 + # Flags; by Fixed, Mandatory, Virtual attributes ? + if element.getFixed() == True or 1: + en_fixed = True + if element.getMandatory() == True or 1: + en_mandatory = True + if element.getVirtual() == True or element.getVirtual(): + en_virtual = True + data += str(int(en_fixed)) + str(int(en_mandatory)) + str(int(en_virtual)) + "0" + + for entry in element.getEntry(): + # Entry Index + data += "{:0>4x}".format(ExtractHexDecValue(entry.getIndex().getcontent()))[2:4] + data += "{:0>4x}".format(ExtractHexDecValue(entry.getIndex().getcontent()))[0:2] + # Subindex + data += "{:0>2x}".format(int(entry.getSubIndex())) + # Entry Name Index + objname = "" + for name in entry.getName(): + objname = name.getcontent() + for name in self.Strings: + count += 1 + if objname == name: + break + if len(self.Strings)+1 == count: + data += "00" + else: + data += "{:0>2x}".format(count) + count = 0 + # DataType + if entry.getDataType() is not None: + if entry.getDataType().getcontent() in self.BaseDataTypeDict: + data += self.BaseDataTypeDict[entry.getDataType().getcontent()] + else: + data += "00" + else: + data += "00" + # BitLen + if entry.getBitLen() is not None: + data += "{:0>2x}".format(int(entry.getBitLen())) + else: + data += "00" + # 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 + if pdotype == "TxPdo": + eeprom.append("32") + elif pdotype == "RxPdo": + eeprom.append("33") + else: + eeprom.append("00") + eeprom.append("00") + # 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()) + for i in range(len(data)/2): + if data == "": + eeprom.append("00") + else: + eeprom.append(data[0:2]) + data = data[2:len(data)] + + return eeprom + + def ExtractEEPROMDCCategory(self, device): + """ + Extract "DC(Distributed Clock)" category data from slave ESI XML and generate EEPROM image data. + @param device : 'device' object in the slave ESI XML + @return eeprom : "Strings" category EEPROM image data + """ + eeprom = [] + data = "" + count = 0 + namecount = 0 + + if device.getDc() is not None: + for element in device.getDc().getOpMode(): + count += 1 + # assume that word 1-7 are 0x0000 + data += "0000" + data += "0000" + data += "0000" + data += "0000" + data += "0000" + data += "0000" + data += "0000" + # 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? + if element.getCycleTimeSync0() is not None: + if element.getCycleTimeSync0().getFactor() is not None: + data += "{:0>2x}".format(int(element.getCycleTimeSync0().getFactor())) + data += "00" + else: + data += "0100" + else: + data += "0100" + # Index of Name in STRINGS Category + # Name Index + objname = "" + for name in element.getName(): + objname += name + for name in self.Strings: + namecount += 1 + if objname == name: + break + if len(self.Strings)+1 == namecount: + data += "00" + else: + data += "{:0>2x}".format(namecount) + namecount = 0 + data += "00" + # assume that word 11-12 are 0x0000 + data += "0000" + data += "0000" + + if data is not "": + # category header + eeprom.append("3c") + eeprom.append("00") + # 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()) + for i in range(len(data)/2): + if data == "": + eeprom.append("00") + else: + eeprom.append(data[0:2]) + data = data[2:len(data)] + + return eeprom + + #------------------------------------------------------------------------------- + # Used Register Access + #------------------------------------------------------------------------------- + def RegRead(self, offset, length): + """ + Read slave ESC register content using "ethercat reg_read -p %d %s %s" command. + Command example : "ethercat reg_read -p 0 0x0c00 0x0400" + @param offset : register address + @param length : register length + @return return_val : register data + """ + error, return_val = self.Controler.RemoteExec(REG_READ%(self.Controler.GetSlavePos(), offset, length), 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. + Command example : "ethercat reg_write -p 0 0x0c04 0x0001" + @param address : register address + @param data : data to write + @return return_val : the execution result of "ethercat reg_write" (for error check) + """ + error, return_val = self.Controler.RemoteExec(REG_WRITE%(self.Controler.GetSlavePos(), address, data), return_val = None) + return return_val + + def Rescan(self): + """ + Synchronize EEPROM data in master controller with the data in slave device after EEPROM write. + Command example : "ethercat rescan -p 0" + """ + error, return_val = self.Controler.RemoteExec(RESCAN%(self.Controler.GetSlavePos()), return_val = None) + + #------------------------------------------------------------------------------- + # Common Use Methods + #------------------------------------------------------------------------------- + def CheckConnect(self, cyclic_flag): + """ + 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" + error, return_val = self.Controler.RemoteExec(SLAVE_XML%(self.Controler.GetSlavePos()), return_val = None) + number_of_lines = return_val.split("\n") + if len(number_of_lines) <= 2 : # No slave connected to the master controller + if not cyclic_flag : + self.CreateErrorDialog('No connected slaves') + return False + + elif len(number_of_lines) > 2 : + return True + else: + # The master controller is not connected to Beremiz host + if not cyclic_flag : + self.CreateErrorDialog('PLC not connected!') + return False + + def CreateErrorDialog(self, mention): + """ + Create a dialog to indicate error or warning. + @param mention : Error String + """ + app_frame = self.Controler.GetCTRoot().AppFrame + dlg = wx.MessageDialog (app_frame, mention, + ' Warning...', + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy()