--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etherlab/EthercatCFileGenerator.py Fri Mar 29 01:15:01 2013 +0100
@@ -0,0 +1,547 @@
+import os
+
+from EthercatSlave import ExtractHexDecValue, DATATYPECONVERSION, ExtractName
+
+SLAVE_PDOS_CONFIGURATION_DECLARATION = """
+/* Slave %(slave)d, "%(device_type)s"
+ * Vendor ID: 0x%(vendor).8x
+ * Product code: 0x%(product_code).8x
+ * Revision number: 0x%(revision_number).8x
+ */
+
+ec_pdo_entry_info_t slave_%(slave)d_pdo_entries[] = {
+%(pdos_entries_infos)s
+};
+
+ec_pdo_info_t slave_%(slave)d_pdos[] = {
+%(pdos_infos)s
+};
+
+ec_sync_info_t slave_%(slave)d_syncs[] = {
+%(pdos_sync_infos)s
+ {0xff}
+};
+"""
+
+SLAVE_CONFIGURATION_TEMPLATE = """
+ if (!(slave%(slave)d = ecrt_master_slave_config(master, %(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x))) {
+ SLOGF(LOG_CRITICAL, "Failed to get slave %(device_type)s configuration at alias %(alias)d and position %(position)d.\\n");
+ return -1;
+ }
+
+ if (ecrt_slave_config_pdos(slave%(slave)d, EC_END, slave_%(slave)d_syncs)) {
+ SLOGF(LOG_CRITICAL, "Failed to configure PDOs for slave %(device_type)s at alias %(alias)d and position %(position)d.\\n");
+ return -1;
+ }
+"""
+
+SLAVE_INITIALIZATION_TEMPLATE = """
+ {
+ uint8_t value[%(data_size)d];
+ EC_WRITE_%(data_type)s((uint8_t *)value, %(data)s);
+ if (ecrt_master_sdo_download(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &abort_code)) {
+ SLOGF(LOG_CRITICAL, "Failed to initialize slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%d\\n", abort_code);
+ return -1;
+ }
+ }
+"""
+
+SLAVE_OUTPUT_PDO_DEFAULT_VALUE = """
+ {
+ uint8_t value[%(data_size)d];
+ if (ecrt_master_sdo_upload(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &result_size, &abort_code)) {
+ SLOGF(LOG_CRITICAL, "Failed to get default value for output PDO in slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%ud\\n", abort_code);
+ return -1;
+ }
+ %(real_var)s = EC_READ_%(data_type)s((uint8_t *)value);
+ }
+"""
+
+def ConfigureVariable(entry_infos, str_completion):
+ entry_infos["data_type"] = DATATYPECONVERSION.get(entry_infos["var_type"], None)
+ if entry_infos["data_type"] is None:
+ raise ValueError, _("Type of location \"%s\" not yet supported!") % entry_infos["var_name"]
+
+ if not entry_infos.get("no_decl", False):
+ if entry_infos.has_key("real_var"):
+ str_completion["located_variables_declaration"].append(
+ "IEC_%(var_type)s %(real_var)s;" % entry_infos)
+ else:
+ entry_infos["real_var"] = "beremiz" + entry_infos["var_name"]
+ str_completion["located_variables_declaration"].extend(
+ ["IEC_%(var_type)s %(real_var)s;" % entry_infos,
+ "IEC_%(var_type)s *%(var_name)s = &%(real_var)s;" % entry_infos])
+ for declaration in entry_infos.get("extra_declarations", []):
+ entry_infos["extra_decl"] = declaration
+ str_completion["located_variables_declaration"].append(
+ "IEC_%(var_type)s *%(extra_decl)s = &%(real_var)s;" % entry_infos)
+ elif not entry_infos.has_key("real_var"):
+ entry_infos["real_var"] = "beremiz" + entry_infos["var_name"]
+
+ str_completion["used_pdo_entry_offset_variables_declaration"].append(
+ "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x;" % entry_infos)
+
+ if entry_infos["data_type"] == "BIT":
+ str_completion["used_pdo_entry_offset_variables_declaration"].append(
+ "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x_bit;" % entry_infos)
+
+ str_completion["used_pdo_entry_configuration"].append(
+ (" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, " +
+ "0x%(index).4x, %(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x, " +
+ "&slave%(slave)d_%(index).4x_%(subindex).2x_bit},") % entry_infos)
+
+ if entry_infos["dir"] == "I":
+ str_completion["retrieve_variables"].append(
+ (" %(real_var)s = EC_READ_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " +
+ "slave%(slave)d_%(index).4x_%(subindex).2x_bit);") % entry_infos)
+ elif entry_infos["dir"] == "Q":
+ str_completion["publish_variables"].append(
+ (" EC_WRITE_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " +
+ "slave%(slave)d_%(index).4x_%(subindex).2x_bit, %(real_var)s);") % entry_infos)
+
+ else:
+ str_completion["used_pdo_entry_configuration"].append(
+ (" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, 0x%(index).4x, " +
+ "%(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x},") % entry_infos)
+
+ if entry_infos["dir"] == "I":
+ str_completion["retrieve_variables"].append(
+ (" %(real_var)s = EC_READ_%(data_type)s(domain1_pd + " +
+ "slave%(slave)d_%(index).4x_%(subindex).2x);") % entry_infos)
+ elif entry_infos["dir"] == "Q":
+ str_completion["publish_variables"].append(
+ (" EC_WRITE_%(data_type)s(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " +
+ "%(real_var)s);") % entry_infos)
+
+def ExclusionSortFunction(x, y):
+ if x["matching"] == y["matching"]:
+ if x["assigned"] and not y["assigned"]:
+ return -1
+ elif not x["assigned"] and y["assigned"]:
+ return 1
+ return cmp(x["count"], y["count"])
+ return -cmp(x["matching"], y["matching"])
+
+class _EthercatCFileGenerator:
+
+ def __init__(self, controler):
+ self.Controler = controler
+
+ self.Slaves = []
+ self.UsedVariables = {}
+
+ def __del__(self):
+ self.Controler = None
+
+ def DeclareSlave(self, slave_index, slave):
+ self.Slaves.append((slave_index, slave.getInfo().getAutoIncAddr(), slave))
+
+ def DeclareVariable(self, slave_index, index, subindex, iec_type, dir, name, no_decl=False):
+ slave_variables = self.UsedVariables.setdefault(slave_index, {})
+
+ entry_infos = slave_variables.get((index, subindex), None)
+ if entry_infos is None:
+ slave_variables[(index, subindex)] = {
+ "infos": (iec_type, dir, name, no_decl, []),
+ "mapped": False}
+ return name
+ elif entry_infos["infos"][:2] == (iec_type, dir):
+ if name != entry_infos["infos"][2]:
+ if dir == "I":
+ entry_infos["infos"][4].append(name)
+ return entry_infos["infos"][2]
+ else:
+ raise ValueError, _("Output variables can't be defined with different locations (%s and %s)") % (entry_infos["infos"][2], name)
+ else:
+ raise ValueError, _("Definition conflict for location \"%s\"") % name
+
+ def GenerateCFile(self, filepath, location_str, master_number):
+
+ # Extract etherlab master code template
+ plc_etherlab_filepath = os.path.join(os.path.split(__file__)[0], "plc_etherlab.c")
+ plc_etherlab_file = open(plc_etherlab_filepath, 'r')
+ plc_etherlab_code = plc_etherlab_file.read()
+ plc_etherlab_file.close()
+
+ # Initialize strings for formatting master code template
+ str_completion = {
+ "location": location_str,
+ "master_number": master_number,
+ "located_variables_declaration": [],
+ "used_pdo_entry_offset_variables_declaration": [],
+ "used_pdo_entry_configuration": [],
+ "pdos_configuration_declaration": "",
+ "slaves_declaration": "",
+ "slaves_configuration": "",
+ "slaves_output_pdos_default_values_extraction": "",
+ "slaves_initialization": "",
+ "retrieve_variables": [],
+ "publish_variables": [],
+ }
+
+ # Initialize variable storing variable mapping state
+ for slave_entries in self.UsedVariables.itervalues():
+ for entry_infos in slave_entries.itervalues():
+ entry_infos["mapped"] = False
+
+ # Sort slaves by position (IEC_Channel)
+ self.Slaves.sort()
+ # Initialize dictionary storing alias auto-increment position values
+ alias = {}
+
+ # Generating code for each slave
+ for (slave_idx, slave_alias, slave) in self.Slaves:
+ type_infos = slave.getType()
+
+ # Defining slave alias and auto-increment position
+ if alias.get(slave_alias) is not None:
+ alias[slave_alias] += 1
+ else:
+ alias[slave_alias] = 0
+ slave_pos = (slave_alias, alias[slave_alias])
+
+ # Extract slave device informations
+ device, alignment = self.Controler.GetModuleInfos(type_infos)
+ if device is not None:
+
+ # Extract slaves variables to be mapped
+ slave_variables = self.UsedVariables.get(slave_idx, {})
+
+ # Extract slave device object dictionary entries
+ device_entries = device.GetEntriesList()
+
+ # Adding code for declaring slave in master code template strings
+ for element in ["vendor", "product_code", "revision_number"]:
+ type_infos[element] = ExtractHexDecValue(type_infos[element])
+ type_infos.update(dict(zip(["slave", "alias", "position"], (slave_idx,) + slave_pos)))
+
+ # Extract slave device CoE informations
+ device_coe = device.getCoE()
+ if device_coe is not None:
+
+ # If device support CanOpen over Ethernet, adding code for calling
+ # init commands when initializing slave in master code template strings
+ initCmds = []
+ for initCmd in device_coe.getInitCmd():
+ initCmds.append({
+ "Index": ExtractHexDecValue(initCmd.getIndex()),
+ "Subindex": ExtractHexDecValue(initCmd.getSubIndex()),
+ "Value": initCmd.getData().getcontent()})
+ initCmds.extend(slave.getStartupCommands())
+ for initCmd in initCmds:
+ index = initCmd["Index"]
+ subindex = initCmd["Subindex"]
+ entry = device_entries.get((index, subindex), None)
+ if entry is not None:
+ data_size = entry["BitSize"] / 8
+ data_str = ("0x%%.%dx" % (data_size * 2)) % initCmd["Value"]
+ init_cmd_infos = {
+ "index": index,
+ "subindex": subindex,
+ "data": data_str,
+ "data_type": DATATYPECONVERSION.get(entry["Type"]),
+ "data_size": data_size
+ }
+ init_cmd_infos.update(type_infos)
+ str_completion["slaves_initialization"] += SLAVE_INITIALIZATION_TEMPLATE % init_cmd_infos
+
+ # Extract slave device PDO configuration capabilities
+ PdoAssign = device_coe.getPdoAssign()
+ PdoConfig = device_coe.getPdoConfig()
+ else:
+ PdoAssign = PdoConfig = False
+
+ # Test if slave has a configuration or need one
+ if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 and PdoConfig and PdoAssign:
+
+ str_completion["slaves_declaration"] += "static ec_slave_config_t *slave%(slave)d = NULL;\n" % type_infos
+ str_completion["slaves_configuration"] += SLAVE_CONFIGURATION_TEMPLATE % type_infos
+
+ # Initializing
+ pdos_infos = {
+ "pdos_entries_infos": [],
+ "pdos_infos": [],
+ "pdos_sync_infos": [],
+ }
+ pdos_infos.update(type_infos)
+
+ sync_managers = []
+ for sync_manager_idx, sync_manager in enumerate(device.getSm()):
+ sync_manager_infos = {
+ "index": sync_manager_idx,
+ "name": sync_manager.getcontent(),
+ "slave": slave_idx,
+ "pdos": [],
+ "pdos_number": 0,
+ }
+
+ sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
+ sync_manager_direction = sync_manager_control_byte & 0x0c
+ sync_manager_watchdog = sync_manager_control_byte & 0x40
+ if sync_manager_direction:
+ sync_manager_infos["sync_manager_type"] = "EC_DIR_OUTPUT"
+ else:
+ sync_manager_infos["sync_manager_type"] = "EC_DIR_INPUT"
+ if sync_manager_watchdog:
+ sync_manager_infos["watchdog"] = "EC_WD_ENABLE"
+ else:
+ sync_manager_infos["watchdog"] = "EC_WD_DISABLE"
+
+ sync_managers.append(sync_manager_infos)
+
+ pdos_index = []
+ exclusive_pdos = {}
+ selected_pdos = []
+ for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
+ [(pdo, "Outputs") for pdo in device.getRxPdo()]):
+
+ pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
+ pdos_index.append(pdo_index)
+
+ excluded_list = pdo.getExclude()
+ if len(excluded_list) > 0:
+ exclusion_list = [pdo_index]
+ for excluded in excluded_list:
+ exclusion_list.append(ExtractHexDecValue(excluded.getcontent()))
+ exclusion_list.sort()
+
+ exclusion_scope = exclusive_pdos.setdefault(tuple(exclusion_list), [])
+
+ entries = pdo.getEntry()
+ pdo_mapping_match = {
+ "index": pdo_index,
+ "matching": 0,
+ "count": len(entries),
+ "assigned": pdo.getSm() is not None
+ }
+ exclusion_scope.append(pdo_mapping_match)
+
+ for entry in entries:
+ index = ExtractHexDecValue(entry.getIndex().getcontent())
+ subindex = ExtractHexDecValue(entry.getSubIndex())
+ if slave_variables.get((index, subindex), None) is not None:
+ pdo_mapping_match["matching"] += 1
+
+ elif pdo.getMandatory():
+ selected_pdos.append(pdo_index)
+
+ excluded_pdos = []
+ for exclusion_scope in exclusive_pdos.itervalues():
+ exclusion_scope.sort(ExclusionSortFunction)
+ start_excluding_index = 0
+ if exclusion_scope[0]["matching"] > 0:
+ selected_pdos.append(exclusion_scope[0]["index"])
+ start_excluding_index = 1
+ excluded_pdos.extend([pdo["index"] for pdo in exclusion_scope[start_excluding_index:] if PdoAssign or not pdo["assigned"]])
+
+ for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
+ [(pdo, "Outputs") for pdo in device.getRxPdo()]):
+ entries = pdo.getEntry()
+
+ pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
+ if pdo_index in excluded_pdos:
+ continue
+
+ pdo_needed = pdo_index in selected_pdos
+
+ entries_infos = []
+
+ for entry in entries:
+ index = ExtractHexDecValue(entry.getIndex().getcontent())
+ subindex = ExtractHexDecValue(entry.getSubIndex())
+ entry_infos = {
+ "index": index,
+ "subindex": subindex,
+ "name": ExtractName(entry.getName()),
+ "bitlen": entry.getBitLen(),
+ }
+ entry_infos.update(type_infos)
+ entries_infos.append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
+
+ entry_declaration = slave_variables.get((index, subindex), None)
+ if entry_declaration is not None and not entry_declaration["mapped"]:
+ pdo_needed = True
+
+ entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"],
+ entry_declaration["infos"])))
+ entry_declaration["mapped"] = True
+
+ entry_type = entry.getDataType().getcontent()
+ if entry_infos["var_type"] != entry_type:
+ message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"]
+ if (self.Controler.GetSizeOfType(entry_infos["var_type"]) !=
+ self.Controler.GetSizeOfType(entry_type)):
+ raise ValueError, message
+ else:
+ self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
+
+ if (entry_infos["dir"] == "I" and pdo_type != "Inputs" or
+ entry_infos["dir"] == "Q" and pdo_type != "Outputs"):
+ raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"]
+
+ ConfigureVariable(entry_infos, str_completion)
+
+ elif pdo_type == "Outputs" and entry.getDataType() is not None and device_coe is not None:
+ data_type = entry.getDataType().getcontent()
+ entry_infos["dir"] = "Q"
+ entry_infos["data_size"] = max(1, entry_infos["bitlen"] / 8)
+ entry_infos["data_type"] = DATATYPECONVERSION.get(data_type)
+ entry_infos["var_type"] = data_type
+ entry_infos["real_var"] = "slave%(slave)d_%(index).4x_%(subindex).2x_default" % entry_infos
+
+ ConfigureVariable(entry_infos, str_completion)
+
+ str_completion["slaves_output_pdos_default_values_extraction"] += \
+ SLAVE_OUTPUT_PDO_DEFAULT_VALUE % entry_infos
+
+ if pdo_needed:
+ for excluded in pdo.getExclude():
+ excluded_index = ExtractHexDecValue(excluded.getcontent())
+ if excluded_index not in excluded_pdos:
+ excluded_pdos.append(excluded_index)
+
+ sm = pdo.getSm()
+ if sm is None:
+ for sm_idx, sync_manager in enumerate(sync_managers):
+ if sync_manager["name"] == pdo_type:
+ sm = sm_idx
+ if sm is None:
+ raise ValueError, _("No sync manager available for %s pdo!") % pdo_type
+
+ sync_managers[sm]["pdos_number"] += 1
+ sync_managers[sm]["pdos"].append(
+ {"slave": slave_idx,
+ "index": pdo_index,
+ "name": ExtractName(pdo.getName()),
+ "type": pdo_type,
+ "entries": entries_infos,
+ "entries_number": len(entries_infos),
+ "fixed": pdo.getFixed() == True})
+
+ if PdoConfig and PdoAssign:
+ dynamic_pdos = {}
+ dynamic_pdos_number = 0
+ for category, min_index, max_index in [("Inputs", 0x1600, 0x1800),
+ ("Outputs", 0x1a00, 0x1C00)]:
+ for sync_manager in sync_managers:
+ if sync_manager["name"] == category:
+ category_infos = dynamic_pdos.setdefault(category, {})
+ category_infos["sync_manager"] = sync_manager
+ category_infos["pdos"] = [pdo for pdo in category_infos["sync_manager"]["pdos"]
+ if not pdo["fixed"] and pdo["type"] == category]
+ category_infos["current_index"] = min_index
+ category_infos["max_index"] = max_index
+ break
+
+ for (index, subindex), entry_declaration in slave_variables.iteritems():
+
+ if not entry_declaration["mapped"]:
+ entry = device_entries.get((index, subindex), None)
+ if entry is None:
+ raise ValueError, _("Unknown entry index 0x%4.4x, subindex 0x%2.2x for device %s") % \
+ (index, subindex, type_infos["device_type"])
+
+ entry_infos = {
+ "index": index,
+ "subindex": subindex,
+ "name": entry["Name"],
+ "bitlen": entry["BitSize"],
+ }
+ entry_infos.update(type_infos)
+
+ entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"],
+ entry_declaration["infos"])))
+ entry_declaration["mapped"] = True
+
+ if entry_infos["var_type"] != entry["Type"]:
+ message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"]
+ if (self.Controler.GetSizeOfType(entry_infos["var_type"]) !=
+ self.Controler.GetSizeOfType(entry["Type"])):
+ raise ValueError, message
+ else:
+ self.Controler.GetCTRoot().logger.write_warning(message + "\n")
+
+ if entry_infos["dir"] == "I" and entry["PDOMapping"] in ["T", "RT"]:
+ pdo_type = "Inputs"
+ elif entry_infos["dir"] == "Q" and entry["PDOMapping"] in ["R", "RT"]:
+ pdo_type = "Outputs"
+ else:
+ raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"]
+
+ if not dynamic_pdos.has_key(pdo_type):
+ raise ValueError, _("No Sync manager defined for %s!") % pdo_type
+
+ ConfigureVariable(entry_infos, str_completion)
+
+ if len(dynamic_pdos[pdo_type]["pdos"]) > 0:
+ pdo = dynamic_pdos[pdo_type]["pdos"][0]
+ else:
+ while dynamic_pdos[pdo_type]["current_index"] in pdos_index:
+ dynamic_pdos[pdo_type]["current_index"] += 1
+ if dynamic_pdos[pdo_type]["current_index"] >= dynamic_pdos[pdo_type]["max_index"]:
+ raise ValueError, _("No more free PDO index available for %s!") % pdo_type
+ pdos_index.append(dynamic_pdos[pdo_type]["current_index"])
+
+ dynamic_pdos_number += 1
+ pdo = {"slave": slave_idx,
+ "index": dynamic_pdos[pdo_type]["current_index"],
+ "name": "Dynamic PDO %d" % dynamic_pdos_number,
+ "type": pdo_type,
+ "entries": [],
+ "entries_number": 0,
+ "fixed": False}
+ dynamic_pdos[pdo_type]["sync_manager"]["pdos_number"] += 1
+ dynamic_pdos[pdo_type]["sync_manager"]["pdos"].append(pdo)
+ dynamic_pdos[pdo_type]["pdos"].append(pdo)
+
+ pdo["entries"].append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
+ if entry_infos["bitlen"] < alignment:
+ print (alignment, entry_infos["bitlen"])
+ pdo["entries"].append(" {0x0000, 0x00, %d}, /* None */" % (alignment - entry_infos["bitlen"]))
+ pdo["entries_number"] += 1
+
+ if pdo["entries_number"] == 255:
+ dynamic_pdos[pdo_type]["pdos"].pop(0)
+
+ pdo_offset = 0
+ entry_offset = 0
+ for sync_manager_infos in sync_managers:
+
+ for pdo_infos in sync_manager_infos["pdos"]:
+ pdo_infos["offset"] = entry_offset
+ pdo_entries = pdo_infos["entries"]
+ pdos_infos["pdos_infos"].append(
+ (" {0x%(index).4x, %(entries_number)d, " +
+ "slave_%(slave)d_pdo_entries + %(offset)d}, /* %(name)s */") % pdo_infos)
+ entry_offset += len(pdo_entries)
+ pdos_infos["pdos_entries_infos"].extend(pdo_entries)
+
+ sync_manager_infos["offset"] = pdo_offset
+ pdo_offset_shift = sync_manager_infos["pdos_number"]
+ pdos_infos["pdos_sync_infos"].append(
+ (" {%(index)d, %(sync_manager_type)s, %(pdos_number)d, " +
+ ("slave_%(slave)d_pdos + %(offset)d" if pdo_offset_shift else "NULL") +
+ ", %(watchdog)s},") % sync_manager_infos)
+ pdo_offset += pdo_offset_shift
+
+ for element in ["pdos_entries_infos", "pdos_infos", "pdos_sync_infos"]:
+ pdos_infos[element] = "\n".join(pdos_infos[element])
+
+ str_completion["pdos_configuration_declaration"] += SLAVE_PDOS_CONFIGURATION_DECLARATION % pdos_infos
+
+ for (index, subindex), entry_declaration in slave_variables.iteritems():
+ if not entry_declaration["mapped"]:
+ message = _("Entry index 0x%4.4x, subindex 0x%2.2x not mapped for device %s") % \
+ (index, subindex, type_infos["device_type"])
+ self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
+
+ for element in ["used_pdo_entry_offset_variables_declaration",
+ "used_pdo_entry_configuration",
+ "located_variables_declaration",
+ "retrieve_variables",
+ "publish_variables"]:
+ str_completion[element] = "\n".join(str_completion[element])
+
+ etherlabfile = open(filepath, 'w')
+ etherlabfile.write(plc_etherlab_code % str_completion)
+ etherlabfile.close()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etherlab/EthercatCIA402Slave.py Fri Mar 29 01:15:01 2013 +0100
@@ -0,0 +1,258 @@
+import os
+
+import wx
+
+from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
+
+from MotionLibrary import Headers, AxisXSD
+from EthercatSlave import _EthercatSlaveCTN
+from ConfigEditor import CIA402NodeEditor
+
+NODE_VARIABLES = [
+ ("ControlWord", 0x6040, 0x00, "UINT", "Q"),
+ ("TargetPosition", 0x607a, 0x00, "DINT", "Q"),
+ ("ModesOfOperation", 0x06060, 0x00, "SINT", "Q"),
+ ("StatusWord", 0x6041, 0x00, "UINT", "I"),
+ ("ModesOfOperationDisplay", 0x06061, 0x00, "SINT", "I"),
+ ("ActualPosition", 0x6064, 0x00, "DINT", "I"),
+ ("ActualVelocity", 0x606C, 0x00, "DINT", "I"),
+]
+
+DEFAULT_RETRIEVE = " __CIA402Node_%(location)s.axis->%(name)s = *(__CIA402Node_%(location)s.%(name)s);"
+DEFAULT_PUBLISH = " *(__CIA402Node_%(location)s.%(name)s) = __CIA402Node_%(location)s.axis->%(name)s;"
+
+EXTRA_NODE_VARIABLES = [
+ ("ErrorCode", [
+ {"description": ("ErrorCode", 0x603F, 0x00, "UINT", "I"),
+ "publish": None}
+ ]),
+ ("DigitalInputs", [
+ {"description": ("DigitalInputs", 0x60FD, 0x00, "UDINT", "I"),
+ "publish": None}
+ ]),
+ ("DigitalOutputs", [
+ {"description": ("DigitalOutputs", 0x60FE, 0x00, "UDINT", "Q"),
+ "retrieve": None}
+ ])
+]
+EXTRA_NODE_VARIABLES_DICT = dict([("Enable" + name, value) for name, value in EXTRA_NODE_VARIABLES])
+
+BLOCK_INPUT_TEMPLATE = " __SET_VAR(%(blockname)s->,%(input_name)s, %(input_value)s);"
+BLOCK_OUTPUT_TEMPLATE = " __SET_VAR(data__->,%(output_name)s, __GET_VAR(%(blockname)s->%(output_name)s));"
+
+BLOCK_FUNCTION_TEMPLATE = """
+extern void ETHERLAB%(ucase_blocktype)s_body__(ETHERLAB%(ucase_blocktype)s* data__);
+void __%(blocktype)s_%(location)s(MC_%(ucase_blocktype)s *data__) {
+__DECLARE_GLOBAL_PROTOTYPE(ETHERLAB%(ucase_blocktype)s, %(blockname)s);
+ETHERLAB%(ucase_blocktype)s* %(blockname)s = __GET_GLOBAL_%(blockname)s();
+%(extract_inputs)s
+ETHERLAB%(ucase_blocktype)s_body__(%(blockname)s);
+%(return_outputs)s
+}
+"""
+
+BLOCK_FUNTION_DEFINITION_TEMPLATE = """ if (!__CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s)
+__CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location)s;"""
+
+GLOBAL_INSTANCES = [
+ {"blocktype": "GetTorqueLimit",
+ "inputs": [],
+ "outputs": [{"name": "TorqueLimitPos", "type": "UINT"},
+ {"name": "TorqueLimitNeg", "type": "UINT"}]},
+ {"blocktype": "SetTorqueLimit",
+ "inputs": [{"name": "TorqueLimitPos", "type": "UINT"},
+ {"name": "TorqueLimitNeg", "type": "UINT"}],
+ "outputs": []},
+]
+
+#--------------------------------------------------
+# Ethercat CIA402 Node
+#--------------------------------------------------
+
+class _EthercatCIA402SlaveCTN(_EthercatSlaveCTN):
+ XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:element name="CIA402SlaveParams">
+ <xsd:complexType>
+ %s
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ """ % ("\n".join(['<xsd:attribute name="Enable%s" type="xsd:boolean" use="optional" default="false"/>' % category
+ for category, variables in EXTRA_NODE_VARIABLES]) + AxisXSD)
+
+ NODE_PROFILE = 402
+ EditorType = CIA402NodeEditor
+
+ ConfNodeMethods = [
+ {"bitmap" : "CIA402AxisRef",
+ "name" : _("Axis Ref"),
+ "tooltip" : _("Initiate Drag'n drop of Axis ref located variable"),
+ "method" : "_getCIA402AxisRef",
+ "push": True},
+ ]
+
+ def GetIconName(self):
+ return "CIA402Slave"
+
+ def SetParamsAttribute(self, path, value):
+ if path == "CIA402SlaveParams.Type":
+ path = "SlaveParams.Type"
+ elif path == "CIA402SlaveParams.Alias":
+ path = "SlaveParams.Alias"
+ return _EthercatSlaveCTN.SetParamsAttribute(self, path, value)
+
+ def GetVariableLocationTree(self):
+ axis_name = self.CTNName()
+ current_location = self.GetCurrentLocation()
+ children = [{"name": "%s Axis Ref" % (axis_name),
+ "type": LOCATION_VAR_INPUT,
+ "size": "W",
+ "IEC_type": "AXIS_REF",
+ "var_name": axis_name,
+ "location": "%%IW%s.0" % (".".join(map(str, current_location))),
+ "description": "",
+ "children": []}]
+ children.extend(self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), current_location, axis_name))
+ return {"name": axis_name,
+ "type": LOCATION_CONFNODE,
+ "location": self.GetFullIEC_Channel(),
+ "children": children,
+ }
+
+ def CTNGlobalInstances(self):
+ current_location = self.GetCurrentLocation()
+ return [("%s_%s" % (block_infos["blocktype"], "_".join(map(str, current_location))),
+ "EtherLab%s" % block_infos["blocktype"]) for block_infos in GLOBAL_INSTANCES]
+
+ def _getCIA402AxisRef(self):
+ data = wx.TextDataObject(str(("%%IW%s.0" % ".".join(map(str, self.GetCurrentLocation())),
+ "location", "AXIS_REF", self.CTNName(), "")))
+ dragSource = wx.DropSource(self.GetCTRoot().AppFrame)
+ dragSource.SetData(data)
+ dragSource.DoDragDrop()
+
+ def CTNGenerate_C(self, buildpath, locations):
+ current_location = self.GetCurrentLocation()
+
+ location_str = "_".join(map(lambda x:str(x), current_location))
+
+ plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_cia402node.c")
+ plc_cia402node_file = open(plc_cia402node_filepath, 'r')
+ plc_cia402node_code = plc_cia402node_file.read()
+ plc_cia402node_file.close()
+
+ str_completion = {
+ "slave_pos": self.GetSlavePos(),
+ "location": location_str,
+ "MCL_headers": Headers,
+ "extern_located_variables_declaration": [],
+ "fieldbus_interface_declaration": [],
+ "fieldbus_interface_definition": [],
+ "entry_variables": [],
+ "init_axis_params": [],
+ "init_entry_variables": [],
+ "extra_variables_retrieve": [],
+ "extra_variables_publish": []
+ }
+
+ for blocktype_infos in GLOBAL_INSTANCES:
+ texts = {
+ "blocktype": blocktype_infos["blocktype"],
+ "ucase_blocktype": blocktype_infos["blocktype"].upper(),
+ "location": "_".join(map(str, current_location))
+ }
+ texts["blockname"] = "%(ucase_blocktype)s_%(location)s" % texts
+
+ inputs = [{"input_name": "POS", "input_value": str(self.GetSlavePos())},
+ {"input_name": "EXECUTE", "input_value": "__GET_VAR(data__->EXECUTE)"}] +\
+ [{"input_name": input["name"].upper(),
+ "input_value": "__GET_VAR(data__->%s)" % input["name"].upper()}
+ for input in blocktype_infos["inputs"]]
+ input_texts = []
+ for input_infos in inputs:
+ input_infos.update(texts)
+ input_texts.append(BLOCK_INPUT_TEMPLATE % input_infos)
+ texts["extract_inputs"] = "\n".join(input_texts)
+
+ outputs = [{"output_name": output} for output in ["DONE", "BUSY", "ERROR"]] + \
+ [{"output_name": output["name"].upper()} for output in blocktype_infos["outputs"]]
+ output_texts = []
+ for output_infos in outputs:
+ output_infos.update(texts)
+ output_texts.append(BLOCK_OUTPUT_TEMPLATE % output_infos)
+ texts["return_outputs"] = "\n".join(output_texts)
+
+ str_completion["fieldbus_interface_declaration"].append(
+ BLOCK_FUNCTION_TEMPLATE % texts)
+
+ str_completion["fieldbus_interface_definition"].append(
+ BLOCK_FUNTION_DEFINITION_TEMPLATE % texts)
+
+ variables = NODE_VARIABLES[:]
+
+ params = self.CTNParams[1].getElementInfos(self.CTNParams[0])
+ for param in params["children"]:
+ if param["name"] in EXTRA_NODE_VARIABLES_DICT:
+ if param["value"]:
+ extra_variables = EXTRA_NODE_VARIABLES_DICT.get(param["name"])
+ for variable_infos in extra_variables:
+ var_infos = {
+ "location": location_str,
+ "name": variable_infos["description"][0]
+ }
+ variables.append(variable_infos["description"])
+ retrieve_template = variable_infos.get("retrieve", DEFAULT_RETRIEVE)
+ publish_template = variable_infos.get("publish", DEFAULT_PUBLISH)
+
+ if retrieve_template is not None:
+ str_completion["extra_variables_retrieve"].append(
+ retrieve_template % var_infos)
+ if publish_template is not None:
+ str_completion["extra_variables_publish"].append(
+ publish_template % var_infos)
+ elif param["value"] is not None:
+ param_infos = {
+ "location": location_str,
+ "param_name": param["name"],
+ }
+ if param["type"] == "boolean":
+ param_infos["param_value"] = {True: "true", False: "false"}[param["value"]]
+ else:
+ param_infos["param_value"] = str(param["value"])
+ str_completion["init_axis_params"].append(
+ " __CIA402Node_%(location)s.axis->%(param_name)s = %(param_value)s;" % param_infos)
+
+ for variable in variables:
+ var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable))
+ var_infos["location"] = location_str
+ var_infos["var_size"] = self.GetSizeOfType(var_infos["var_type"])
+ var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos
+
+ str_completion["extern_located_variables_declaration"].append(
+ "IEC_%(var_type)s *%(var_name)s;" % var_infos)
+ str_completion["entry_variables"].append(
+ " IEC_%(var_type)s *%(name)s;" % var_infos)
+ str_completion["init_entry_variables"].append(
+ " __CIA402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos)
+
+ self.CTNParent.FileGenerator.DeclareVariable(
+ self.GetSlavePos(), var_infos["index"], var_infos["subindex"],
+ var_infos["var_type"], var_infos["dir"], var_infos["var_name"])
+
+ for element in ["extern_located_variables_declaration",
+ "fieldbus_interface_declaration",
+ "fieldbus_interface_definition",
+ "entry_variables",
+ "init_axis_params",
+ "init_entry_variables",
+ "extra_variables_retrieve",
+ "extra_variables_publish"]:
+ str_completion[element] = "\n".join(str_completion[element])
+
+ Gen_CIA402Nodefile_path = os.path.join(buildpath, "cia402node_%s.c"%location_str)
+ cia402nodefile = open(Gen_CIA402Nodefile_path, 'w')
+ cia402nodefile.write(plc_cia402node_code % str_completion)
+ cia402nodefile.close()
+
+ return [(Gen_CIA402Nodefile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))],"",True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etherlab/EthercatMaster.py Fri Mar 29 01:15:01 2013 +0100
@@ -0,0 +1,762 @@
+import os
+import cPickle
+from xml.dom import minidom
+
+import wx
+
+from xmlclass import *
+
+from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
+from ConfigTreeNode import ConfigTreeNode
+from dialogs import BrowseValuesLibraryDialog
+from IDEFrame import TITLE, FILEMENU, PROJECTTREE
+
+from EthercatSlave import _EthercatSlaveCTN, ExtractHexDecValue, GenerateHexDecValue, TYPECONVERSION, VARCLASSCONVERSION
+from EthercatCFileGenerator import _EthercatCFileGenerator
+from ConfigEditor import MasterEditor
+from POULibrary import POULibrary
+
+try:
+ from EthercatCIA402Slave import _EthercatCIA402SlaveCTN
+ HAS_MCL = True
+except:
+ HAS_MCL = False
+
+#--------------------------------------------------
+# Remote Exec Etherlab Commands
+#--------------------------------------------------
+
+SCAN_COMMAND = """
+import commands
+result = commands.getoutput("ethercat slaves")
+slaves = []
+for slave_line in result.splitlines():
+ chunks = slave_line.split()
+ idx, pos, state, flag = chunks[:4]
+ name = " ".join(chunks[4:])
+ alias, position = pos.split(":")
+ slave = {"idx": int(idx),
+ "alias": int(alias),
+ "position": int(position),
+ "name": name}
+ details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"])
+ for details_line in details.splitlines():
+ details_line = details_line.strip()
+ for header, param in [("Vendor Id:", "vendor_id"),
+ ("Product code:", "product_code"),
+ ("Revision number:", "revision_number")]:
+ if details_line.startswith(header):
+ slave[param] = details_line.split()[-1]
+ break
+ slaves.append(slave)
+returnVal = slaves
+"""
+
+#--------------------------------------------------
+# Etherlab Specific Blocks Library
+#--------------------------------------------------
+
+def GetLocalPath(filename):
+ return os.path.join(os.path.split(__file__)[0], filename)
+
+class EtherlabLibrary(POULibrary):
+ def GetLibraryPath(self):
+ return GetLocalPath("pous.xml")
+
+ def Generate_C(self, buildpath, varlist, IECCFLAGS):
+ etherlab_ext_file = open(GetLocalPath("etherlab_ext.c"), 'r')
+ etherlab_ext_code = etherlab_ext_file.read()
+ etherlab_ext_file.close()
+
+ Gen_etherlabfile_path = os.path.join(buildpath, "etherlab_ext.c")
+ ethelabfile = open(Gen_etherlabfile_path,'w')
+ ethelabfile.write(etherlab_ext_code)
+ ethelabfile.close()
+
+ runtimefile_path = os.path.join(os.path.split(__file__)[0], "runtime_etherlab.py")
+ return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "",
+ ("runtime_etherlab.py", file(GetLocalPath("runtime_etherlab.py"))))
+
+#--------------------------------------------------
+# Ethercat MASTER
+#--------------------------------------------------
+
+EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd"))
+
+def sort_commands(x, y):
+ if x["Index"] == y["Index"]:
+ return cmp(x["Subindex"], y["Subindex"])
+ return cmp(x["Index"], y["Index"])
+
+cls = EtherCATConfigClasses.get("Config_Slave", None)
+if cls:
+
+ def getType(self):
+ slave_info = self.getInfo()
+ return {"device_type": slave_info.getName(),
+ "vendor": GenerateHexDecValue(slave_info.getVendorId()),
+ "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16),
+ "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)}
+ setattr(cls, "getType", getType)
+
+ def setType(self, type_infos):
+ slave_info = self.getInfo()
+ slave_info.setName(type_infos["device_type"])
+ slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"]))
+ slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"]))
+ slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"]))
+ setattr(cls, "setType", setType)
+
+ def getInitCmds(self, create_default=False):
+ Mailbox = self.getMailbox()
+ if Mailbox is None:
+ if create_default:
+ self.addMailbox()
+ Mailbox = self.getMailbox()
+ else:
+ return None
+ CoE = Mailbox.getCoE()
+ if CoE is None:
+ if create_default:
+ Mailbox.addCoE()
+ CoE = Mailbox.getCoE()
+ else:
+ return None
+ InitCmds = CoE.getInitCmds()
+ if InitCmds is None and create_default:
+ CoE.addInitCmds()
+ InitCmds = CoE.getInitCmds()
+ return InitCmds
+ setattr(cls, "getInitCmds", getInitCmds)
+
+ def getStartupCommands(self):
+ pos = self.getInfo().getPhysAddr()
+ InitCmds = self.getInitCmds()
+ if InitCmds is None:
+ return []
+ commands = []
+ for idx, InitCmd in enumerate(InitCmds.getInitCmd()):
+ comment = InitCmd.getComment()
+ if comment is None:
+ comment = ""
+ commands.append({
+ "command_idx": idx,
+ "Position": pos,
+ "Index": InitCmd.getIndex(),
+ "Subindex": InitCmd.getSubIndex(),
+ "Value": InitCmd.getData(),
+ "Description": comment})
+ commands.sort(sort_commands)
+ return commands
+ setattr(cls, "getStartupCommands", getStartupCommands)
+
+ def appendStartupCommand(self, command_infos):
+ InitCmds = self.getInitCmds(True)
+ command = EtherCATConfigClasses["InitCmds_InitCmd"]()
+ command.setIndex(command_infos["Index"])
+ command.setSubIndex(command_infos["Subindex"])
+ command.setData(command_infos["Value"])
+ command.setComment(command_infos["Description"])
+ InitCmds.appendInitCmd(command)
+ return len(InitCmds.getInitCmd()) - 1
+ setattr(cls, "appendStartupCommand", appendStartupCommand)
+
+ def setStartupCommand(self, command_infos):
+ InitCmds = self.getInitCmds()
+ if InitCmds is not None:
+ commands = InitCmds.getInitCmd()
+ if command_infos["command_idx"] < len(commands):
+ command = commands[command_infos["command_idx"]]
+ command.setIndex(command_infos["Index"])
+ command.setSubIndex(command_infos["Subindex"])
+ command.setData(command_infos["Value"])
+ command.setComment(command_infos["Description"])
+ setattr(cls, "setStartupCommand", setStartupCommand)
+
+ def removeStartupCommand(self, command_idx):
+ InitCmds = self.getInitCmds()
+ if InitCmds is not None:
+ if command_idx < len(InitCmds.getInitCmd()):
+ InitCmds.removeInitCmd(command_idx)
+ setattr(cls, "removeStartupCommand", removeStartupCommand)
+
+ProcessVariablesXSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:element name="ProcessVariables">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="ReadFrom" type="LocationDesc" minOccurs="0"/>
+ <xsd:element name="WriteTo" type="LocationDesc" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="Name" type="xsd:string" use="required"/>
+ <xsd:attribute name="Comment" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:complexType name="LocationDesc">
+ <xsd:attribute name="Position" type="xsd:integer" use="required"/>
+ <xsd:attribute name="Index" type="xsd:integer" use="required"/>
+ <xsd:attribute name="SubIndex" type="xsd:integer" use="required"/>
+ </xsd:complexType>
+ </xsd:schema>
+"""
+
+ProcessVariablesClasses = GenerateClassesFromXSDstring(ProcessVariablesXSD)
+
+class _EthercatCTN:
+
+ CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")]
+ if HAS_MCL:
+ CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave"))
+ EditorType = MasterEditor
+
+ def __init__(self):
+ config_filepath = self.ConfigFileName()
+ config_is_saved = False
+ self.Config = EtherCATConfigClasses["EtherCATConfig"]()
+ if os.path.isfile(config_filepath):
+ config_xmlfile = open(config_filepath, 'r')
+ config_tree = minidom.parse(config_xmlfile)
+ config_xmlfile.close()
+
+ for child in config_tree.childNodes:
+ if child.nodeType == config_tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig":
+ self.Config.loadXMLTree(child)
+ config_is_saved = True
+
+ process_filepath = self.ProcessVariablesFileName()
+ process_is_saved = False
+ self.ProcessVariables = ProcessVariablesClasses["ProcessVariables"]()
+ if os.path.isfile(process_filepath):
+ process_xmlfile = open(process_filepath, 'r')
+ process_tree = minidom.parse(process_xmlfile)
+ process_xmlfile.close()
+
+ for child in process_tree.childNodes:
+ if child.nodeType == process_tree.ELEMENT_NODE and child.nodeName == "ProcessVariables":
+ self.ProcessVariables.loadXMLTree(child)
+ process_is_saved = True
+
+ if config_is_saved and process_is_saved:
+ self.CreateBuffer(True)
+ else:
+ self.CreateBuffer(False)
+ self.OnCTNSave()
+
+ def GetContextualMenuItems(self):
+ return [("Add Ethercat Slave", "Add Ethercat Slave to Master", self.OnAddEthercatSlave)]
+
+ def OnAddEthercatSlave(self, event):
+ app_frame = self.GetCTRoot().AppFrame
+ dialog = BrowseValuesLibraryDialog(app_frame,
+ "Ethercat Slave Type", self.GetSlaveTypesLibrary())
+ if dialog.ShowModal() == wx.ID_OK:
+ type_infos = dialog.GetValueInfos()
+ device, alignment = self.GetModuleInfos(type_infos)
+ if device is not None:
+ if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
+ ConfNodeType = "EthercatCIA402Slave"
+ else:
+ ConfNodeType = "EthercatSlave"
+ new_child = self.CTNAddChild("%s_0" % ConfNodeType, ConfNodeType)
+ new_child.SetParamsAttribute("SlaveParams.Type", type_infos)
+ self.CTNRequestSave()
+ new_child._OpenView()
+ app_frame._Refresh(TITLE, FILEMENU, PROJECTTREE)
+ dialog.Destroy()
+
+ def ExtractHexDecValue(self, value):
+ return ExtractHexDecValue(value)
+
+ def GetSizeOfType(self, type):
+ return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
+
+ def ConfigFileName(self):
+ return os.path.join(self.CTNPath(), "config.xml")
+
+ def ProcessVariablesFileName(self):
+ return os.path.join(self.CTNPath(), "process_variables.xml")
+
+ def FilterSlave(self, slave, vendor=None, slave_pos=None, slave_profile=None):
+ if slave_pos is not None and slave.getInfo().getPhysAddr() != slave_pos:
+ return False
+ type_infos = slave.getType()
+ if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
+ return False
+ device, alignment = self.GetModuleInfos(type_infos)
+ if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
+ return False
+ return True
+
+ def GetSlaves(self, vendor=None, slave_pos=None, slave_profile=None):
+ slaves = []
+ for slave in self.Config.getConfig().getSlave():
+ if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
+ slaves.append(slave.getInfo().getPhysAddr())
+ slaves.sort()
+ return slaves
+
+ def GetSlave(self, slave_pos):
+ for slave in self.Config.getConfig().getSlave():
+ slave_info = slave.getInfo()
+ if slave_info.getPhysAddr() == slave_pos:
+ return slave
+ return None
+
+ def GetStartupCommands(self, vendor=None, slave_pos=None, slave_profile=None):
+ commands = []
+ for slave in self.Config.getConfig().getSlave():
+ if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
+ commands.append((slave.getInfo().getPhysAddr(), slave.getStartupCommands()))
+ commands.sort()
+ return reduce(lambda x, y: x + y[1], commands, [])
+
+ def AppendStartupCommand(self, command_infos):
+ slave = self.GetSlave(command_infos["Position"])
+ if slave is not None:
+ command_idx = slave.appendStartupCommand(command_infos)
+ self.BufferModel()
+ return command_idx
+ return None
+
+ def SetStartupCommandInfos(self, command_infos):
+ slave = self.GetSlave(command_infos["Position"])
+ if slave is not None:
+ slave.setStartupCommand(command_infos)
+ self.BufferModel()
+
+ def RemoveStartupCommand(self, slave_pos, command_idx, buffer=True):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ slave.removeStartupCommand(command_idx)
+ if buffer:
+ self.BufferModel()
+
+ def SetProcessVariables(self, variables):
+ vars = []
+ for var in variables:
+ variable = ProcessVariablesClasses["ProcessVariables_variable"]()
+ variable.setName(var["Name"])
+ variable.setComment(var["Description"])
+ if var["ReadFrom"] != "":
+ position, index, subindex = var["ReadFrom"]
+ if variable.getReadFrom() is None:
+ variable.addReadFrom()
+ read_from = variable.getReadFrom()
+ read_from.setPosition(position)
+ read_from.setIndex(index)
+ read_from.setSubIndex(subindex)
+ elif variable.getReadFrom() is not None:
+ variable.deleteReadFrom()
+ if var["WriteTo"] != "":
+ position, index, subindex = var["WriteTo"]
+ if variable.getWriteTo() is None:
+ variable.addWriteTo()
+ write_to = variable.getWriteTo()
+ write_to.setPosition(position)
+ write_to.setIndex(index)
+ write_to.setSubIndex(subindex)
+ elif variable.getWriteTo() is not None:
+ variable.deleteWriteTo()
+ vars.append(variable)
+ self.ProcessVariables.setvariable(vars)
+ self.BufferModel()
+
+ def GetProcessVariables(self):
+ variables = []
+ idx = 0
+ for variable in self.ProcessVariables.getvariable():
+ var = {"Name": variable.getName(),
+ "Number": idx,
+ "Description": variable.getComment()}
+ read_from = variable.getReadFrom()
+ if read_from is not None:
+ var["ReadFrom"] = (read_from.getPosition(),
+ read_from.getIndex(),
+ read_from.getSubIndex())
+ else:
+ var["ReadFrom"] = ""
+ write_to = variable.getWriteTo()
+ if write_to is not None:
+ var["WriteTo"] = (write_to.getPosition(),
+ write_to.getIndex(),
+ write_to.getSubIndex())
+ else:
+ var["WriteTo"] = ""
+ variables.append(var)
+ idx += 1
+ return variables
+
+ def _ScanNetwork(self):
+ app_frame = self.GetCTRoot().AppFrame
+
+ execute = True
+ if len(self.Children) > 0:
+ dialog = wx.MessageDialog(app_frame,
+ _("The current network configuration will be deleted.\nDo you want to continue?"),
+ _("Scan Network"),
+ wx.YES_NO|wx.ICON_QUESTION)
+ execute = dialog.ShowModal() == wx.ID_YES
+ dialog.Destroy()
+
+ if execute:
+ error, returnVal = self.RemoteExec(SCAN_COMMAND, returnVal = None)
+ if error != 0:
+ dialog = wx.MessageDialog(app_frame, returnVal, "Error", wx.OK|wx.ICON_ERROR)
+ dialog.ShowModal()
+ dialog.Destroy()
+ elif returnVal is not None:
+ for child in self.IECSortedChildren():
+ self._doRemoveChild(child)
+
+ for slave in returnVal:
+ type_infos = {
+ "vendor": slave["vendor_id"],
+ "product_code": slave["product_code"],
+ "revision_number":slave["revision_number"],
+ }
+ device, alignment = self.GetModuleInfos(type_infos)
+ if device is not None:
+ if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
+ CTNType = "EthercatCIA402Slave"
+ else:
+ CTNType = "EthercatSlave"
+ self.CTNAddChild("slave%s" % slave["idx"], CTNType, slave["idx"])
+ self.SetSlaveAlias(slave["idx"], slave["alias"])
+ type_infos["device_type"] = device.getType().getcontent()
+ self.SetSlaveType(slave["idx"], type_infos)
+
+ def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0):
+ """
+ Create the confnodes that may be added as child to this node self
+ @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes)
+ @param CTNName: string for the name of the confnode instance
+ """
+ newConfNodeOpj = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel)
+
+ slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel())
+ if slave is None:
+ slave = EtherCATConfigClasses["Config_Slave"]()
+ slave_infos = slave.getInfo()
+ slave_infos.setName("undefined")
+ slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel())
+ slave_infos.setAutoIncAddr(0)
+ self.Config.getConfig().appendSlave(slave)
+ self.BufferModel()
+ self.OnCTNSave()
+
+ return newConfNodeOpj
+
+ def _doRemoveChild(self, CTNInstance):
+ slave_pos = CTNInstance.GetSlavePos()
+ config = self.Config.getConfig()
+ for idx, slave in enumerate(config.getSlave()):
+ slave_infos = slave.getInfo()
+ if slave_infos.getPhysAddr() == slave_pos:
+ config.removeSlave(idx)
+ self.BufferModel()
+ self.OnCTNSave()
+ ConfigTreeNode._doRemoveChild(self, CTNInstance)
+
+ def SetSlavePosition(self, slave_pos, new_pos):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ slave_info = slave.getInfo()
+ slave_info.setPhysAddr(new_pos)
+ for variable in self.ProcessVariables.getvariable():
+ read_from = variable.getReadFrom()
+ if read_from is not None and read_from.getPosition() == slave_pos:
+ read_from.setPosition(new_pos)
+ write_to = variable.getWriteTo()
+ if write_to is not None and write_to.getPosition() == slave_pos:
+ write_to.setPosition(new_pos)
+ self.CreateBuffer(True)
+ self.OnCTNSave()
+ if self._View is not None:
+ self._View.RefreshView()
+ self._View.RefreshBuffer()
+
+ def GetSlaveAlias(self, slave_pos):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ slave_info = slave.getInfo()
+ return slave_info.getAutoIncAddr()
+ return None
+
+ def SetSlaveAlias(self, slave_pos, alias):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ slave_info = slave.getInfo()
+ slave_info.setAutoIncAddr(alias)
+ self.BufferModel()
+
+ def GetSlaveType(self, slave_pos):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ return slave.getType()
+ return None
+
+ def SetSlaveType(self, slave_pos, type_infos):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ slave.setType(type_infos)
+ self.BufferModel()
+
+ def GetSlaveInfos(self, slave_pos):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ type_infos = slave.getType()
+ device, alignment = self.GetModuleInfos(type_infos)
+ if device is not None:
+ infos = type_infos.copy()
+ infos.update({"physics": device.getPhysics(),
+ "sync_managers": device.GetSyncManagers(),
+ "entries": self.GetSlaveVariables(device)})
+ return infos
+ return None
+
+ def GetSlaveVariables(self, slave_pos=None, limits=None, device=None):
+ if device is None and slave_pos is not None:
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ type_infos = slave.getType()
+ device, alignment = self.GetModuleInfos(type_infos)
+ if device is not None:
+ entries = device.GetEntriesList(limits)
+ entries_list = entries.items()
+ entries_list.sort()
+ entries = []
+ current_index = None
+ current_entry = None
+ for (index, subindex), entry in entries_list:
+ entry["children"] = []
+ if slave_pos is not None:
+ entry["Position"] = str(slave_pos)
+ entry
+ if index != current_index:
+ current_index = index
+ current_entry = entry
+ entries.append(entry)
+ elif current_entry is not None:
+ current_entry["children"].append(entry)
+ else:
+ entries.append(entry)
+ return entries
+ return []
+
+ def GetSlaveVariableDataType(self, slave_pos, index, subindex):
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ device, alignment = self.GetModuleInfos(slave.getType())
+ if device is not None:
+ entries = device.GetEntriesList()
+ entry_infos = entries.get((index, subindex))
+ if entry_infos is not None:
+ return entry_infos["Type"]
+ return None
+
+ def GetNodesVariables(self, vendor=None, slave_pos=None, slave_profile=None, limits=None):
+ entries = []
+ for slave_position in self.GetSlaves():
+ if slave_pos is not None and slave_position != slave_pos:
+ continue
+ slave = self.GetSlave(slave_position)
+ type_infos = slave.getType()
+ if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
+ continue
+ device, alignment = self.GetModuleInfos(type_infos)
+ if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
+ continue
+ entries.extend(self.GetSlaveVariables(slave_position, limits, device))
+ return entries
+
+ def GetModuleInfos(self, type_infos):
+ return self.CTNParent.GetModuleInfos(type_infos)
+
+ def GetSlaveTypesLibrary(self, profile_filter=None):
+ return self.CTNParent.GetModulesLibrary(profile_filter)
+
+ def GetLibraryVendors(self):
+ return self.CTNParent.GetVendors()
+
+ def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
+ slave = self.GetSlave(slave_pos)
+ vars = []
+ if slave is not None:
+ type_infos = slave.getType()
+
+ device, alignment = self.GetModuleInfos(type_infos)
+ if device is not None:
+ sync_managers = []
+ for sync_manager in device.getSm():
+ sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
+ sync_manager_direction = sync_manager_control_byte & 0x0c
+ if sync_manager_direction:
+ sync_managers.append(LOCATION_VAR_OUTPUT)
+ else:
+ sync_managers.append(LOCATION_VAR_INPUT)
+
+ entries = device.GetEntriesList().items()
+ entries.sort()
+ for (index, subindex), entry in entries:
+ var_size = self.GetSizeOfType(entry["Type"])
+ if var_size is not None:
+ var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
+ if var_class is not None:
+ if var_class == LOCATION_VAR_INPUT:
+ var_dir = "%I"
+ else:
+ var_dir = "%Q"
+
+ vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
+ "type": var_class,
+ "size": var_size,
+ "IEC_type": entry["Type"],
+ "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex),
+ "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location +
+ (index, subindex)))),
+ "description": "",
+ "children": []})
+
+ return vars
+
+ def CTNTestModified(self):
+ return self.ChangesToSave or not self.ModelIsSaved()
+
+ def OnCTNSave(self):
+ config_filepath = self.ConfigFileName()
+
+ config_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ config_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
+ "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"}
+ config_text += self.Config.generateXMLText("EtherCATConfig", 0, config_extras)
+
+ config_xmlfile = open(config_filepath,"w")
+ config_xmlfile.write(config_text.encode("utf-8"))
+ config_xmlfile.close()
+
+ process_filepath = self.ProcessVariablesFileName()
+
+ process_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ process_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"}
+ process_text += self.ProcessVariables.generateXMLText("ProcessVariables", 0, process_extras)
+
+ process_xmlfile = open(process_filepath,"w")
+ process_xmlfile.write(process_text.encode("utf-8"))
+ process_xmlfile.close()
+
+ self.Buffer.CurrentSaved()
+ return True
+
+ def GetProcessVariableName(self, location, var_type):
+ return "__M%s_%s" % (self.GetSizeOfType(var_type), "_".join(map(str, location)))
+
+ def _Generate_C(self, buildpath, locations):
+ current_location = self.GetCurrentLocation()
+ # define a unique name for the generated C file
+ location_str = "_".join(map(lambda x:str(x), current_location))
+
+ Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c"%location_str)
+
+ self.FileGenerator = _EthercatCFileGenerator(self)
+
+ LocationCFilesAndCFLAGS, LDFLAGS, extra_files = ConfigTreeNode._Generate_C(self, buildpath, locations)
+
+ for idx, variable in enumerate(self.ProcessVariables.getvariable()):
+ name = None
+ var_type = None
+ read_from = variable.getReadFrom()
+ write_to = variable.getWriteTo()
+ if read_from is not None:
+ pos = read_from.getPosition()
+ index = read_from.getIndex()
+ subindex = read_from.getSubIndex()
+ location = current_location + (idx, )
+ var_type = self.GetSlaveVariableDataType(pos, index, subindex)
+ name = self.FileGenerator.DeclareVariable(
+ pos, index, subindex, var_type, "I",
+ self.GetProcessVariableName(location, var_type))
+ if write_to is not None:
+ pos = write_to.getPosition()
+ index = write_to.getIndex()
+ subindex = write_to.getSubIndex()
+ if name is None:
+ location = current_location + (idx, )
+ var_type = self.GetSlaveVariableDataType(pos, index, subindex)
+ name = self.GetProcessVariableName(location, var_type)
+ self.FileGenerator.DeclareVariable(
+ pos, index, subindex, var_type, "Q", name, True)
+
+ self.FileGenerator.GenerateCFile(Gen_Ethercatfile_path, location_str, self.BaseParams.getIEC_Channel())
+
+ LocationCFilesAndCFLAGS.append(
+ (current_location,
+ [(Gen_Ethercatfile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))],
+ True))
+ LDFLAGS.append("-lethercat -lrtdm")
+
+ return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
+
+ ConfNodeMethods = [
+ {"bitmap" : "ScanNetwork",
+ "name" : _("Scan Network"),
+ "tooltip" : _("Scan Network"),
+ "method" : "_ScanNetwork"},
+ ]
+
+ def CTNGenerate_C(self, buildpath, locations):
+ current_location = self.GetCurrentLocation()
+
+ slaves = self.GetSlaves()
+ for slave_pos in slaves:
+ slave = self.GetSlave(slave_pos)
+ if slave is not None:
+ self.FileGenerator.DeclareSlave(slave_pos, slave)
+
+ for location in locations:
+ loc = location["LOC"][len(current_location):]
+ slave_pos = loc[0]
+ if slave_pos in slaves and len(loc) == 3 and location["DIR"] != "M":
+ self.FileGenerator.DeclareVariable(
+ slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"])
+
+ return [],"",False
+
+#-------------------------------------------------------------------------------
+# Current Buffering Management Functions
+#-------------------------------------------------------------------------------
+
+ """
+ Return a copy of the config
+ """
+ def Copy(self, model):
+ return cPickle.loads(cPickle.dumps(model))
+
+ def CreateBuffer(self, saved):
+ self.Buffer = UndoBuffer(cPickle.dumps((self.Config, self.ProcessVariables)), saved)
+
+ def BufferModel(self):
+ self.Buffer.Buffering(cPickle.dumps((self.Config, self.ProcessVariables)))
+
+ def ModelIsSaved(self):
+ if self.Buffer is not None:
+ return self.Buffer.IsCurrentSaved()
+ else:
+ return True
+
+ def LoadPrevious(self):
+ self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Previous())
+
+ def LoadNext(self):
+ self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Next())
+
+ def GetBufferState(self):
+ first = self.Buffer.IsFirst()
+ last = self.Buffer.IsLast()
+ return not first, not last
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etherlab/EthercatSlave.py Fri Mar 29 01:15:01 2013 +0100
@@ -0,0 +1,135 @@
+import wx
+
+from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
+from ConfigTreeNode import ConfigTreeNode
+
+from ConfigEditor import NodeEditor
+
+TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L",
+ "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L",
+ "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"}
+
+DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64",
+ "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64",
+ "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"}
+
+VARCLASSCONVERSION = {"T": LOCATION_VAR_INPUT, "R": LOCATION_VAR_OUTPUT, "RT": LOCATION_VAR_MEMORY}
+
+def ExtractHexDecValue(value):
+ try:
+ return int(value)
+ except:
+ pass
+ try:
+ return int(value.replace("#", "0"), 16)
+ except:
+ raise ValueError, "Invalid value for HexDecValue \"%s\"" % value
+
+def GenerateHexDecValue(value, base=10):
+ if base == 10:
+ return str(value)
+ elif base == 16:
+ return "#x%.8x" % value
+ else:
+ raise ValueError, "Not supported base"
+
+def ExtractName(names, default=None):
+ if len(names) == 1:
+ return names[0].getcontent()
+ else:
+ for name in names:
+ if name.getLcId() == 1033:
+ return name.getcontent()
+ return default
+
+#--------------------------------------------------
+# Ethercat Node
+#--------------------------------------------------
+
+class _EthercatSlaveCTN:
+
+ NODE_PROFILE = None
+ EditorType = NodeEditor
+
+ def GetIconName(self):
+ return "Slave"
+
+ def ExtractHexDecValue(self, value):
+ return ExtractHexDecValue(value)
+
+ def GetSizeOfType(self, type):
+ return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
+
+ def GetSlavePos(self):
+ return self.BaseParams.getIEC_Channel()
+
+ def GetParamsAttributes(self, path = None):
+ if path:
+ parts = path.split(".", 1)
+ if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
+ return self.MandatoryParams[1].getElementInfos(parts[0], parts[1])
+ elif self.CTNParams and parts[0] == self.CTNParams[0]:
+ return self.CTNParams[1].getElementInfos(parts[0], parts[1])
+ else:
+ params = []
+ if self.CTNParams:
+ params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0]))
+ else:
+ params.append({
+ 'use': 'required',
+ 'type': 'element',
+ 'name': 'SlaveParams',
+ 'value': None,
+ 'children': []
+ })
+
+ slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos())
+ params[0]['children'].insert(0,
+ {'use': 'optional',
+ 'type': self.CTNParent.GetSlaveTypesLibrary(self.NODE_PROFILE),
+ 'name': 'Type',
+ 'value': (slave_type["device_type"], slave_type)})
+ params[0]['children'].insert(1,
+ {'use': 'optional',
+ 'type': 'unsignedLong',
+ 'name': 'Alias',
+ 'value': self.CTNParent.GetSlaveAlias(self.GetSlavePos())})
+ return params
+
+ def SetParamsAttribute(self, path, value):
+ position = self.BaseParams.getIEC_Channel()
+
+ if path == "SlaveParams.Type":
+ self.CTNParent.SetSlaveType(position, value)
+ slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos())
+ value = (slave_type["device_type"], slave_type)
+ if self._View is not None:
+ wx.CallAfter(self._View.RefreshSlaveInfos)
+ return value, True
+ elif path == "SlaveParams.Alias":
+ self.CTNParent.SetSlaveAlias(position, value)
+ return value, True
+
+ value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value)
+
+ # Filter IEC_Channel, Slave_Type and Alias that have specific behavior
+ if path == "BaseParams.IEC_Channel" and value != position:
+ self.CTNParent.SetSlavePosition(position, value)
+
+ return value, refresh
+
+ def GetSlaveInfos(self):
+ return self.CTNParent.GetSlaveInfos(self.GetSlavePos())
+
+ def GetSlaveVariables(self, limits):
+ return self.CTNParent.GetSlaveVariables(self.GetSlavePos(), limits)
+
+ def GetVariableLocationTree(self):
+ return {"name": self.BaseParams.getName(),
+ "type": LOCATION_CONFNODE,
+ "location": self.GetFullIEC_Channel(),
+ "children": self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), self.GetCurrentLocation(), self.BaseParams.getName())
+ }
+
+ def CTNGenerate_C(self, buildpath, locations):
+ return [],"",False
--- a/etherlab/etherlab.py Fri Mar 29 01:13:17 2013 +0100
+++ b/etherlab/etherlab.py Fri Mar 29 01:15:01 2013 +0100
@@ -1,1677 +1,17 @@
import os, shutil
-import cPickle
from xml.dom import minidom
import wx
import csv
from xmlclass import *
-from POULibrary import POULibrary
+
from ConfigTreeNode import ConfigTreeNode
from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
-from ConfigEditor import NodeEditor, CIA402NodeEditor, MasterEditor, LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
-from dialogs import BrowseValuesLibraryDialog
-from IDEFrame import TITLE, FILEMENU, PROJECTTREE
-
-try:
- from MotionLibrary import Headers, AxisXSD
- HAS_MCL = True
-except:
- HAS_MCL = False
-
-TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L",
- "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L",
- "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L"}
-
-DATATYPECONVERSION = {"BOOL" : "BIT", "SINT" : "S8", "INT" : "S16", "DINT" : "S32", "LINT" : "S64",
- "USINT" : "U8", "UINT" : "U16", "UDINT" : "U32", "ULINT" : "U64",
- "BYTE" : "U8", "WORD" : "U16", "DWORD" : "U32", "LWORD" : "U64"}
-
-VARCLASSCONVERSION = {"T": LOCATION_VAR_INPUT, "R": LOCATION_VAR_OUTPUT, "RT": LOCATION_VAR_MEMORY}
-
-#--------------------------------------------------
-# Remote Exec Etherlab Commands
-#--------------------------------------------------
-
-SCAN_COMMAND = """
-import commands
-result = commands.getoutput("ethercat slaves")
-slaves = []
-for slave_line in result.splitlines():
- chunks = slave_line.split()
- idx, pos, state, flag = chunks[:4]
- name = " ".join(chunks[4:])
- alias, position = pos.split(":")
- slave = {"idx": int(idx),
- "alias": int(alias),
- "position": int(position),
- "name": name}
- details = commands.getoutput("ethercat slaves -p %d -v" % slave["idx"])
- for details_line in details.splitlines():
- details_line = details_line.strip()
- for header, param in [("Vendor Id:", "vendor_id"),
- ("Product code:", "product_code"),
- ("Revision number:", "revision_number")]:
- if details_line.startswith(header):
- slave[param] = details_line.split()[-1]
- break
- slaves.append(slave)
-returnVal = slaves
-"""
-
-#--------------------------------------------------
-# Etherlab Specific Blocks Library
-#--------------------------------------------------
-
-def GetLocalPath(filename):
- return os.path.join(os.path.split(__file__)[0], filename)
-
-class EtherlabLibrary(POULibrary):
- def GetLibraryPath(self):
- return GetLocalPath("pous.xml")
-
- def Generate_C(self, buildpath, varlist, IECCFLAGS):
- etherlab_ext_file = open(GetLocalPath("etherlab_ext.c"), 'r')
- etherlab_ext_code = etherlab_ext_file.read()
- etherlab_ext_file.close()
-
- Gen_etherlabfile_path = os.path.join(buildpath, "etherlab_ext.c")
- ethelabfile = open(Gen_etherlabfile_path,'w')
- ethelabfile.write(etherlab_ext_code)
- ethelabfile.close()
-
- runtimefile_path = os.path.join(os.path.split(__file__)[0], "runtime_etherlab.py")
- return ((["etherlab_ext"], [(Gen_etherlabfile_path, IECCFLAGS)], True), "",
- ("runtime_etherlab.py", file(GetLocalPath("runtime_etherlab.py"))))
-
-#--------------------------------------------------
-# Ethercat Node
-#--------------------------------------------------
-
-class _EthercatSlaveCTN:
-
- NODE_PROFILE = None
- EditorType = NodeEditor
-
- def GetIconName(self):
- return "Slave"
-
- def ExtractHexDecValue(self, value):
- return ExtractHexDecValue(value)
-
- def GetSizeOfType(self, type):
- return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
-
- def GetSlavePos(self):
- return self.BaseParams.getIEC_Channel()
-
- def GetParamsAttributes(self, path = None):
- if path:
- parts = path.split(".", 1)
- if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
- return self.MandatoryParams[1].getElementInfos(parts[0], parts[1])
- elif self.CTNParams and parts[0] == self.CTNParams[0]:
- return self.CTNParams[1].getElementInfos(parts[0], parts[1])
- else:
- params = []
- if self.CTNParams:
- params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0]))
- else:
- params.append({
- 'use': 'required',
- 'type': 'element',
- 'name': 'SlaveParams',
- 'value': None,
- 'children': []
- })
-
- slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos())
- params[0]['children'].insert(0,
- {'use': 'optional',
- 'type': self.CTNParent.GetSlaveTypesLibrary(self.NODE_PROFILE),
- 'name': 'Type',
- 'value': (slave_type["device_type"], slave_type)})
- params[0]['children'].insert(1,
- {'use': 'optional',
- 'type': 'unsignedLong',
- 'name': 'Alias',
- 'value': self.CTNParent.GetSlaveAlias(self.GetSlavePos())})
- return params
-
- def SetParamsAttribute(self, path, value):
- position = self.BaseParams.getIEC_Channel()
-
- if path == "SlaveParams.Type":
- self.CTNParent.SetSlaveType(position, value)
- slave_type = self.CTNParent.GetSlaveType(self.GetSlavePos())
- value = (slave_type["device_type"], slave_type)
- if self._View is not None:
- wx.CallAfter(self._View.RefreshSlaveInfos)
- return value, True
- elif path == "SlaveParams.Alias":
- self.CTNParent.SetSlaveAlias(position, value)
- return value, True
-
- value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value)
-
- # Filter IEC_Channel, Slave_Type and Alias that have specific behavior
- if path == "BaseParams.IEC_Channel" and value != position:
- self.CTNParent.SetSlavePosition(position, value)
-
- return value, refresh
-
- def GetSlaveInfos(self):
- return self.CTNParent.GetSlaveInfos(self.GetSlavePos())
-
- def GetSlaveVariables(self, limits):
- return self.CTNParent.GetSlaveVariables(self.GetSlavePos(), limits)
-
- def GetVariableLocationTree(self):
- return {"name": self.BaseParams.getName(),
- "type": LOCATION_CONFNODE,
- "location": self.GetFullIEC_Channel(),
- "children": self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), self.GetCurrentLocation(), self.BaseParams.getName())
- }
-
- def CTNGenerate_C(self, buildpath, locations):
- return [],"",False
-
-#--------------------------------------------------
-# Ethercat CIA402 Node
-#--------------------------------------------------
-
-if HAS_MCL:
-
- NODE_VARIABLES = [
- ("ControlWord", 0x6040, 0x00, "UINT", "Q"),
- ("TargetPosition", 0x607a, 0x00, "DINT", "Q"),
- ("ModesOfOperation", 0x06060, 0x00, "SINT", "Q"),
- ("StatusWord", 0x6041, 0x00, "UINT", "I"),
- ("ModesOfOperationDisplay", 0x06061, 0x00, "SINT", "I"),
- ("ActualPosition", 0x6064, 0x00, "DINT", "I"),
- ("ActualVelocity", 0x606C, 0x00, "DINT", "I"),
- ]
-
- DEFAULT_RETRIEVE = " __CIA402Node_%(location)s.axis->%(name)s = *(__CIA402Node_%(location)s.%(name)s);"
- DEFAULT_PUBLISH = " *(__CIA402Node_%(location)s.%(name)s) = __CIA402Node_%(location)s.axis->%(name)s;"
-
- EXTRA_NODE_VARIABLES = [
- ("ErrorCode", [
- {"description": ("ErrorCode", 0x603F, 0x00, "UINT", "I"),
- "publish": None}
- ]),
- ("DigitalInputs", [
- {"description": ("DigitalInputs", 0x60FD, 0x00, "UDINT", "I"),
- "publish": None}
- ]),
- ("DigitalOutputs", [
- {"description": ("DigitalOutputs", 0x60FE, 0x00, "UDINT", "Q"),
- "retrieve": None}
- ])
- ]
- EXTRA_NODE_VARIABLES_DICT = dict([("Enable" + name, value) for name, value in EXTRA_NODE_VARIABLES])
-
- BLOCK_INPUT_TEMPLATE = " __SET_VAR(%(blockname)s->,%(input_name)s, %(input_value)s);"
- BLOCK_OUTPUT_TEMPLATE = " __SET_VAR(data__->,%(output_name)s, __GET_VAR(%(blockname)s->%(output_name)s));"
-
- BLOCK_FUNCTION_TEMPLATE = """
-extern void ETHERLAB%(ucase_blocktype)s_body__(ETHERLAB%(ucase_blocktype)s* data__);
-void __%(blocktype)s_%(location)s(MC_%(ucase_blocktype)s *data__) {
- __DECLARE_GLOBAL_PROTOTYPE(ETHERLAB%(ucase_blocktype)s, %(blockname)s);
- ETHERLAB%(ucase_blocktype)s* %(blockname)s = __GET_GLOBAL_%(blockname)s();
-%(extract_inputs)s
- ETHERLAB%(ucase_blocktype)s_body__(%(blockname)s);
-%(return_outputs)s
-}
-"""
-
- BLOCK_FUNTION_DEFINITION_TEMPLATE = """ if (!__CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s)
- __CIA402Node_%(location)s.axis->__mcl_func_MC_%(blocktype)s = __%(blocktype)s_%(location)s;"""
-
- GLOBAL_INSTANCES = [
- {"blocktype": "GetTorqueLimit",
- "inputs": [],
- "outputs": [{"name": "TorqueLimitPos", "type": "UINT"},
- {"name": "TorqueLimitNeg", "type": "UINT"}]},
- {"blocktype": "SetTorqueLimit",
- "inputs": [{"name": "TorqueLimitPos", "type": "UINT"},
- {"name": "TorqueLimitNeg", "type": "UINT"}],
- "outputs": []},
- ]
-
- class _EthercatCIA402SlaveCTN(_EthercatSlaveCTN):
- XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
- <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <xsd:element name="CIA402SlaveParams">
- <xsd:complexType>
- %s
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- """ % ("\n".join(['<xsd:attribute name="Enable%s" type="xsd:boolean" use="optional" default="false"/>' % category
- for category, variables in EXTRA_NODE_VARIABLES]) + AxisXSD)
-
- NODE_PROFILE = 402
- EditorType = CIA402NodeEditor
-
- ConfNodeMethods = [
- {"bitmap" : "CIA402AxisRef",
- "name" : _("Axis Ref"),
- "tooltip" : _("Initiate Drag'n drop of Axis ref located variable"),
- "method" : "_getCIA402AxisRef",
- "push": True},
- ]
-
- def GetIconName(self):
- return "CIA402Slave"
-
- def SetParamsAttribute(self, path, value):
- if path == "CIA402SlaveParams.Type":
- path = "SlaveParams.Type"
- elif path == "CIA402SlaveParams.Alias":
- path = "SlaveParams.Alias"
- return _EthercatSlaveCTN.SetParamsAttribute(self, path, value)
-
- def GetVariableLocationTree(self):
- axis_name = self.CTNName()
- current_location = self.GetCurrentLocation()
- children = [{"name": "%s Axis Ref" % (axis_name),
- "type": LOCATION_VAR_INPUT,
- "size": "W",
- "IEC_type": "AXIS_REF",
- "var_name": axis_name,
- "location": "%%IW%s.0" % (".".join(map(str, current_location))),
- "description": "",
- "children": []}]
- children.extend(self.CTNParent.GetDeviceLocationTree(self.GetSlavePos(), current_location, axis_name))
- return {"name": axis_name,
- "type": LOCATION_CONFNODE,
- "location": self.GetFullIEC_Channel(),
- "children": children,
- }
-
- def CTNGlobalInstances(self):
- current_location = self.GetCurrentLocation()
- return [("%s_%s" % (block_infos["blocktype"], "_".join(map(str, current_location))),
- "EtherLab%s" % block_infos["blocktype"]) for block_infos in GLOBAL_INSTANCES]
-
- def _getCIA402AxisRef(self):
- data = wx.TextDataObject(str(("%%IW%s.0" % ".".join(map(str, self.GetCurrentLocation())),
- "location", "AXIS_REF", self.CTNName(), "")))
- dragSource = wx.DropSource(self.GetCTRoot().AppFrame)
- dragSource.SetData(data)
- dragSource.DoDragDrop()
-
- def CTNGenerate_C(self, buildpath, locations):
- current_location = self.GetCurrentLocation()
-
- location_str = "_".join(map(lambda x:str(x), current_location))
-
- plc_cia402node_filepath = os.path.join(os.path.split(__file__)[0], "plc_cia402node.c")
- plc_cia402node_file = open(plc_cia402node_filepath, 'r')
- plc_cia402node_code = plc_cia402node_file.read()
- plc_cia402node_file.close()
-
- str_completion = {
- "slave_pos": self.GetSlavePos(),
- "location": location_str,
- "MCL_headers": Headers,
- "extern_located_variables_declaration": [],
- "fieldbus_interface_declaration": [],
- "fieldbus_interface_definition": [],
- "entry_variables": [],
- "init_axis_params": [],
- "init_entry_variables": [],
- "extra_variables_retrieve": [],
- "extra_variables_publish": []
- }
-
- for blocktype_infos in GLOBAL_INSTANCES:
- texts = {
- "blocktype": blocktype_infos["blocktype"],
- "ucase_blocktype": blocktype_infos["blocktype"].upper(),
- "location": "_".join(map(str, current_location))
- }
- texts["blockname"] = "%(ucase_blocktype)s_%(location)s" % texts
-
- inputs = [{"input_name": "POS", "input_value": str(self.GetSlavePos())},
- {"input_name": "EXECUTE", "input_value": "__GET_VAR(data__->EXECUTE)"}] +\
- [{"input_name": input["name"].upper(),
- "input_value": "__GET_VAR(data__->%s)" % input["name"].upper()}
- for input in blocktype_infos["inputs"]]
- input_texts = []
- for input_infos in inputs:
- input_infos.update(texts)
- input_texts.append(BLOCK_INPUT_TEMPLATE % input_infos)
- texts["extract_inputs"] = "\n".join(input_texts)
-
- outputs = [{"output_name": output} for output in ["DONE", "BUSY", "ERROR"]] + \
- [{"output_name": output["name"].upper()} for output in blocktype_infos["outputs"]]
- output_texts = []
- for output_infos in outputs:
- output_infos.update(texts)
- output_texts.append(BLOCK_OUTPUT_TEMPLATE % output_infos)
- texts["return_outputs"] = "\n".join(output_texts)
-
- str_completion["fieldbus_interface_declaration"].append(
- BLOCK_FUNCTION_TEMPLATE % texts)
-
- str_completion["fieldbus_interface_definition"].append(
- BLOCK_FUNTION_DEFINITION_TEMPLATE % texts)
-
- variables = NODE_VARIABLES[:]
-
- params = self.CTNParams[1].getElementInfos(self.CTNParams[0])
- for param in params["children"]:
- if param["name"] in EXTRA_NODE_VARIABLES_DICT:
- if param["value"]:
- extra_variables = EXTRA_NODE_VARIABLES_DICT.get(param["name"])
- for variable_infos in extra_variables:
- var_infos = {
- "location": location_str,
- "name": variable_infos["description"][0]
- }
- variables.append(variable_infos["description"])
- retrieve_template = variable_infos.get("retrieve", DEFAULT_RETRIEVE)
- publish_template = variable_infos.get("publish", DEFAULT_PUBLISH)
-
- if retrieve_template is not None:
- str_completion["extra_variables_retrieve"].append(
- retrieve_template % var_infos)
- if publish_template is not None:
- str_completion["extra_variables_publish"].append(
- publish_template % var_infos)
- elif param["value"] is not None:
- param_infos = {
- "location": location_str,
- "param_name": param["name"],
- }
- if param["type"] == "boolean":
- param_infos["param_value"] = {True: "true", False: "false"}[param["value"]]
- else:
- param_infos["param_value"] = str(param["value"])
- str_completion["init_axis_params"].append(
- " __CIA402Node_%(location)s.axis->%(param_name)s = %(param_value)s;" % param_infos)
-
- for variable in variables:
- var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable))
- var_infos["location"] = location_str
- var_infos["var_size"] = self.GetSizeOfType(var_infos["var_type"])
- var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos
-
- str_completion["extern_located_variables_declaration"].append(
- "IEC_%(var_type)s *%(var_name)s;" % var_infos)
- str_completion["entry_variables"].append(
- " IEC_%(var_type)s *%(name)s;" % var_infos)
- str_completion["init_entry_variables"].append(
- " __CIA402Node_%(location)s.%(name)s = %(var_name)s;" % var_infos)
-
- self.CTNParent.FileGenerator.DeclareVariable(
- self.GetSlavePos(), var_infos["index"], var_infos["subindex"],
- var_infos["var_type"], var_infos["dir"], var_infos["var_name"])
-
- for element in ["extern_located_variables_declaration",
- "fieldbus_interface_declaration",
- "fieldbus_interface_definition",
- "entry_variables",
- "init_axis_params",
- "init_entry_variables",
- "extra_variables_retrieve",
- "extra_variables_publish"]:
- str_completion[element] = "\n".join(str_completion[element])
-
- Gen_CIA402Nodefile_path = os.path.join(buildpath, "cia402node_%s.c"%location_str)
- cia402nodefile = open(Gen_CIA402Nodefile_path, 'w')
- cia402nodefile.write(plc_cia402node_code % str_completion)
- cia402nodefile.close()
-
- return [(Gen_CIA402Nodefile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))],"",True
-
-#--------------------------------------------------
-# Ethercat MASTER
-#--------------------------------------------------
-
-EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd"))
-
-def ExtractHexDecValue(value):
- try:
- return int(value)
- except:
- pass
- try:
- return int(value.replace("#", "0"), 16)
- except:
- raise ValueError, "Invalid value for HexDecValue \"%s\"" % value
-
-def GenerateHexDecValue(value, base=10):
- if base == 10:
- return str(value)
- elif base == 16:
- return "#x%.8x" % value
- else:
- raise ValueError, "Not supported base"
-
-def sort_commands(x, y):
- if x["Index"] == y["Index"]:
- return cmp(x["Subindex"], y["Subindex"])
- return cmp(x["Index"], y["Index"])
-
-cls = EtherCATConfigClasses.get("Config_Slave", None)
-if cls:
-
- def getType(self):
- slave_info = self.getInfo()
- return {"device_type": slave_info.getName(),
- "vendor": GenerateHexDecValue(slave_info.getVendorId()),
- "product_code": GenerateHexDecValue(slave_info.getProductCode(), 16),
- "revision_number": GenerateHexDecValue(slave_info.getRevisionNo(), 16)}
- setattr(cls, "getType", getType)
-
- def setType(self, type_infos):
- slave_info = self.getInfo()
- slave_info.setName(type_infos["device_type"])
- slave_info.setVendorId(ExtractHexDecValue(type_infos["vendor"]))
- slave_info.setProductCode(ExtractHexDecValue(type_infos["product_code"]))
- slave_info.setRevisionNo(ExtractHexDecValue(type_infos["revision_number"]))
- setattr(cls, "setType", setType)
-
- def getInitCmds(self, create_default=False):
- Mailbox = self.getMailbox()
- if Mailbox is None:
- if create_default:
- self.addMailbox()
- Mailbox = self.getMailbox()
- else:
- return None
- CoE = Mailbox.getCoE()
- if CoE is None:
- if create_default:
- Mailbox.addCoE()
- CoE = Mailbox.getCoE()
- else:
- return None
- InitCmds = CoE.getInitCmds()
- if InitCmds is None and create_default:
- CoE.addInitCmds()
- InitCmds = CoE.getInitCmds()
- return InitCmds
- setattr(cls, "getInitCmds", getInitCmds)
-
- def getStartupCommands(self):
- pos = self.getInfo().getPhysAddr()
- InitCmds = self.getInitCmds()
- if InitCmds is None:
- return []
- commands = []
- for idx, InitCmd in enumerate(InitCmds.getInitCmd()):
- comment = InitCmd.getComment()
- if comment is None:
- comment = ""
- commands.append({
- "command_idx": idx,
- "Position": pos,
- "Index": InitCmd.getIndex(),
- "Subindex": InitCmd.getSubIndex(),
- "Value": InitCmd.getData(),
- "Description": comment})
- commands.sort(sort_commands)
- return commands
- setattr(cls, "getStartupCommands", getStartupCommands)
-
- def appendStartupCommand(self, command_infos):
- InitCmds = self.getInitCmds(True)
- command = EtherCATConfigClasses["InitCmds_InitCmd"]()
- command.setIndex(command_infos["Index"])
- command.setSubIndex(command_infos["Subindex"])
- command.setData(command_infos["Value"])
- command.setComment(command_infos["Description"])
- InitCmds.appendInitCmd(command)
- return len(InitCmds.getInitCmd()) - 1
- setattr(cls, "appendStartupCommand", appendStartupCommand)
-
- def setStartupCommand(self, command_infos):
- InitCmds = self.getInitCmds()
- if InitCmds is not None:
- commands = InitCmds.getInitCmd()
- if command_infos["command_idx"] < len(commands):
- command = commands[command_infos["command_idx"]]
- command.setIndex(command_infos["Index"])
- command.setSubIndex(command_infos["Subindex"])
- command.setData(command_infos["Value"])
- command.setComment(command_infos["Description"])
- setattr(cls, "setStartupCommand", setStartupCommand)
-
- def removeStartupCommand(self, command_idx):
- InitCmds = self.getInitCmds()
- if InitCmds is not None:
- if command_idx < len(InitCmds.getInitCmd()):
- InitCmds.removeInitCmd(command_idx)
- setattr(cls, "removeStartupCommand", removeStartupCommand)
-
-ProcessVariablesXSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
- <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <xsd:element name="ProcessVariables">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="variable" minOccurs="0" maxOccurs="unbounded">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="ReadFrom" type="LocationDesc" minOccurs="0"/>
- <xsd:element name="WriteTo" type="LocationDesc" minOccurs="0"/>
- </xsd:sequence>
- <xsd:attribute name="Name" type="xsd:string" use="required"/>
- <xsd:attribute name="Comment" type="xsd:string" use="required"/>
- </xsd:complexType>
- </xsd:element>
- </xsd:sequence>
- </xsd:complexType>
- </xsd:element>
- <xsd:complexType name="LocationDesc">
- <xsd:attribute name="Position" type="xsd:integer" use="required"/>
- <xsd:attribute name="Index" type="xsd:integer" use="required"/>
- <xsd:attribute name="SubIndex" type="xsd:integer" use="required"/>
- </xsd:complexType>
- </xsd:schema>
-"""
-
-ProcessVariablesClasses = GenerateClassesFromXSDstring(ProcessVariablesXSD)
-
-class _EthercatCTN:
-
- CTNChildrenTypes = [("EthercatSlave", _EthercatSlaveCTN, "Ethercat Slave")]
- if HAS_MCL:
- CTNChildrenTypes.append(("EthercatCIA402Slave", _EthercatCIA402SlaveCTN, "Ethercat CIA402 Slave"))
- EditorType = MasterEditor
-
- def __init__(self):
- config_filepath = self.ConfigFileName()
- config_is_saved = False
- self.Config = EtherCATConfigClasses["EtherCATConfig"]()
- if os.path.isfile(config_filepath):
- config_xmlfile = open(config_filepath, 'r')
- config_tree = minidom.parse(config_xmlfile)
- config_xmlfile.close()
-
- for child in config_tree.childNodes:
- if child.nodeType == config_tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig":
- self.Config.loadXMLTree(child)
- config_is_saved = True
-
- process_filepath = self.ProcessVariablesFileName()
- process_is_saved = False
- self.ProcessVariables = ProcessVariablesClasses["ProcessVariables"]()
- if os.path.isfile(process_filepath):
- process_xmlfile = open(process_filepath, 'r')
- process_tree = minidom.parse(process_xmlfile)
- process_xmlfile.close()
-
- for child in process_tree.childNodes:
- if child.nodeType == process_tree.ELEMENT_NODE and child.nodeName == "ProcessVariables":
- self.ProcessVariables.loadXMLTree(child)
- process_is_saved = True
-
- if config_is_saved and process_is_saved:
- self.CreateBuffer(True)
- else:
- self.CreateBuffer(False)
- self.OnCTNSave()
-
- def GetContextualMenuItems(self):
- return [("Add Ethercat Slave", "Add Ethercat Slave to Master", self.OnAddEthercatSlave)]
-
- def OnAddEthercatSlave(self, event):
- app_frame = self.GetCTRoot().AppFrame
- dialog = BrowseValuesLibraryDialog(app_frame,
- "Ethercat Slave Type", self.GetSlaveTypesLibrary())
- if dialog.ShowModal() == wx.ID_OK:
- type_infos = dialog.GetValueInfos()
- device, alignment = self.GetModuleInfos(type_infos)
- if device is not None:
- if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
- ConfNodeType = "EthercatCIA402Slave"
- else:
- ConfNodeType = "EthercatSlave"
- new_child = self.CTNAddChild("%s_0" % ConfNodeType, ConfNodeType)
- new_child.SetParamsAttribute("SlaveParams.Type", type_infos)
- self.CTNRequestSave()
- new_child._OpenView()
- app_frame._Refresh(TITLE, FILEMENU, PROJECTTREE)
- dialog.Destroy()
-
- def ExtractHexDecValue(self, value):
- return ExtractHexDecValue(value)
-
- def GetSizeOfType(self, type):
- return TYPECONVERSION.get(self.GetCTRoot().GetBaseType(type), None)
-
- def ConfigFileName(self):
- return os.path.join(self.CTNPath(), "config.xml")
-
- def ProcessVariablesFileName(self):
- return os.path.join(self.CTNPath(), "process_variables.xml")
-
- def FilterSlave(self, slave, vendor=None, slave_pos=None, slave_profile=None):
- if slave_pos is not None and slave.getInfo().getPhysAddr() != slave_pos:
- return False
- type_infos = slave.getType()
- if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
- return False
- device, alignment = self.GetModuleInfos(type_infos)
- if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
- return False
- return True
-
- def GetSlaves(self, vendor=None, slave_pos=None, slave_profile=None):
- slaves = []
- for slave in self.Config.getConfig().getSlave():
- if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
- slaves.append(slave.getInfo().getPhysAddr())
- slaves.sort()
- return slaves
-
- def GetSlave(self, slave_pos):
- for slave in self.Config.getConfig().getSlave():
- slave_info = slave.getInfo()
- if slave_info.getPhysAddr() == slave_pos:
- return slave
- return None
-
- def GetStartupCommands(self, vendor=None, slave_pos=None, slave_profile=None):
- commands = []
- for slave in self.Config.getConfig().getSlave():
- if self.FilterSlave(slave, vendor, slave_pos, slave_profile):
- commands.append((slave.getInfo().getPhysAddr(), slave.getStartupCommands()))
- commands.sort()
- return reduce(lambda x, y: x + y[1], commands, [])
-
- def AppendStartupCommand(self, command_infos):
- slave = self.GetSlave(command_infos["Position"])
- if slave is not None:
- command_idx = slave.appendStartupCommand(command_infos)
- self.BufferModel()
- return command_idx
- return None
-
- def SetStartupCommandInfos(self, command_infos):
- slave = self.GetSlave(command_infos["Position"])
- if slave is not None:
- slave.setStartupCommand(command_infos)
- self.BufferModel()
-
- def RemoveStartupCommand(self, slave_pos, command_idx, buffer=True):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- slave.removeStartupCommand(command_idx)
- if buffer:
- self.BufferModel()
-
- def SetProcessVariables(self, variables):
- vars = []
- for var in variables:
- variable = ProcessVariablesClasses["ProcessVariables_variable"]()
- variable.setName(var["Name"])
- variable.setComment(var["Description"])
- if var["ReadFrom"] != "":
- position, index, subindex = var["ReadFrom"]
- if variable.getReadFrom() is None:
- variable.addReadFrom()
- read_from = variable.getReadFrom()
- read_from.setPosition(position)
- read_from.setIndex(index)
- read_from.setSubIndex(subindex)
- elif variable.getReadFrom() is not None:
- variable.deleteReadFrom()
- if var["WriteTo"] != "":
- position, index, subindex = var["WriteTo"]
- if variable.getWriteTo() is None:
- variable.addWriteTo()
- write_to = variable.getWriteTo()
- write_to.setPosition(position)
- write_to.setIndex(index)
- write_to.setSubIndex(subindex)
- elif variable.getWriteTo() is not None:
- variable.deleteWriteTo()
- vars.append(variable)
- self.ProcessVariables.setvariable(vars)
- self.BufferModel()
-
- def GetProcessVariables(self):
- variables = []
- idx = 0
- for variable in self.ProcessVariables.getvariable():
- var = {"Name": variable.getName(),
- "Number": idx,
- "Description": variable.getComment()}
- read_from = variable.getReadFrom()
- if read_from is not None:
- var["ReadFrom"] = (read_from.getPosition(),
- read_from.getIndex(),
- read_from.getSubIndex())
- else:
- var["ReadFrom"] = ""
- write_to = variable.getWriteTo()
- if write_to is not None:
- var["WriteTo"] = (write_to.getPosition(),
- write_to.getIndex(),
- write_to.getSubIndex())
- else:
- var["WriteTo"] = ""
- variables.append(var)
- idx += 1
- return variables
-
- def _ScanNetwork(self):
- app_frame = self.GetCTRoot().AppFrame
-
- execute = True
- if len(self.Children) > 0:
- dialog = wx.MessageDialog(app_frame,
- _("The current network configuration will be deleted.\nDo you want to continue?"),
- _("Scan Network"),
- wx.YES_NO|wx.ICON_QUESTION)
- execute = dialog.ShowModal() == wx.ID_YES
- dialog.Destroy()
-
- if execute:
- error, returnVal = self.RemoteExec(SCAN_COMMAND, returnVal = None)
- if error != 0:
- dialog = wx.MessageDialog(app_frame, returnVal, "Error", wx.OK|wx.ICON_ERROR)
- dialog.ShowModal()
- dialog.Destroy()
- elif returnVal is not None:
- for child in self.IECSortedChildren():
- self._doRemoveChild(child)
-
- for slave in returnVal:
- type_infos = {
- "vendor": slave["vendor_id"],
- "product_code": slave["product_code"],
- "revision_number":slave["revision_number"],
- }
- device, alignment = self.GetModuleInfos(type_infos)
- if device is not None:
- if HAS_MCL and _EthercatCIA402SlaveCTN.NODE_PROFILE in device.GetProfileNumbers():
- CTNType = "EthercatCIA402Slave"
- else:
- CTNType = "EthercatSlave"
- self.CTNAddChild("slave%s" % slave["idx"], CTNType, slave["idx"])
- self.SetSlaveAlias(slave["idx"], slave["alias"])
- type_infos["device_type"] = device.getType().getcontent()
- self.SetSlaveType(slave["idx"], type_infos)
-
- def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0):
- """
- Create the confnodes that may be added as child to this node self
- @param CTNType: string desining the confnode class name (get name from CTNChildrenTypes)
- @param CTNName: string for the name of the confnode instance
- """
- newConfNodeOpj = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel)
-
- slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel())
- if slave is None:
- slave = EtherCATConfigClasses["Config_Slave"]()
- slave_infos = slave.getInfo()
- slave_infos.setName("undefined")
- slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel())
- slave_infos.setAutoIncAddr(0)
- self.Config.getConfig().appendSlave(slave)
- self.BufferModel()
- self.OnCTNSave()
-
- return newConfNodeOpj
-
- def _doRemoveChild(self, CTNInstance):
- slave_pos = CTNInstance.GetSlavePos()
- config = self.Config.getConfig()
- for idx, slave in enumerate(config.getSlave()):
- slave_infos = slave.getInfo()
- if slave_infos.getPhysAddr() == slave_pos:
- config.removeSlave(idx)
- self.BufferModel()
- self.OnCTNSave()
- ConfigTreeNode._doRemoveChild(self, CTNInstance)
-
- def SetSlavePosition(self, slave_pos, new_pos):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- slave_info = slave.getInfo()
- slave_info.setPhysAddr(new_pos)
- for variable in self.ProcessVariables.getvariable():
- read_from = variable.getReadFrom()
- if read_from is not None and read_from.getPosition() == slave_pos:
- read_from.setPosition(new_pos)
- write_to = variable.getWriteTo()
- if write_to is not None and write_to.getPosition() == slave_pos:
- write_to.setPosition(new_pos)
- self.CreateBuffer(True)
- self.OnCTNSave()
- if self._View is not None:
- self._View.RefreshView()
- self._View.RefreshBuffer()
-
- def GetSlaveAlias(self, slave_pos):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- slave_info = slave.getInfo()
- return slave_info.getAutoIncAddr()
- return None
-
- def SetSlaveAlias(self, slave_pos, alias):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- slave_info = slave.getInfo()
- slave_info.setAutoIncAddr(alias)
- self.BufferModel()
-
- def GetSlaveType(self, slave_pos):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- return slave.getType()
- return None
-
- def SetSlaveType(self, slave_pos, type_infos):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- slave.setType(type_infos)
- self.BufferModel()
-
- def GetSlaveInfos(self, slave_pos):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- type_infos = slave.getType()
- device, alignment = self.GetModuleInfos(type_infos)
- if device is not None:
- infos = type_infos.copy()
- infos.update({"physics": device.getPhysics(),
- "sync_managers": device.GetSyncManagers(),
- "entries": self.GetSlaveVariables(device)})
- return infos
- return None
-
- def GetSlaveVariables(self, slave_pos=None, limits=None, device=None):
- if device is None and slave_pos is not None:
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- type_infos = slave.getType()
- device, alignment = self.GetModuleInfos(type_infos)
- if device is not None:
- entries = device.GetEntriesList(limits)
- entries_list = entries.items()
- entries_list.sort()
- entries = []
- current_index = None
- current_entry = None
- for (index, subindex), entry in entries_list:
- entry["children"] = []
- if slave_pos is not None:
- entry["Position"] = str(slave_pos)
- entry
- if index != current_index:
- current_index = index
- current_entry = entry
- entries.append(entry)
- elif current_entry is not None:
- current_entry["children"].append(entry)
- else:
- entries.append(entry)
- return entries
- return []
-
- def GetSlaveVariableDataType(self, slave_pos, index, subindex):
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- device, alignment = self.GetModuleInfos(slave.getType())
- if device is not None:
- entries = device.GetEntriesList()
- entry_infos = entries.get((index, subindex))
- if entry_infos is not None:
- return entry_infos["Type"]
- return None
-
- def GetNodesVariables(self, vendor=None, slave_pos=None, slave_profile=None, limits=None):
- entries = []
- for slave_position in self.GetSlaves():
- if slave_pos is not None and slave_position != slave_pos:
- continue
- slave = self.GetSlave(slave_position)
- type_infos = slave.getType()
- if vendor is not None and ExtractHexDecValue(type_infos["vendor"]) != vendor:
- continue
- device, alignment = self.GetModuleInfos(type_infos)
- if slave_profile is not None and slave_profile not in device.GetProfileNumbers():
- continue
- entries.extend(self.GetSlaveVariables(slave_position, limits, device))
- return entries
-
- def GetModuleInfos(self, type_infos):
- return self.CTNParent.GetModuleInfos(type_infos)
-
- def GetSlaveTypesLibrary(self, profile_filter=None):
- return self.CTNParent.GetModulesLibrary(profile_filter)
-
- def GetLibraryVendors(self):
- return self.CTNParent.GetVendors()
-
- def GetDeviceLocationTree(self, slave_pos, current_location, device_name):
- slave = self.GetSlave(slave_pos)
- vars = []
- if slave is not None:
- type_infos = slave.getType()
-
- device, alignment = self.GetModuleInfos(type_infos)
- if device is not None:
- sync_managers = []
- for sync_manager in device.getSm():
- sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
- sync_manager_direction = sync_manager_control_byte & 0x0c
- if sync_manager_direction:
- sync_managers.append(LOCATION_VAR_OUTPUT)
- else:
- sync_managers.append(LOCATION_VAR_INPUT)
-
- entries = device.GetEntriesList().items()
- entries.sort()
- for (index, subindex), entry in entries:
- var_size = self.GetSizeOfType(entry["Type"])
- if var_size is not None:
- var_class = VARCLASSCONVERSION.get(entry["PDOMapping"], None)
- if var_class is not None:
- if var_class == LOCATION_VAR_INPUT:
- var_dir = "%I"
- else:
- var_dir = "%Q"
-
- vars.append({"name": "0x%4.4x-0x%2.2x: %s" % (index, subindex, entry["Name"]),
- "type": var_class,
- "size": var_size,
- "IEC_type": entry["Type"],
- "var_name": "%s_%4.4x_%2.2x" % ("_".join(device_name.split()), index, subindex),
- "location": "%s%s%s"%(var_dir, var_size, ".".join(map(str, current_location +
- (index, subindex)))),
- "description": "",
- "children": []})
-
- return vars
-
- def CTNTestModified(self):
- return self.ChangesToSave or not self.ModelIsSaved()
-
- def OnCTNSave(self):
- config_filepath = self.ConfigFileName()
-
- config_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
- config_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
- "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"}
- config_text += self.Config.generateXMLText("EtherCATConfig", 0, config_extras)
-
- config_xmlfile = open(config_filepath,"w")
- config_xmlfile.write(config_text.encode("utf-8"))
- config_xmlfile.close()
-
- process_filepath = self.ProcessVariablesFileName()
-
- process_text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
- process_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"}
- process_text += self.ProcessVariables.generateXMLText("ProcessVariables", 0, process_extras)
-
- process_xmlfile = open(process_filepath,"w")
- process_xmlfile.write(process_text.encode("utf-8"))
- process_xmlfile.close()
-
- self.Buffer.CurrentSaved()
- return True
-
- def GetProcessVariableName(self, location, var_type):
- return "__M%s_%s" % (self.GetSizeOfType(var_type), "_".join(map(str, location)))
-
- def _Generate_C(self, buildpath, locations):
- current_location = self.GetCurrentLocation()
- # define a unique name for the generated C file
- location_str = "_".join(map(lambda x:str(x), current_location))
-
- Gen_Ethercatfile_path = os.path.join(buildpath, "ethercat_%s.c"%location_str)
-
- self.FileGenerator = _EthercatCFileGenerator(self)
-
- LocationCFilesAndCFLAGS, LDFLAGS, extra_files = ConfigTreeNode._Generate_C(self, buildpath, locations)
-
- for idx, variable in enumerate(self.ProcessVariables.getvariable()):
- name = None
- var_type = None
- read_from = variable.getReadFrom()
- write_to = variable.getWriteTo()
- if read_from is not None:
- pos = read_from.getPosition()
- index = read_from.getIndex()
- subindex = read_from.getSubIndex()
- location = current_location + (idx, )
- var_type = self.GetSlaveVariableDataType(pos, index, subindex)
- name = self.FileGenerator.DeclareVariable(
- pos, index, subindex, var_type, "I",
- self.GetProcessVariableName(location, var_type))
- if write_to is not None:
- pos = write_to.getPosition()
- index = write_to.getIndex()
- subindex = write_to.getSubIndex()
- if name is None:
- location = current_location + (idx, )
- var_type = self.GetSlaveVariableDataType(pos, index, subindex)
- name = self.GetProcessVariableName(location, var_type)
- self.FileGenerator.DeclareVariable(
- pos, index, subindex, var_type, "Q", name, True)
-
- self.FileGenerator.GenerateCFile(Gen_Ethercatfile_path, location_str, self.BaseParams.getIEC_Channel())
-
- LocationCFilesAndCFLAGS.append(
- (current_location,
- [(Gen_Ethercatfile_path, '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()))],
- True))
- LDFLAGS.append("-lethercat -lrtdm")
-
- return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
-
- ConfNodeMethods = [
- {"bitmap" : "ScanNetwork",
- "name" : _("Scan Network"),
- "tooltip" : _("Scan Network"),
- "method" : "_ScanNetwork"},
- ]
-
- def CTNGenerate_C(self, buildpath, locations):
- current_location = self.GetCurrentLocation()
-
- slaves = self.GetSlaves()
- for slave_pos in slaves:
- slave = self.GetSlave(slave_pos)
- if slave is not None:
- self.FileGenerator.DeclareSlave(slave_pos, slave)
-
- for location in locations:
- loc = location["LOC"][len(current_location):]
- slave_pos = loc[0]
- if slave_pos in slaves and len(loc) == 3 and location["DIR"] != "M":
- self.FileGenerator.DeclareVariable(
- slave_pos, loc[1], loc[2], location["IEC_TYPE"], location["DIR"], location["NAME"])
-
- return [],"",False
-
-#-------------------------------------------------------------------------------
-# Current Buffering Management Functions
-#-------------------------------------------------------------------------------
-
- """
- Return a copy of the config
- """
- def Copy(self, model):
- return cPickle.loads(cPickle.dumps(model))
-
- def CreateBuffer(self, saved):
- self.Buffer = UndoBuffer(cPickle.dumps((self.Config, self.ProcessVariables)), saved)
-
- def BufferModel(self):
- self.Buffer.Buffering(cPickle.dumps((self.Config, self.ProcessVariables)))
-
- def ModelIsSaved(self):
- if self.Buffer is not None:
- return self.Buffer.IsCurrentSaved()
- else:
- return True
-
- def LoadPrevious(self):
- self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Previous())
-
- def LoadNext(self):
- self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Next())
-
- def GetBufferState(self):
- first = self.Buffer.IsFirst()
- last = self.Buffer.IsLast()
- return not first, not last
-
-
-SLAVE_PDOS_CONFIGURATION_DECLARATION = """
-/* Slave %(slave)d, "%(device_type)s"
- * Vendor ID: 0x%(vendor).8x
- * Product code: 0x%(product_code).8x
- * Revision number: 0x%(revision_number).8x
- */
-
-ec_pdo_entry_info_t slave_%(slave)d_pdo_entries[] = {
-%(pdos_entries_infos)s
-};
-
-ec_pdo_info_t slave_%(slave)d_pdos[] = {
-%(pdos_infos)s
-};
-
-ec_sync_info_t slave_%(slave)d_syncs[] = {
-%(pdos_sync_infos)s
- {0xff}
-};
-"""
-
-SLAVE_CONFIGURATION_TEMPLATE = """
- if (!(slave%(slave)d = ecrt_master_slave_config(master, %(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x))) {
- SLOGF(LOG_CRITICAL, "Failed to get slave %(device_type)s configuration at alias %(alias)d and position %(position)d.\\n");
- return -1;
- }
-
- if (ecrt_slave_config_pdos(slave%(slave)d, EC_END, slave_%(slave)d_syncs)) {
- SLOGF(LOG_CRITICAL, "Failed to configure PDOs for slave %(device_type)s at alias %(alias)d and position %(position)d.\\n");
- return -1;
- }
-"""
-
-SLAVE_INITIALIZATION_TEMPLATE = """
- {
- uint8_t value[%(data_size)d];
- EC_WRITE_%(data_type)s((uint8_t *)value, %(data)s);
- if (ecrt_master_sdo_download(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &abort_code)) {
- SLOGF(LOG_CRITICAL, "Failed to initialize slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%d\\n", abort_code);
- return -1;
- }
- }
-"""
-
-SLAVE_OUTPUT_PDO_DEFAULT_VALUE = """
- {
- uint8_t value[%(data_size)d];
- if (ecrt_master_sdo_upload(master, %(slave)d, 0x%(index).4x, 0x%(subindex).2x, (uint8_t *)value, %(data_size)d, &result_size, &abort_code)) {
- SLOGF(LOG_CRITICAL, "Failed to get default value for output PDO in slave %(device_type)s at alias %(alias)d and position %(position)d.\\nError: %%ud\\n", abort_code);
- return -1;
- }
- %(real_var)s = EC_READ_%(data_type)s((uint8_t *)value);
- }
-"""
-
-def ConfigureVariable(entry_infos, str_completion):
- entry_infos["data_type"] = DATATYPECONVERSION.get(entry_infos["var_type"], None)
- if entry_infos["data_type"] is None:
- raise ValueError, _("Type of location \"%s\" not yet supported!") % entry_infos["var_name"]
-
- if not entry_infos.get("no_decl", False):
- if entry_infos.has_key("real_var"):
- str_completion["located_variables_declaration"].append(
- "IEC_%(var_type)s %(real_var)s;" % entry_infos)
- else:
- entry_infos["real_var"] = "beremiz" + entry_infos["var_name"]
- str_completion["located_variables_declaration"].extend(
- ["IEC_%(var_type)s %(real_var)s;" % entry_infos,
- "IEC_%(var_type)s *%(var_name)s = &%(real_var)s;" % entry_infos])
- for declaration in entry_infos.get("extra_declarations", []):
- entry_infos["extra_decl"] = declaration
- str_completion["located_variables_declaration"].append(
- "IEC_%(var_type)s *%(extra_decl)s = &%(real_var)s;" % entry_infos)
- elif not entry_infos.has_key("real_var"):
- entry_infos["real_var"] = "beremiz" + entry_infos["var_name"]
-
- str_completion["used_pdo_entry_offset_variables_declaration"].append(
- "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x;" % entry_infos)
-
- if entry_infos["data_type"] == "BIT":
- str_completion["used_pdo_entry_offset_variables_declaration"].append(
- "unsigned int slave%(slave)d_%(index).4x_%(subindex).2x_bit;" % entry_infos)
-
- str_completion["used_pdo_entry_configuration"].append(
- (" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, " +
- "0x%(index).4x, %(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x, " +
- "&slave%(slave)d_%(index).4x_%(subindex).2x_bit},") % entry_infos)
-
- if entry_infos["dir"] == "I":
- str_completion["retrieve_variables"].append(
- (" %(real_var)s = EC_READ_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " +
- "slave%(slave)d_%(index).4x_%(subindex).2x_bit);") % entry_infos)
- elif entry_infos["dir"] == "Q":
- str_completion["publish_variables"].append(
- (" EC_WRITE_BIT(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " +
- "slave%(slave)d_%(index).4x_%(subindex).2x_bit, %(real_var)s);") % entry_infos)
-
- else:
- str_completion["used_pdo_entry_configuration"].append(
- (" {%(alias)d, %(position)d, 0x%(vendor).8x, 0x%(product_code).8x, 0x%(index).4x, " +
- "%(subindex)d, &slave%(slave)d_%(index).4x_%(subindex).2x},") % entry_infos)
-
- if entry_infos["dir"] == "I":
- str_completion["retrieve_variables"].append(
- (" %(real_var)s = EC_READ_%(data_type)s(domain1_pd + " +
- "slave%(slave)d_%(index).4x_%(subindex).2x);") % entry_infos)
- elif entry_infos["dir"] == "Q":
- str_completion["publish_variables"].append(
- (" EC_WRITE_%(data_type)s(domain1_pd + slave%(slave)d_%(index).4x_%(subindex).2x, " +
- "%(real_var)s);") % entry_infos)
-
-def ExclusionSortFunction(x, y):
- if x["matching"] == y["matching"]:
- if x["assigned"] and not y["assigned"]:
- return -1
- elif not x["assigned"] and y["assigned"]:
- return 1
- return cmp(x["count"], y["count"])
- return -cmp(x["matching"], y["matching"])
-
-class _EthercatCFileGenerator:
-
- def __init__(self, controler):
- self.Controler = controler
-
- self.Slaves = []
- self.UsedVariables = {}
-
- def __del__(self):
- self.Controler = None
-
- def DeclareSlave(self, slave_index, slave):
- self.Slaves.append((slave_index, slave.getInfo().getAutoIncAddr(), slave))
-
- def DeclareVariable(self, slave_index, index, subindex, iec_type, dir, name, no_decl=False):
- slave_variables = self.UsedVariables.setdefault(slave_index, {})
-
- entry_infos = slave_variables.get((index, subindex), None)
- if entry_infos is None:
- slave_variables[(index, subindex)] = {
- "infos": (iec_type, dir, name, no_decl, []),
- "mapped": False}
- return name
- elif entry_infos["infos"][:2] == (iec_type, dir):
- if name != entry_infos["infos"][2]:
- if dir == "I":
- entry_infos["infos"][3].append(name)
- return entry_infos["infos"][2]
- else:
- raise ValueError, _("Output variables can't be defined with different locations (%s and %s)") % (entry_infos["infos"][2], name)
- else:
- raise ValueError, _("Definition conflict for location \"%s\"") % name
-
- def GenerateCFile(self, filepath, location_str, master_number):
-
- # Extract etherlab master code template
- plc_etherlab_filepath = os.path.join(os.path.split(__file__)[0], "plc_etherlab.c")
- plc_etherlab_file = open(plc_etherlab_filepath, 'r')
- plc_etherlab_code = plc_etherlab_file.read()
- plc_etherlab_file.close()
-
- # Initialize strings for formatting master code template
- str_completion = {
- "location": location_str,
- "master_number": master_number,
- "located_variables_declaration": [],
- "used_pdo_entry_offset_variables_declaration": [],
- "used_pdo_entry_configuration": [],
- "pdos_configuration_declaration": "",
- "slaves_declaration": "",
- "slaves_configuration": "",
- "slaves_output_pdos_default_values_extraction": "",
- "slaves_initialization": "",
- "retrieve_variables": [],
- "publish_variables": [],
- }
-
- # Initialize variable storing variable mapping state
- for slave_entries in self.UsedVariables.itervalues():
- for entry_infos in slave_entries.itervalues():
- entry_infos["mapped"] = False
-
- # Sort slaves by position (IEC_Channel)
- self.Slaves.sort()
- # Initialize dictionary storing alias auto-increment position values
- alias = {}
-
- # Generating code for each slave
- for (slave_idx, slave_alias, slave) in self.Slaves:
- type_infos = slave.getType()
-
- # Defining slave alias and auto-increment position
- if alias.get(slave_alias) is not None:
- alias[slave_alias] += 1
- else:
- alias[slave_alias] = 0
- slave_pos = (slave_alias, alias[slave_alias])
-
- # Extract slave device informations
- device, alignment = self.Controler.GetModuleInfos(type_infos)
- if device is not None:
-
- # Extract slaves variables to be mapped
- slave_variables = self.UsedVariables.get(slave_idx, {})
-
- # Extract slave device object dictionary entries
- device_entries = device.GetEntriesList()
-
- # Adding code for declaring slave in master code template strings
- for element in ["vendor", "product_code", "revision_number"]:
- type_infos[element] = ExtractHexDecValue(type_infos[element])
- type_infos.update(dict(zip(["slave", "alias", "position"], (slave_idx,) + slave_pos)))
-
- # Extract slave device CoE informations
- device_coe = device.getCoE()
- if device_coe is not None:
-
- # If device support CanOpen over Ethernet, adding code for calling
- # init commands when initializing slave in master code template strings
- initCmds = []
- for initCmd in device_coe.getInitCmd():
- initCmds.append({
- "Index": ExtractHexDecValue(initCmd.getIndex()),
- "Subindex": ExtractHexDecValue(initCmd.getSubIndex()),
- "Value": initCmd.getData().getcontent()})
- initCmds.extend(slave.getStartupCommands())
- for initCmd in initCmds:
- index = initCmd["Index"]
- subindex = initCmd["Subindex"]
- entry = device_entries.get((index, subindex), None)
- if entry is not None:
- data_size = entry["BitSize"] / 8
- data_str = ("0x%%.%dx" % (data_size * 2)) % initCmd["Value"]
- init_cmd_infos = {
- "index": index,
- "subindex": subindex,
- "data": data_str,
- "data_type": DATATYPECONVERSION.get(entry["Type"]),
- "data_size": data_size
- }
- init_cmd_infos.update(type_infos)
- str_completion["slaves_initialization"] += SLAVE_INITIALIZATION_TEMPLATE % init_cmd_infos
-
- # Extract slave device PDO configuration capabilities
- PdoAssign = device_coe.getPdoAssign()
- PdoConfig = device_coe.getPdoConfig()
- else:
- PdoAssign = PdoConfig = False
-
- # Test if slave has a configuration or need one
- if len(device.getTxPdo() + device.getRxPdo()) > 0 or len(slave_variables) > 0 and PdoConfig and PdoAssign:
-
- str_completion["slaves_declaration"] += "static ec_slave_config_t *slave%(slave)d = NULL;\n" % type_infos
- str_completion["slaves_configuration"] += SLAVE_CONFIGURATION_TEMPLATE % type_infos
-
- # Initializing
- pdos_infos = {
- "pdos_entries_infos": [],
- "pdos_infos": [],
- "pdos_sync_infos": [],
- }
- pdos_infos.update(type_infos)
-
- sync_managers = []
- for sync_manager_idx, sync_manager in enumerate(device.getSm()):
- sync_manager_infos = {
- "index": sync_manager_idx,
- "name": sync_manager.getcontent(),
- "slave": slave_idx,
- "pdos": [],
- "pdos_number": 0,
- }
-
- sync_manager_control_byte = ExtractHexDecValue(sync_manager.getControlByte())
- sync_manager_direction = sync_manager_control_byte & 0x0c
- sync_manager_watchdog = sync_manager_control_byte & 0x40
- if sync_manager_direction:
- sync_manager_infos["sync_manager_type"] = "EC_DIR_OUTPUT"
- else:
- sync_manager_infos["sync_manager_type"] = "EC_DIR_INPUT"
- if sync_manager_watchdog:
- sync_manager_infos["watchdog"] = "EC_WD_ENABLE"
- else:
- sync_manager_infos["watchdog"] = "EC_WD_DISABLE"
-
- sync_managers.append(sync_manager_infos)
-
- pdos_index = []
- exclusive_pdos = {}
- selected_pdos = []
- for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
- [(pdo, "Outputs") for pdo in device.getRxPdo()]):
-
- pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
- pdos_index.append(pdo_index)
-
- excluded_list = pdo.getExclude()
- if len(excluded_list) > 0:
- exclusion_list = [pdo_index]
- for excluded in excluded_list:
- exclusion_list.append(ExtractHexDecValue(excluded.getcontent()))
- exclusion_list.sort()
-
- exclusion_scope = exclusive_pdos.setdefault(tuple(exclusion_list), [])
-
- entries = pdo.getEntry()
- pdo_mapping_match = {
- "index": pdo_index,
- "matching": 0,
- "count": len(entries),
- "assigned": pdo.getSm() is not None
- }
- exclusion_scope.append(pdo_mapping_match)
-
- for entry in entries:
- index = ExtractHexDecValue(entry.getIndex().getcontent())
- subindex = ExtractHexDecValue(entry.getSubIndex())
- if slave_variables.get((index, subindex), None) is not None:
- pdo_mapping_match["matching"] += 1
-
- elif pdo.getMandatory():
- selected_pdos.append(pdo_index)
-
- excluded_pdos = []
- for exclusion_scope in exclusive_pdos.itervalues():
- exclusion_scope.sort(ExclusionSortFunction)
- start_excluding_index = 0
- if exclusion_scope[0]["matching"] > 0:
- selected_pdos.append(exclusion_scope[0]["index"])
- start_excluding_index = 1
- excluded_pdos.extend([pdo["index"] for pdo in exclusion_scope[start_excluding_index:] if PdoAssign or not pdo["assigned"]])
-
- for pdo, pdo_type in ([(pdo, "Inputs") for pdo in device.getTxPdo()] +
- [(pdo, "Outputs") for pdo in device.getRxPdo()]):
- entries = pdo.getEntry()
-
- pdo_index = ExtractHexDecValue(pdo.getIndex().getcontent())
- if pdo_index in excluded_pdos:
- continue
-
- pdo_needed = pdo_index in selected_pdos
-
- entries_infos = []
-
- for entry in entries:
- index = ExtractHexDecValue(entry.getIndex().getcontent())
- subindex = ExtractHexDecValue(entry.getSubIndex())
- entry_infos = {
- "index": index,
- "subindex": subindex,
- "name": ExtractName(entry.getName()),
- "bitlen": entry.getBitLen(),
- }
- entry_infos.update(type_infos)
- entries_infos.append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
-
- entry_declaration = slave_variables.get((index, subindex), None)
- if entry_declaration is not None and not entry_declaration["mapped"]:
- pdo_needed = True
-
- entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"],
- entry_declaration["infos"])))
- entry_declaration["mapped"] = True
-
- entry_type = entry.getDataType().getcontent()
- if entry_infos["var_type"] != entry_type:
- message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"]
- if (self.Controler.GetSizeOfType(entry_infos["var_type"]) !=
- self.Controler.GetSizeOfType(entry_type)):
- raise ValueError, message
- else:
- self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
-
- if (entry_infos["dir"] == "I" and pdo_type != "Inputs" or
- entry_infos["dir"] == "Q" and pdo_type != "Outputs"):
- raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"]
-
- ConfigureVariable(entry_infos, str_completion)
-
- elif pdo_type == "Outputs" and entry.getDataType() is not None and device_coe is not None:
- data_type = entry.getDataType().getcontent()
- entry_infos["dir"] = "Q"
- entry_infos["data_size"] = max(1, entry_infos["bitlen"] / 8)
- entry_infos["data_type"] = DATATYPECONVERSION.get(data_type)
- entry_infos["var_type"] = data_type
- entry_infos["real_var"] = "slave%(slave)d_%(index).4x_%(subindex).2x_default" % entry_infos
-
- ConfigureVariable(entry_infos, str_completion)
-
- str_completion["slaves_output_pdos_default_values_extraction"] += \
- SLAVE_OUTPUT_PDO_DEFAULT_VALUE % entry_infos
-
- if pdo_needed:
- for excluded in pdo.getExclude():
- excluded_index = ExtractHexDecValue(excluded.getcontent())
- if excluded_index not in excluded_pdos:
- excluded_pdos.append(excluded_index)
-
- sm = pdo.getSm()
- if sm is None:
- for sm_idx, sync_manager in enumerate(sync_managers):
- if sync_manager["name"] == pdo_type:
- sm = sm_idx
- if sm is None:
- raise ValueError, _("No sync manager available for %s pdo!") % pdo_type
-
- sync_managers[sm]["pdos_number"] += 1
- sync_managers[sm]["pdos"].append(
- {"slave": slave_idx,
- "index": pdo_index,
- "name": ExtractName(pdo.getName()),
- "type": pdo_type,
- "entries": entries_infos,
- "entries_number": len(entries_infos),
- "fixed": pdo.getFixed() == True})
-
- if PdoConfig and PdoAssign:
- dynamic_pdos = {}
- dynamic_pdos_number = 0
- for category, min_index, max_index in [("Inputs", 0x1600, 0x1800),
- ("Outputs", 0x1a00, 0x1C00)]:
- for sync_manager in sync_managers:
- if sync_manager["name"] == category:
- category_infos = dynamic_pdos.setdefault(category, {})
- category_infos["sync_manager"] = sync_manager
- category_infos["pdos"] = [pdo for pdo in category_infos["sync_manager"]["pdos"]
- if not pdo["fixed"] and pdo["type"] == category]
- category_infos["current_index"] = min_index
- category_infos["max_index"] = max_index
- break
-
- for (index, subindex), entry_declaration in slave_variables.iteritems():
-
- if not entry_declaration["mapped"]:
- entry = device_entries.get((index, subindex), None)
- if entry is None:
- raise ValueError, _("Unknown entry index 0x%4.4x, subindex 0x%2.2x for device %s") % \
- (index, subindex, type_infos["device_type"])
-
- entry_infos = {
- "index": index,
- "subindex": subindex,
- "name": entry["Name"],
- "bitlen": entry["BitSize"],
- }
- entry_infos.update(type_infos)
-
- entry_infos.update(dict(zip(["var_type", "dir", "var_name", "no_decl", "extra_declarations"],
- entry_declaration["infos"])))
- entry_declaration["mapped"] = True
-
- if entry_infos["var_type"] != entry["Type"]:
- message = _("Wrong type for location \"%s\"!") % entry_infos["var_name"]
- if (self.Controler.GetSizeOfType(entry_infos["var_type"]) !=
- self.Controler.GetSizeOfType(entry["Type"])):
- raise ValueError, message
- else:
- self.Controler.GetCTRoot().logger.write_warning(message + "\n")
-
- if entry_infos["dir"] == "I" and entry["PDOMapping"] in ["T", "RT"]:
- pdo_type = "Inputs"
- elif entry_infos["dir"] == "Q" and entry["PDOMapping"] in ["R", "RT"]:
- pdo_type = "Outputs"
- else:
- raise ValueError, _("Wrong direction for location \"%s\"!") % entry_infos["var_name"]
-
- if not dynamic_pdos.has_key(pdo_type):
- raise ValueError, _("No Sync manager defined for %s!") % pdo_type
-
- ConfigureVariable(entry_infos, str_completion)
-
- if len(dynamic_pdos[pdo_type]["pdos"]) > 0:
- pdo = dynamic_pdos[pdo_type]["pdos"][0]
- else:
- while dynamic_pdos[pdo_type]["current_index"] in pdos_index:
- dynamic_pdos[pdo_type]["current_index"] += 1
- if dynamic_pdos[pdo_type]["current_index"] >= dynamic_pdos[pdo_type]["max_index"]:
- raise ValueError, _("No more free PDO index available for %s!") % pdo_type
- pdos_index.append(dynamic_pdos[pdo_type]["current_index"])
-
- dynamic_pdos_number += 1
- pdo = {"slave": slave_idx,
- "index": dynamic_pdos[pdo_type]["current_index"],
- "name": "Dynamic PDO %d" % dynamic_pdos_number,
- "type": pdo_type,
- "entries": [],
- "entries_number": 0,
- "fixed": False}
- dynamic_pdos[pdo_type]["sync_manager"]["pdos_number"] += 1
- dynamic_pdos[pdo_type]["sync_manager"]["pdos"].append(pdo)
- dynamic_pdos[pdo_type]["pdos"].append(pdo)
-
- pdo["entries"].append(" {0x%(index).4x, 0x%(subindex).2x, %(bitlen)d}, /* %(name)s */" % entry_infos)
- if entry_infos["bitlen"] < alignment:
- print (alignment, entry_infos["bitlen"])
- pdo["entries"].append(" {0x0000, 0x00, %d}, /* None */" % (alignment - entry_infos["bitlen"]))
- pdo["entries_number"] += 1
-
- if pdo["entries_number"] == 255:
- dynamic_pdos[pdo_type]["pdos"].pop(0)
-
- pdo_offset = 0
- entry_offset = 0
- for sync_manager_infos in sync_managers:
-
- for pdo_infos in sync_manager_infos["pdos"]:
- pdo_infos["offset"] = entry_offset
- pdo_entries = pdo_infos["entries"]
- pdos_infos["pdos_infos"].append(
- (" {0x%(index).4x, %(entries_number)d, " +
- "slave_%(slave)d_pdo_entries + %(offset)d}, /* %(name)s */") % pdo_infos)
- entry_offset += len(pdo_entries)
- pdos_infos["pdos_entries_infos"].extend(pdo_entries)
-
- sync_manager_infos["offset"] = pdo_offset
- pdo_offset_shift = sync_manager_infos["pdos_number"]
- pdos_infos["pdos_sync_infos"].append(
- (" {%(index)d, %(sync_manager_type)s, %(pdos_number)d, " +
- ("slave_%(slave)d_pdos + %(offset)d" if pdo_offset_shift else "NULL") +
- ", %(watchdog)s},") % sync_manager_infos)
- pdo_offset += pdo_offset_shift
-
- for element in ["pdos_entries_infos", "pdos_infos", "pdos_sync_infos"]:
- pdos_infos[element] = "\n".join(pdos_infos[element])
-
- str_completion["pdos_configuration_declaration"] += SLAVE_PDOS_CONFIGURATION_DECLARATION % pdos_infos
-
- for (index, subindex), entry_declaration in slave_variables.iteritems():
- if not entry_declaration["mapped"]:
- message = _("Entry index 0x%4.4x, subindex 0x%2.2x not mapped for device %s") % \
- (index, subindex, type_infos["device_type"])
- self.Controler.GetCTRoot().logger.write_warning(_("Warning: ") + message + "\n")
-
- for element in ["used_pdo_entry_offset_variables_declaration",
- "used_pdo_entry_configuration",
- "located_variables_declaration",
- "retrieve_variables",
- "publish_variables"]:
- str_completion[element] = "\n".join(str_completion[element])
-
- etherlabfile = open(filepath, 'w')
- etherlabfile.write(plc_etherlab_code % str_completion)
- etherlabfile.close()
+
+from EthercatSlave import ExtractHexDecValue, ExtractName
+from EthercatMaster import _EthercatCTN
+from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
#--------------------------------------------------
# Ethercat ConfNode
@@ -1860,15 +200,6 @@
SortGroupItems(item)
group["children"].sort(GroupItemCompare)
-def ExtractName(names, default=None):
- if len(names) == 1:
- return names[0].getcontent()
- else:
- for name in names:
- if name.getLcId() == 1033:
- return name.getcontent()
- return default
-
def ExtractPdoInfos(pdo, pdo_type, entries, limits=None):
pdo_index = pdo.getIndex().getcontent()
pdo_name = ExtractName(pdo.getName())
--- a/etherlab/extension.py Fri Mar 29 01:13:17 2013 +0100
+++ b/etherlab/extension.py Fri Mar 29 01:15:01 2013 +0100
@@ -1,7 +1,7 @@
import features
def GetEtherLabLibClass():
- from etherlab import EtherlabLibrary
+ from EthercatMaster import EtherlabLibrary
return EtherlabLibrary
features.libraries.append(