IDE: Fix wrong red wire coloring in FBD, this time for good.
#!/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.
from __future__ import absolute_import
from __future__ import division
from builtins import str as text
import codecs
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 Exception:
pass
try:
return int(value.replace("#", "0"), 16)
except Exception:
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 upload -p (slave position) -t (type) (index) (sub index)
SDO_UPLOAD = """
import commands
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)
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 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
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
"""
# ethercat pdos
PDOS = """
import commands
result = commands.getoutput("ethercat pdos -p 0")
returnVal =result
"""
# --------------------------------------------------
# Common Method For EtherCAT Management
# --------------------------------------------------
class _CommonSlave(object):
# ----- 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)
# SDO data informations: index, sub-index, type, bit size, category-name
SDOVariables = []
SDOSubEntryData = []
# defalut value of SDO data in XML
# Not Used
DefaultValueDic = {}
# Flags for checking "write" permission of OD entries
CheckPREOP = False
CheckSAFEOP = False
CheckOP = False
# 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.HexDecode = codecs.getdecoder("hex_codec")
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 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
"""
valid_type = self.GetValidDataType(data_type)
_error, return_val = self.Controler.RemoteExec(
SDO_DOWNLOAD%(valid_type, self.Controler.GetSlavePos(),
idx, sub_idx, value), return_val = None)
return return_val
def BackupSDODataSet(self):
"""
Back-up current SDO entry information to restore the SDO data
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 dummy in range(6):
self.SaveSDOData.append([])
def GetAllSDOValuesFromSlave(self):
"""
Get SDO values of All SDO entries.
@return return_val: list of result of "SDO_UPLOAD"
"""
entry_infos = ""
alldata_idx = len(self.SDOVariables)
counter = 0
for category in self.SDOVariables:
counter +=1
# for avoid redundant repetition
if counter == alldata_idx:
continue
for entry in category:
valid_type = self.GetValidDataType(entry["type"])
for_command_string = "%s %s %s ," % \
(valid_type, entry["idx"], entry["subIdx"])
entry_infos += for_command_string
error, return_val = self.Controler.RemoteExec(SDO_UPLOAD%(entry_infos, self.Controler.GetSlavePos()), return_val = None)
return return_val
def GetSDOValuesFromSlave(self, entries_info):
"""
Get SDO values of some SDO entries.
@param entries_info: dictionary of SDO entries that is wanted to know the value.
@return return_val: list of result of "SDO_UPLOAD"
"""
entry_infos = ""
entries_info_list = entries_info.items()
entries_info_list.sort()
for (idx, subIdx), entry in entries_info_list:
valid_type = self.GetValidDataType(entry["type"])
for_command_string = "%s %s %s ," % \
(valid_type, str(idx), str(subIdx))
entry_infos += for_command_string
error, return_val = self.Controler.RemoteExec(SDO_UPLOAD%(entry_infos, self.Controler.GetSlavePos()), return_val = None)
return return_val
def ExtractObjects(self):
"""
Extract object type items from imported ESI xml.
And they are stuctured as dictionary.
@return objects: dictionary of objects
"""
objects = {}
slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
type_infos = slave.getType()
device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
if device is not None :
for dictionary in device.GetProfileDictionaries():
dictionary.load()
for object in dictionary.getObjects().getObject():
object_index = ExtractHexDecValue(object.getIndex().getcontent())
objects[(object_index)] = object
return objects
def ExtractAllDataTypes(self):
"""
Extract all data types from imported ESI xml.
@return dataTypes: dictionary of datatypes
"""
dataTypes = {}
slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
type_infos = slave.getType()
device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
for dictionary in device.GetProfileDictionaries():
dictionary.load()
datatypes = dictionary.getDataTypes()
if datatypes is not None:
for datatype in datatypes.getDataType():
dataTypes[datatype.getName()] = datatype
return dataTypes
def IsBaseDataType(self, datatype):
"""
Check if the datatype is a base data type.
@return baseTypeFlag: true if datatype is a base data type, unless false
"""
baseTypeFlag = False
for baseDataTypeList in self.BaseDataTypes.values():
if datatype in baseDataTypeList:
baseTypeFlag = True
break
return baseTypeFlag
def GetBaseDataType(self, datatype):
"""
Get a base data type corresponding the datatype.
@param datatype: Some data type (string format)
@return base data type
"""
if self.IsBaseDataType(datatype):
return datatype
elif not datatype.find("STRING") == -1:
return datatype
else:
datatypes = self.ExtractAllDataTypes()
base_datatype = datatypes[datatype].getBaseType()
return self.GetBaseDataType(base_datatype)
def GetValidDataType(self, datatype):
"""
Convert the datatype into a data type that is possible to download/upload
in etherlab master stack.
@param datatype: Some data type (string format)
@return base_type: vaild data type
"""
base_type = self.GetBaseDataType(datatype)
if re.match("STRING\([0-9]*\)", datatype) is not None:
return "string"
else:
for key, value in self.BaseDataTypes.items():
if base_type in value:
return key
return base_type
# Not Used
def GetAllEntriesList(self):
"""
Get All entries information that includes index, sub-index, name,
type, bit size, PDO mapping, and default value.
@return self.entries: dictionary of entry
"""
slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
type_infos = slave.getType()
device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
self.entries = device.GetEntriesList()
datatypes = self.ExtractAllDataTypes()
objects = self.ExtractObjects()
entries_list = self.entries.items()
entries_list.sort()
# append sub entries
for (index, subidx), entry in entries_list:
# entry_* is string type
entry_type = entry["Type"]
entry_index = entry["Index"]
try:
object_info = objects[index].getInfo()
except:
continue
if object_info is not None:
obj_content = object_info.getcontent()
typeinfo = datatypes.get(entry_type, None)
bitsize = typeinfo.getBitSize()
type_content = typeinfo.getcontent()
# ArrayInfo type
if type_content is not None and type_content["name"] == "ArrayInfo":
for arrayinfo in type_content["value"]:
element_num = arrayinfo.getElements()
first_subidx = arrayinfo.getLBound()
for offset in range(element_num):
new_subidx = int(first_subidx) + offset
entry_subidx = hex(new_subidx)
if obj_content["value"][new_subidx]["name"] == "SubItem":
subitem = obj_content["value"][new_subidx]["value"]
subname = subitem[new_subidx].getName()
if subname is not None:
entry_name = "%s - %s" % \
(ExtractName(objects[index].getName()), subname)
else:
entry_name = ExtractName(objects[index].getName())
self.entries[(index, new_subidx)] = {
"Index": entry_index,
"SubIndex": entry_subidx,
"Name": entry_name,
"Type": typeinfo.getBaseType(),
"BitSize": str(bitsize/element_num),
"Access": entry["Access"],
"PDOMapping": entry["PDOMapping"]}
try:
value_info = subitem[new_subidx].getInfo().getcontent()\
["value"][0]["value"][0]
self.AppendDefaultValue(index, new_subidx, value_info)
except:
pass
try:
value_info = subitem[subidx].getInfo().getcontent()\
["value"][0]["value"][0]
self.AppendDefaultValue(index, subidx, value_info)
except:
pass
# EnumInfo type
elif type_content is not None and type_content["name"] == "EnumInfo":
self.entries[(index, subidx)]["EnumInfo"] = {}
for enuminfo in type_content["value"]:
text = enuminfo.getText()
enum = enuminfo.getEnum()
self.entries[(index, subidx)]["EnumInfo"][str(enum)] = text
self.entries[(index, subidx)]["DefaultValue"] = "0x00"
# another types
else:
if subidx == 0x00:
tmp_subidx = 0x00
try:
if obj_content["value"][tmp_subidx]["name"] == "SubItem":
sub_name = entry["Name"].split(" - ")[1]
for num in range(len(obj_content["value"])):
if sub_name == \
obj_content["value"][num]["value"][num].getName():
subitem_content = obj_content["value"][tmp_subidx]\
["value"][tmp_subidx]
value_info = subitem_content.getInfo().getcontent()\
["value"][0]["value"][0]
tmp_subidx += 1
break
else:
value_info = None
else:
value_info = \
obj_content["value"][tmp_subidx]["value"][tmp_subidx]
self.AppendDefaultValue(index, subidx, value_info)
except:
pass
return self.entries
# Not Used
def AppendDefaultValue(self, index, subidx, value_info=None):
"""
Get the default value from the ESI xml
@param index: entry index
@param subidx: entry sub index
@param value_info: dictionary of infomation about default value
"""
# there is not default value.
if value_info == None:
return
raw_value = value_info["value"]
# default value is hex binary type.
if value_info["name"] == "DefaultData":
raw_value_bit = list(hex(raw_value).split("0x")[1])
datatype = self.GetValidDataType(self.entries[(index, subidx)]["Type"])
if datatype is "string" or datatype is "octet_string":
if "L" in raw_value_bit:
raw_value_bit.remove("L")
default_value = "".join(raw_value_bit).decode("hex")
elif datatype is "unicode_string":
default_value = "".join(raw_value_bit).decode("hex").\
decode("utf-8")
else:
bit_num = len(raw_value_bit)
# padding
if not bit_num%2 == 0:
raw_value_bit.insert(0, "0")
default_value_bit = []
# little endian -> big endian
for num in range(bit_num):
if num%2 == 0:
default_value_bit.insert(0, raw_value_bit[num])
default_value_bit.insert(1, raw_value_bit[num+1])
default_value = "0x%s" % "".join(default_value_bit)
# default value is string type.
# this case is not tested yet.
elif value_info["name"] == "DefaultString":
default_value = raw_value
# default value is Hex or Dec type.
elif value_info["name"] == "DefaultValue":
default_value = "0x" + hex(ExtractHexDecValue(raw_value))
self.entries[(index, subidx)]["DefaultValue"] = default_value
# -------------------------------------------------------------------------------
# Used PDO Monitoring
# -------------------------------------------------------------------------------
def RequestPDOInfo(self):
"""
Load slave information from RootClass (XML data) and parse the information
(calling SlavePDOData() method).
"""
# Load slave information from 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())
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
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()
}
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, "sm" : Sm,
"number_of_entry" : count, "exclude_list" : exclude_list}
self.TxPDOCategory.append(categorys)
# Parsing RxPDO entries
for pdo, _pdo_info in ([(rxpdo, "Outputs") for rxpdo 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())
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
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())
}
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, "sm" : Sm,
"number_of_entry" : count, "exclude_list" : exclude_list}
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.TxPDOInfo = []
self.TxPDOCategory = []
self.RxPDOInfo = []
self.RxPDOCategory = []
# -------------------------------------------------------------------------------
# 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",
"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; <Device>-<Eeprom>-<ByteSize>
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; <Device>-<Eeprom>-<ConfigData> address 0x00
smartview_infos["pdi_type"] = int(configData_data[0:2], 16)
# get state of device emulation; <Device>-<Eeprom>-<ConfigData> 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; <Device>-<Eeprom>-<BootStrap>
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; <Device>-<Mailbox>
with device.getMailbox() as mb:
if mb is not None:
for mailbox_protocol in mailbox_protocols:
if getattr(mb, "get%s" % mailbox_protocol)() is not None:
smartview_infos["supported_mailbox"] += "%s, " % mailbox_protocol
smartview_infos["supported_mailbox"] = smartview_infos["supported_mailbox"].strip(", ")
# get standard configuration of mailbox; <Device>-<Sm>
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 <Device>-<Type>
# vendor ID; by default, pre-defined value in self.ModulesLibrary
# if device type in 'vendor' item equals to actual slave device type, set 'vendor_id' to vendor ID.
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" % 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(int(decnum, 16))
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 dummy in range(self.SmartViewInfosFromXML["eeprom_size"] - len(self.BinaryCode)):
self.BinaryCode = self.BinaryCode + self.HexDecode('ff')[0]
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 dummy 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; <Device>-<Eeprom>-<ConfigData>
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 dummy 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; <Device>-<Eeprom>-<BootStrap>
data = ""
for eeprom_element in device.getEeprom().getcontent():
if eeprom_element["name"] == "BootStrap":
data = "{:0>16x}".format(int(eeprom_element,16))
eeprom += self.GenerateEEPROMList(data, 0, 16)
# get Standard Mailbox for EEPROM offset 0x0030-0x0037; <Device>-<sm>
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
with device.getMailbox() as mb:
if mb is not None:
for bit, mbprot in enumerate(mailbox_protocols):
if getattr(mb, "get%s" % mbprot)() is not None:
data += 1 << bit
data = "{:0>4x}".format(data)
eeprom.append(data[2:4])
eeprom.append(data[0:2])
# resereved for EEPROM offset 0x003a-0x007b;
for i in range(0x007b-0x003a+0x0001):
eeprom.append("00")
# get EEPROM size for EEPROM offset 0x007c-0x007d;
# Modify by jblee because of update IDE module (minidom -> lxml)
data = ""
for eeprom_element in device.getEeprom().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")
else:
eeprom.append(data[2:4])
eeprom.append(data[0:2])
# Version for EEPROM 0x007e-0x007f;
# According to "EtherCAT Slave Device Description(V0.3.0)"
eeprom.append("01")
eeprom.append("00")
# append String Category data
for data in self.ExtractEEPROMStringCategory(device):
eeprom.append(data)
# append General Category data
for data in self.ExtractEEPROMGeneralCategory(device):
eeprom.append(data)
# append FMMU Category data
for data in self.ExtractEEPROMFMMUCategory(device):
eeprom.append(data)
# append SyncM Category data
for data in self.ExtractEEPROMSyncMCategory(device):
eeprom.append(data)
# append TxPDO Category data
for data in self.ExtractEEPROMPDOCategory(device, "TxPdo"):
eeprom.append(data)
# append RxPDO Category data
for data in self.ExtractEEPROMPDOCategory(device, "RxPdo"):
eeprom.append(data)
# append DC Category data
for data in self.ExtractEEPROMDCCategory(device):
eeprom.append(data)
# append padding
padding = eeprom_size-len(eeprom)
for i in range(padding):
eeprom.append("ff")
# convert binary code
for index in range(eeprom_size):
eeprom_binary = eeprom_binary + self.HexDecode(eeprom[index])[0]
return eeprom_binary
def ExtractEEPROMStringCategory(self, device):
"""
Extract "Strings" 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 = []
self.Strings = []
data = ""
count = 0 # string counter
padflag = False # padding flag if category length is odd
# index information for General Category in EEPROM
self.GroupIdx = 0
self.ImgIdx = 0
self.OrderIdx = 0
self.NameIdx = 0
# flag for preventing duplicated vendor specific data
typeflag = False
grouptypeflag = False
groupnameflag = False
devnameflag = False
imageflag = False
# vendor specific data
# element1; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Type>
# 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 != "" and isinstance(data, text):
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; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<GroupType>
data = device.getGroupType()
if data is not None and isinstance(data, text):
for vendor_spec_string in vendor_spec_strings:
if data == vendor_spec_string:
self.GroupIdx = vendor_spec_strings.index(data)+1
grouptypeflag = True
break
if grouptypeflag is False:
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; <EtherCATInfo>-<Groups>-<Group>-<Type>
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 isinstance(data, text):
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:
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; <EtherCATInfo>-<Descriptions>-<Groups>-<Group>-<Name(LcId is "1033")>
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 != "" and isinstance(data, text):
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; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Name(LcId is "1033" or "1"?)>
for element in device.getName():
if element.getLcId() == 1 or element.getLcId() == 1033:
data = element.getcontent()
if data != "" and isinstance(data, text):
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; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Image16x14>
if device.getcontent() is not None:
data = device.getcontent()
if data is not None and isinstance(data, text):
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; <EtherCATInfo>-<Descriptions>-<Groups>-<Group>-<Image16x14>
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 isinstance(data, text):
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
# <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Dc>-<OpMode>-<Name>
dc_related_elements = ""
if device.getDc() is not None:
for element in device.getDc().getOpMode():
data = element.getName()
if data != "":
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)
# <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<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 != "":
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 != "":
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)
# <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<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 != "":
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 != "":
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 dummy 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 = []
# 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; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<CoE>
coe_details = 1 # sdo enabled
with device.getMailbox() as mb
if mb is not None:
coe = mb.getCoE()
if coe is not None:
for bit, flag in enumerate(["SdoInfo", "PdoAssign", "PdoConfig",
"PdoUpload", "CompleteAccess"]):
if getattr(coe, "get%s" % flag)() is not None:
coe_details += 1 << bit
eeprom.append("{:0>2x}".format(coe_details))
# word 4 : FoE Details and EoE Details
# FoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<FoE>
if mb is not None and mb.getFoE() is not None:
eeprom.append("01")
else:
eeprom.append("00")
# EoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<EoE>
if mb is not None and mb.getEoE() is not None:
eeprom.append("01")
else:
eeprom.append("00")
# word 5 : SoE Channels(reserved) and DS402 Channels
# SoE Details; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<SoE>
if mb is not None and mb.getSoE() is not None:
eeprom.append("01")
else:
eeprom.append("00")
# DS402Channels; <EtherCATInfo>-<Descriptions>-<Devices>-<Device>-<Mailbox>-<CoE>: DS402Channels
ds402ch = False
if mb is not None:
coe = mb.getCoE()
if coe is not None:
ds402ch = coe.getDS402Channels()
eeprom.append("01" if ds402ch in [True, 1] else "00")
# word 6 : SysmanClass(reserved) and Flags
eeprom.append("00") # reserved
# Flags
en_safeop = False
en_lrw = False
if device.getType().getTcCfgModeSafeOp() is True \
or device.getType().getTcCfgModeSafeOp() == 1:
en_safeop = True
if device.getType().getUseLrdLwr() is 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 != "":
# 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 dummy 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() is True:
data += "01"
else:
data += "00"
data += number[sm.getcontent()]
if data != "":
# 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 dummy 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() is True or 1:
en_fixed = True
if element.getMandatory() is True or 1:
en_mandatory = True
if element.getVirtual() is 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() is True or entry.getFixed() == 1:
en_fixed = True
data += str(int(en_fixed)) + "000"
if data != "":
# 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 dummy 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 != "":
# 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 dummy 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 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.
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)
# -------------------------------------------------------------------------------
# Used DC Configuration
#-------------------------------------------------------------------------------
def LoadESIData(self):
return_data = []
slave = self.Controler.CTNParent.GetSlave(self.Controler.GetSlavePos())
type_infos = slave.getType()
device, alignment = self.Controler.CTNParent.GetModuleInfos(type_infos)
if device.getDc() is not None:
for OpMode in device.getDc().getOpMode():
temp_data = {
"desc" : OpMode.getDesc() if OpMode.getDesc() is not None else "Unused",
"assign_activate" : OpMode.getAssignActivate() \
if OpMode.getAssignActivate() is not None else "#x0000",
"cycletime_sync0" : OpMode.getCycleTimeSync0().getcontent() \
if OpMode.getCycleTimeSync0() is not None else None,
"shifttime_sync0" : OpMode.getShiftTimeSync0().getcontent() \
if OpMode.getShiftTimeSync0() is not None else None,
"cycletime_sync1" : OpMode.getShiftTimeSync1().getcontent() \
if OpMode.getShiftTimeSync1() is not None else None,
"shifttime_sync1" : OpMode.getShiftTimeSync1().getcontent() \
if OpMode.getShiftTimeSync1() is not None else None
}
if OpMode.getCycleTimeSync0() is not None:
temp_data["cycletime_sync0_factor"] = OpMode.getCycleTimeSync0().getFactor()
if OpMode.getCycleTimeSync1() is not None:
temp_data["cycletime_sync1_factor"] = OpMode.getCycleTimeSync1().getFactor()
return_data.append(temp_data)
return return_data
#-------------------------------------------------------------------------------
# Common Use Methods
# -------------------------------------------------------------------------------
def CheckConnect(self, cyclic_flag):
"""
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()