edouard@2165: #!/usr/bin/env python edouard@2165: # -*- coding: utf-8 -*- edouard@2165: edouard@2165: # This file is part of Beremiz edouard@2165: # edouard@2165: # Copyright (C) 2011-2014: Laurent BESSARD, Edouard TISSERANT edouard@2165: # RTES Lab : CRKim, JBLee, youcu edouard@2165: # Higen Motor : Donggu Kang edouard@2165: # edouard@2165: # See COPYING file for copyrights details. edouard@2165: laurent@2022: import os, shutil Laurent@2157: from lxml import etree laurent@2022: laurent@2022: import wx Laurent@2097: import csv laurent@2022: laurent@2022: from xmlclass import * Laurent@2111: Laurent@2160: from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage Edouard@2048: from PLCControler import UndoBuffer, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY Laurent@2111: Laurent@2111: from EthercatSlave import ExtractHexDecValue, ExtractName Laurent@2111: from EthercatMaster import _EthercatCTN Laurent@2111: from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE laurent@2022: Laurent@2157: ScriptDirectory = os.path.split(os.path.realpath(__file__))[0] Laurent@2157: laurent@2022: #-------------------------------------------------- Edouard@2048: # Ethercat ConfNode laurent@2022: #-------------------------------------------------- laurent@2022: Laurent@2157: EtherCATInfoParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) Laurent@2157: EtherCATInfo_XPath = lambda xpath: etree.XPath(xpath) Laurent@2157: Laurent@2162: def HexDecValue(context, *args): Laurent@2162: return str(ExtractHexDecValue(args[0][0])) Laurent@2162: Laurent@2162: def EntryName(context, *args): Laurent@2162: return ExtractName(args[0], Laurent@2162: args[1][0] if len(args) > 1 else None) Laurent@2162: Laurent@2162: ENTRY_INFOS_KEYS = [ Laurent@2162: ("Index", lambda x: "#x%4.4X" % int(x), "#x0000"), Laurent@2162: ("SubIndex", str, "0"), Laurent@2162: ("Name", str, ""), Laurent@2162: ("Type", str, ""), Laurent@2162: ("BitSize", int, 0), Laurent@2162: ("Access", str, ""), Laurent@2162: ("PDOMapping", str, ""), Laurent@2162: ("PDO index", str, ""), Laurent@2162: ("PDO name", str, ""), Laurent@2162: ("PDO type", str, "")] Laurent@2162: Laurent@2162: class EntryListFactory: Laurent@2157: Laurent@2157: def __init__(self, entries): Laurent@2157: self.Entries = entries Laurent@2157: Laurent@2162: def AddEntry(self, context, *args): Laurent@2162: index, subindex = map(lambda x: int(x[0]), args[:2]) Laurent@2157: new_entry_infos = { Laurent@2162: key: translate(arg[0]) if len(arg) > 0 else default Laurent@2162: for (key, translate, default), arg Laurent@2162: in zip(ENTRY_INFOS_KEYS, args)} Laurent@2162: Laurent@2157: if (index, subindex) != (0, 0): Laurent@2157: entry_infos = self.Entries.get((index, subindex)) Laurent@2157: if entry_infos is not None: Laurent@2162: for param in ["PDO index", "PDO name", "PDO type"]: Laurent@2162: value = new_entry_infos.get(param) Laurent@2162: if value is not None: Laurent@2162: entry_infos[param] = value Laurent@2157: else: Laurent@2157: self.Entries[(index, subindex)] = new_entry_infos Laurent@2157: Laurent@2157: entries_list_xslt = etree.parse( Laurent@2157: os.path.join(ScriptDirectory, "entries_list.xslt")) Laurent@2157: Laurent@2157: cls = EtherCATInfoParser.GetElementClass("DeviceType") Laurent@2074: if cls: Laurent@2157: Laurent@2157: profile_numbers_xpath = EtherCATInfo_XPath("Profile/ProfileNo") laurent@2032: def GetProfileNumbers(self): Laurent@2157: return [number.text for number in profile_numbers_xpath(self)] laurent@2032: setattr(cls, "GetProfileNumbers", GetProfileNumbers) laurent@2032: Laurent@2079: def getCoE(self): laurent@2032: mailbox = self.getMailbox() Laurent@2079: if mailbox is not None: Laurent@2079: return mailbox.getCoE() Laurent@2079: return None Laurent@2079: setattr(cls, "getCoE", getCoE) laurent@2032: Laurent@2098: def GetEntriesList(self, limits=None): laurent@2029: entries = {} laurent@2029: Laurent@2162: factory = EntryListFactory(entries) Laurent@2162: Laurent@2157: entries_list_xslt_tree = etree.XSLT( Laurent@2157: entries_list_xslt, extensions = { Laurent@2162: ("entries_list_ns", "AddEntry"): factory.AddEntry, Laurent@2157: ("entries_list_ns", "HexDecValue"): HexDecValue, Laurent@2157: ("entries_list_ns", "EntryName"): EntryName}) Laurent@2157: entries_list_xslt_tree(self, **dict(zip( Laurent@2157: ["min_index", "max_index"], Laurent@2157: map(lambda x: etree.XSLT.strparam(str(x)), Laurent@2157: limits if limits is not None else [0x0000, 0xFFFF]) Laurent@2157: ))) laurent@2029: laurent@2029: return entries laurent@2029: setattr(cls, "GetEntriesList", GetEntriesList) Laurent@2157: laurent@2029: def GetSyncManagers(self): laurent@2029: sync_managers = [] laurent@2029: for sync_manager in self.getSm(): laurent@2029: sync_manager_infos = {} laurent@2029: for name, value in [("Name", sync_manager.getcontent()), laurent@2029: ("Start Address", sync_manager.getStartAddress()), laurent@2029: ("Default Size", sync_manager.getDefaultSize()), laurent@2029: ("Control Byte", sync_manager.getControlByte()), laurent@2029: ("Enable", sync_manager.getEnable())]: laurent@2029: if value is None: laurent@2029: value ="" laurent@2029: sync_manager_infos[name] = value laurent@2029: sync_managers.append(sync_manager_infos) laurent@2029: return sync_managers laurent@2029: setattr(cls, "GetSyncManagers", GetSyncManagers) laurent@2029: laurent@2022: def GroupItemCompare(x, y): laurent@2022: if x["type"] == y["type"]: laurent@2022: if x["type"] == ETHERCAT_GROUP: laurent@2031: return cmp(x["order"], y["order"]) laurent@2022: else: laurent@2031: return cmp(x["name"], y["name"]) laurent@2022: elif x["type"] == ETHERCAT_GROUP: laurent@2022: return -1 laurent@2022: return 1 laurent@2022: laurent@2022: def SortGroupItems(group): laurent@2022: for item in group["children"]: laurent@2022: if item["type"] == ETHERCAT_GROUP: laurent@2022: SortGroupItems(item) laurent@2022: group["children"].sort(GroupItemCompare) laurent@2022: Laurent@2097: class ModulesLibrary: Laurent@2137: Laurent@2137: MODULES_EXTRA_PARAMS = [ Laurent@2137: ("pdo_alignment", { Laurent@2138: "column_label": _("PDO alignment"), Laurent@2138: "column_size": 150, Laurent@2138: "default": 8, Laurent@2138: "description": _( Laurent@2138: "Minimal size in bits between 2 pdo entries")}), Laurent@2137: ("max_pdo_size", { Laurent@2138: "column_label": _("Max entries by PDO"), Laurent@2138: "column_size": 150, Laurent@2139: "default": 255, Laurent@2138: "description": _( Laurent@2138: """Maximal number of entries mapped in a PDO Laurent@2139: including empty entries used for PDO alignment""")}), Laurent@2137: ("add_pdo", { Laurent@2138: "column_label": _("Creating new PDO"), Laurent@2138: "column_size": 150, Laurent@2138: "default": 0, Laurent@2138: "description": _( Laurent@2138: """Adding a PDO not defined in default configuration Laurent@2138: for mapping needed location variables Laurent@2138: (1 if possible)""")}) Laurent@2137: ] Laurent@2097: Laurent@2097: def __init__(self, path, parent_library=None): Laurent@2097: self.Path = path Laurent@2097: if not os.path.exists(self.Path): Laurent@2097: os.makedirs(self.Path) Laurent@2097: self.ParentLibrary = parent_library Laurent@2097: Laurent@2098: if parent_library is not None: Laurent@2098: self.LoadModules() Laurent@2098: else: Laurent@2098: self.Library = None Laurent@2137: self.LoadModulesExtraParams() Laurent@2097: Laurent@2097: def GetPath(self): Laurent@2097: return self.Path Laurent@2097: Laurent@2137: def GetModulesExtraParamsFilePath(self): Laurent@2137: return os.path.join(self.Path, "modules_extra_params.cfg") Laurent@2097: Laurent@2157: groups_xpath = EtherCATInfo_XPath("Descriptions/Groups/Group") Laurent@2157: devices_xpath = EtherCATInfo_XPath("Descriptions/Devices/Device") Laurent@2097: def LoadModules(self): Laurent@2097: self.Library = {} Laurent@2097: Laurent@2097: files = os.listdir(self.Path) laurent@2022: for file in files: Laurent@2097: filepath = os.path.join(self.Path, file) laurent@2022: if os.path.isfile(filepath) and os.path.splitext(filepath)[-1] == ".xml": Laurent@2157: self.modules_infos = None Laurent@2157: laurent@2022: xmlfile = open(filepath, 'r') Laurent@2157: try: Laurent@2160: self.modules_infos, error = EtherCATInfoParser.LoadXMLString(xmlfile.read()) Laurent@2160: if error is not None: Laurent@2160: self.GetCTRoot().logger.write_warning( Laurent@2160: XSDSchemaErrorMessage % (filepath + error)) Laurent@2160: except Exception, exc: Laurent@2160: self.modules_infos, error = None, unicode(exc) laurent@2022: xmlfile.close() laurent@2022: Edouard@2152: if self.modules_infos is not None: Edouard@2152: vendor = self.modules_infos.getVendor() laurent@2022: Laurent@2157: vendor_category = self.Library.setdefault( Laurent@2157: ExtractHexDecValue(vendor.getId()), Laurent@2157: {"name": ExtractName(vendor.getName(), _("Miscellaneous")), Laurent@2157: "groups": {}}) laurent@2022: Laurent@2157: for group in self.groups_xpath(self.modules_infos): laurent@2022: group_type = group.getType() laurent@2022: Laurent@2157: vendor_category["groups"].setdefault(group_type, Laurent@2157: {"name": ExtractName(group.getName(), group_type), Laurent@2157: "parent": group.getParentGroup(), Laurent@2157: "order": group.getSortOrder(), Laurent@2157: #"value": group.getcontent()["value"], Laurent@2157: "devices": []}) laurent@2022: Laurent@2157: for device in self.devices_xpath(self.modules_infos): laurent@2022: device_group = device.getGroupType() laurent@2022: if not vendor_category["groups"].has_key(device_group): laurent@2022: raise ValueError, "Not such group \"%\"" % device_group Laurent@2157: vendor_category["groups"][device_group]["devices"].append( Laurent@2157: (device.getType().getcontent(), device)) Laurent@2160: Laurent@2160: else: Laurent@2160: Laurent@2160: self.GetCTRoot().logger.write_error( Laurent@2160: _("Couldn't load %s XML file:\n%s") % (filepath, error)) Laurent@2160: Laurent@2160: return self.Library Edouard@2152: laurent@2041: def GetModulesLibrary(self, profile_filter=None): Laurent@2098: if self.Library is None: Laurent@2098: self.LoadModules() laurent@2022: library = [] Laurent@2097: for vendor_id, vendor in self.Library.iteritems(): laurent@2022: groups = [] Laurent@2073: children_dict = {} laurent@2022: for group_type, group in vendor["groups"].iteritems(): laurent@2022: group_infos = {"name": group["name"], laurent@2022: "order": group["order"], laurent@2022: "type": ETHERCAT_GROUP, laurent@2041: "infos": None, laurent@2022: "children": children_dict.setdefault(group_type, [])} laurent@2022: device_dict = {} laurent@2022: for device_type, device in group["devices"]: laurent@2041: if profile_filter is None or profile_filter in device.GetProfileNumbers(): Laurent@2097: product_code = device.getType().getProductCode() Laurent@2097: revision_number = device.getType().getRevisionNo() Laurent@2137: module_infos = {"device_type": device_type, Laurent@2137: "vendor": vendor_id, Laurent@2137: "product_code": product_code, Laurent@2137: "revision_number": revision_number} Laurent@2137: module_infos.update(self.GetModuleExtraParams(vendor_id, product_code, revision_number)) laurent@2041: device_infos = {"name": ExtractName(device.getName()), laurent@2041: "type": ETHERCAT_DEVICE, Laurent@2137: "infos": module_infos, laurent@2041: "children": []} laurent@2041: group_infos["children"].append(device_infos) laurent@2041: device_type_occurrences = device_dict.setdefault(device_type, []) laurent@2041: device_type_occurrences.append(device_infos) laurent@2022: for device_type_occurrences in device_dict.itervalues(): laurent@2022: if len(device_type_occurrences) > 1: laurent@2022: for occurrence in device_type_occurrences: laurent@2022: occurrence["name"] += _(" (rev. %s)") % occurrence["infos"]["revision_number"] laurent@2041: if len(group_infos["children"]) > 0: laurent@2041: if group["parent"] is not None: laurent@2041: parent_children = children_dict.setdefault(group["parent"], []) laurent@2041: parent_children.append(group_infos) laurent@2041: else: laurent@2041: groups.append(group_infos) laurent@2041: if len(groups) > 0: laurent@2041: library.append({"name": vendor["name"], laurent@2041: "type": ETHERCAT_VENDOR, laurent@2041: "infos": None, laurent@2041: "children": groups}) laurent@2031: library.sort(lambda x, y: cmp(x["name"], y["name"])) laurent@2022: return library Laurent@2097: Laurent@2098: def GetVendors(self): Laurent@2098: return [(vendor_id, vendor["name"]) for vendor_id, vendor in self.Library.items()] Laurent@2098: Laurent@2097: def GetModuleInfos(self, module_infos): Laurent@2097: vendor = ExtractHexDecValue(module_infos["vendor"]) Laurent@2097: vendor_infos = self.Library.get(vendor) Laurent@2097: if vendor_infos is not None: Laurent@2097: for group_name, group_infos in vendor_infos["groups"].iteritems(): Laurent@2097: for device_type, device_infos in group_infos["devices"]: Laurent@2097: product_code = ExtractHexDecValue(device_infos.getType().getProductCode()) Laurent@2097: revision_number = ExtractHexDecValue(device_infos.getType().getRevisionNo()) Laurent@2097: if (product_code == ExtractHexDecValue(module_infos["product_code"]) and Laurent@2097: revision_number == ExtractHexDecValue(module_infos["revision_number"])): Edouard@2152: self.cntdevice = device_infos Edouard@2152: self.cntdeviceType = device_type Laurent@2137: return device_infos, self.GetModuleExtraParams(vendor, product_code, revision_number) Laurent@2097: return None, None Laurent@2097: Laurent@2097: def ImportModuleLibrary(self, filepath): Laurent@2097: if os.path.isfile(filepath): Laurent@2097: shutil.copy(filepath, self.Path) Laurent@2097: self.LoadModules() Laurent@2097: return True Laurent@2097: return False Laurent@2097: Laurent@2137: def LoadModulesExtraParams(self): Laurent@2137: self.ModulesExtraParams = {} Laurent@2137: Laurent@2137: csvfile_path = self.GetModulesExtraParamsFilePath() Laurent@2097: if os.path.exists(csvfile_path): Laurent@2097: csvfile = open(csvfile_path, "rb") Laurent@2097: sample = csvfile.read(1024) Laurent@2097: csvfile.seek(0) Laurent@2097: dialect = csv.Sniffer().sniff(sample) Laurent@2097: has_header = csv.Sniffer().has_header(sample) Laurent@2097: reader = csv.reader(csvfile, dialect) Laurent@2097: for row in reader: Laurent@2097: if has_header: Laurent@2097: has_header = False Laurent@2097: else: Laurent@2138: params_values = {} Laurent@2138: for (param, param_infos), value in zip( Laurent@2138: self.MODULES_EXTRA_PARAMS, row[3:]): Laurent@2138: if value != "": Laurent@2138: params_values[param] = int(value) Laurent@2138: self.ModulesExtraParams[ Laurent@2138: tuple(map(int, row[:3]))] = params_values Laurent@2097: csvfile.close() Laurent@2137: Laurent@2137: def SaveModulesExtraParams(self): Laurent@2137: csvfile = open(self.GetModulesExtraParamsFilePath(), "wb") Laurent@2137: extra_params = [param for param, params_infos in self.MODULES_EXTRA_PARAMS] Laurent@2097: writer = csv.writer(csvfile, delimiter=';') Laurent@2137: writer.writerow(['Vendor', 'product_code', 'revision_number'] + extra_params) Laurent@2137: for (vendor, product_code, revision_number), module_extra_params in self.ModulesExtraParams.iteritems(): Laurent@2137: writer.writerow([vendor, product_code, revision_number] + Laurent@2137: [module_extra_params.get(param, '') Laurent@2137: for param in extra_params]) Laurent@2097: csvfile.close() Laurent@2097: Laurent@2137: def SetModuleExtraParam(self, vendor, product_code, revision_number, param, value): Laurent@2097: vendor = ExtractHexDecValue(vendor) Laurent@2097: product_code = ExtractHexDecValue(product_code) Laurent@2097: revision_number = ExtractHexDecValue(revision_number) Laurent@2097: Laurent@2138: module_infos = (vendor, product_code, revision_number) Laurent@2138: self.ModulesExtraParams.setdefault(module_infos, {}) Laurent@2138: self.ModulesExtraParams[module_infos][param] = value Laurent@2138: Laurent@2137: self.SaveModulesExtraParams() Laurent@2137: Laurent@2137: def GetModuleExtraParams(self, vendor, product_code, revision_number): Laurent@2097: vendor = ExtractHexDecValue(vendor) Laurent@2097: product_code = ExtractHexDecValue(product_code) Laurent@2097: revision_number = ExtractHexDecValue(revision_number) Laurent@2097: Laurent@2097: if self.ParentLibrary is not None: Laurent@2137: extra_params = self.ParentLibrary.GetModuleExtraParams(vendor, product_code, revision_number) Laurent@2137: else: Laurent@2137: extra_params = {} Laurent@2137: Laurent@2138: extra_params.update(self.ModulesExtraParams.get((vendor, product_code, revision_number), {})) Laurent@2137: Laurent@2137: for param, param_infos in self.MODULES_EXTRA_PARAMS: Laurent@2137: extra_params.setdefault(param, param_infos["default"]) Laurent@2137: Laurent@2137: return extra_params Laurent@2097: Laurent@2097: USERDATA_DIR = wx.StandardPaths.Get().GetUserDataDir() Laurent@2097: if wx.Platform != '__WXMSW__': Laurent@2097: USERDATA_DIR += '_files' Laurent@2097: Laurent@2097: ModulesDatabase = ModulesLibrary( Laurent@2097: os.path.join(USERDATA_DIR, "ethercat_modules")) Laurent@2097: Laurent@2097: class RootClass: Laurent@2097: Laurent@2097: CTNChildrenTypes = [("EthercatNode",_EthercatCTN,"Ethercat Master")] Laurent@2097: EditorType = LibraryEditor Laurent@2157: Laurent@2157: Laurent@2097: def __init__(self): Laurent@2097: self.ModulesLibrary = None Laurent@2097: self.LoadModulesLibrary() Laurent@2097: Laurent@2149: def GetIconName(self): Laurent@2149: return "Ethercat" Laurent@2149: Laurent@2133: def GetModulesLibraryPath(self, project_path=None): Laurent@2133: if project_path is None: Laurent@2133: project_path = self.CTNPath() Laurent@2133: return os.path.join(project_path, "modules") Laurent@2133: Laurent@2133: def OnCTNSave(self, from_project_path=None): Laurent@2133: if from_project_path is not None: Laurent@2133: shutil.copytree(self.GetModulesLibraryPath(from_project_path), Laurent@2133: self.GetModulesLibraryPath()) Laurent@2133: return True Laurent@2097: Laurent@2097: def CTNGenerate_C(self, buildpath, locations): Laurent@2097: return [],"",False Laurent@2097: Laurent@2097: def LoadModulesLibrary(self): Laurent@2097: if self.ModulesLibrary is None: Laurent@2097: self.ModulesLibrary = ModulesLibrary(self.GetModulesLibraryPath(), ModulesDatabase) Laurent@2097: else: Laurent@2097: self.ModulesLibrary.LoadModulesLibrary() Laurent@2097: Laurent@2097: def GetModulesDatabaseInstance(self): Laurent@2097: return ModulesDatabase Laurent@2097: Laurent@2097: def GetModulesLibraryInstance(self): Laurent@2097: return self.ModulesLibrary Laurent@2097: Laurent@2097: def GetModulesLibrary(self, profile_filter=None): Laurent@2097: return self.ModulesLibrary.GetModulesLibrary(profile_filter) Laurent@2097: Laurent@2098: def GetVendors(self): Laurent@2098: return self.ModulesLibrary.GetVendors() Laurent@2098: Laurent@2097: def GetModuleInfos(self, module_infos): Laurent@2097: return self.ModulesLibrary.GetModuleInfos(module_infos) laurent@2041: