Laurent@2097: import os Laurent@2097: laurent@2022: import wx laurent@2026: import wx.grid laurent@2038: import wx.gizmos Laurent@2097: import wx.lib.buttons Laurent@2097: Laurent@2097: from controls import CustomGrid, CustomTable, FolderTree Laurent@2071: from editors.ConfTreeNodeEditor import ConfTreeNodeEditor, SCROLLBAR_UNIT Laurent@2097: from util.BitmapLibrary import GetBitmap laurent@2022: laurent@2022: [ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE] = range(3) laurent@2022: laurent@2022: def AppendMenu(parent, help, id, kind, text): laurent@2022: if wx.VERSION >= (2, 6, 0): laurent@2022: parent.Append(help=help, id=id, kind=kind, text=text) laurent@2022: else: laurent@2022: parent.Append(helpString=help, id=id, kind=kind, item=text) laurent@2022: laurent@2022: def GetVariablesTableColnames(): laurent@2022: _ = lambda x : x Laurent@2097: return ["#", _("Name"), _("Index"), _("SubIndex"), _("Type"), _("Access")] Laurent@2097: Laurent@2097: ACCESS_TYPES = { Laurent@2097: 'ro': 'R', Laurent@2097: 'wo': 'W', Laurent@2097: 'rw': 'R/W'} Laurent@2097: Laurent@2097: def GetAccessValue(access, pdo_mapping): Laurent@2097: value = ACCESS_TYPES.get(access) Laurent@2097: if pdo_mapping != "": Laurent@2097: value += "/P" Laurent@2097: return value laurent@2034: laurent@2053: class NodeEditor(ConfTreeNodeEditor): laurent@2041: Laurent@2095: CONFNODEEDITOR_TABS = [ Laurent@2095: (_("Ethercat node"), "_create_EthercatNodeEditor")] laurent@2022: Laurent@2095: def _create_EthercatNodeEditor(self, prnt): Laurent@2097: self.EthercatNodeEditor = wx.Panel(prnt, style=wx.TAB_TRAVERSAL) Laurent@2097: Laurent@2097: main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) Laurent@2097: main_sizer.AddGrowableCol(0) Laurent@2097: main_sizer.AddGrowableRow(1) Laurent@2097: Laurent@2097: variables_label = wx.StaticText(self.EthercatNodeEditor, Laurent@2097: label=_('Variable entries:')) Laurent@2097: main_sizer.AddWindow(variables_label, border=10, flag=wx.TOP|wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: self.VariablesGrid = wx.gizmos.TreeListCtrl(self.EthercatNodeEditor, Laurent@2097: style=wx.TR_DEFAULT_STYLE | Laurent@2097: wx.TR_ROW_LINES | Laurent@2097: wx.TR_COLUMN_LINES | Laurent@2097: wx.TR_HIDE_ROOT | Laurent@2097: wx.TR_FULL_ROW_HIGHLIGHT) Laurent@2097: self.VariablesGrid.GetMainWindow().Bind(wx.EVT_LEFT_DOWN, Laurent@2097: self.OnVariablesGridLeftClick) Laurent@2097: main_sizer.AddWindow(self.VariablesGrid, border=10, Laurent@2097: flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) laurent@2038: Laurent@2097: self.EthercatNodeEditor.SetSizer(main_sizer) Laurent@2097: Laurent@2095: return self.EthercatNodeEditor Laurent@2095: laurent@2041: def __init__(self, parent, controler, window): laurent@2056: ConfTreeNodeEditor.__init__(self, parent, controler, window) laurent@2041: laurent@2038: for colname, colsize, colalign in zip(GetVariablesTableColnames(), Laurent@2097: [40, 150, 100, 100, 150, 100], laurent@2038: [wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, Laurent@2097: wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]): Laurent@2072: self.VariablesGrid.AddColumn(_(colname), colsize, colalign) laurent@2038: self.VariablesGrid.SetMainColumn(1) laurent@2041: laurent@2041: def GetBufferState(self): laurent@2041: return False, False laurent@2041: laurent@2041: def RefreshView(self): Laurent@2060: ConfTreeNodeEditor.RefreshView(self) Laurent@2067: Laurent@2067: self.RefreshSlaveInfos() Laurent@2067: Laurent@2067: def RefreshSlaveInfos(self): laurent@2041: slave_infos = self.Controler.GetSlaveInfos() laurent@2022: if slave_infos is not None: laurent@2038: self.RefreshVariablesGrid(slave_infos["entries"]) laurent@2022: else: laurent@2038: self.RefreshVariablesGrid([]) laurent@2041: laurent@2038: def RefreshVariablesGrid(self, entries): laurent@2038: root = self.VariablesGrid.GetRootItem() laurent@2038: if not root.IsOk(): laurent@2038: root = self.VariablesGrid.AddRoot("Slave entries") laurent@2038: self.GenerateVariablesGridBranch(root, entries, GetVariablesTableColnames()) laurent@2038: self.VariablesGrid.Expand(root) laurent@2038: laurent@2038: def GenerateVariablesGridBranch(self, root, entries, colnames, idx=0): Laurent@2097: item, root_cookie = self.VariablesGrid.GetFirstChild(root) laurent@2038: laurent@2056: no_more_items = not item.IsOk() laurent@2038: for entry in entries: laurent@2038: idx += 1 laurent@2056: if no_more_items: laurent@2038: item = self.VariablesGrid.AppendItem(root, "") laurent@2038: for col, colname in enumerate(colnames): laurent@2038: if col == 0: laurent@2038: self.VariablesGrid.SetItemText(item, str(idx), 0) laurent@2038: else: Laurent@2097: value = entry.get(colname, "") Laurent@2097: if colname == "Access": Laurent@2097: value = GetAccessValue(value, entry.get("PDOMapping", "")) Laurent@2097: self.VariablesGrid.SetItemText(item, value, col) laurent@2038: if entry["PDOMapping"] == "": laurent@2038: self.VariablesGrid.SetItemBackgroundColour(item, wx.LIGHT_GREY) laurent@2038: self.VariablesGrid.SetItemPyData(item, entry) laurent@2056: idx = self.GenerateVariablesGridBranch(item, entry["children"], colnames, idx) laurent@2056: if not no_more_items: laurent@2041: item, root_cookie = self.VariablesGrid.GetNextChild(root, root_cookie) laurent@2056: no_more_items = not item.IsOk() laurent@2056: laurent@2056: if not no_more_items: laurent@2056: to_delete = [] laurent@2056: while item.IsOk(): laurent@2056: to_delete.append(item) laurent@2056: item, root_cookie = self.VariablesGrid.GetNextChild(root, root_cookie) laurent@2056: for item in to_delete: laurent@2056: self.VariablesGrid.Delete(item) laurent@2038: laurent@2038: return idx laurent@2038: laurent@2038: def OnVariablesGridLeftClick(self, event): laurent@2038: item, flags, col = self.VariablesGrid.HitTest(event.GetPosition()) laurent@2038: if item.IsOk(): laurent@2038: entry = self.VariablesGrid.GetItemPyData(item) laurent@2038: data_type = entry.get("Type", "") laurent@2038: pdo_mapping = entry.get("PDOMapping", "") laurent@2029: laurent@2038: if (col == -1 and pdo_mapping != "" and laurent@2038: self.Controler.GetSizeOfType(data_type) is not None): laurent@2038: laurent@2038: entry_index = self.Controler.ExtractHexDecValue(entry.get("Index", "0")) laurent@2038: entry_subindex = self.Controler.ExtractHexDecValue(entry.get("SubIndex", "0")) Edouard@2048: var_name = "%s_%4.4x_%2.2x" % (self.Controler.CTNName(), entry_index, entry_subindex) laurent@2038: if pdo_mapping == "R": laurent@2038: dir = "%I" laurent@2038: else: laurent@2038: dir = "%Q" laurent@2038: location = "%s%s" % (dir, self.Controler.GetSizeOfType(data_type)) + \ laurent@2041: ".".join(map(lambda x:str(x), self.Controler.GetCurrentLocation() + (self.Controler.GetSlavePos(), entry_index, entry_subindex))) laurent@2038: laurent@2038: data = wx.TextDataObject(str((location, "location", data_type, var_name, ""))) laurent@2038: dragSource = wx.DropSource(self.VariablesGrid) laurent@2038: dragSource.SetData(data) laurent@2038: dragSource.DoDragDrop() Laurent@2089: return laurent@2038: laurent@2022: event.Skip() laurent@2022: Laurent@2097: CIA402NodeEditor = NodeEditor Laurent@2097: Laurent@2097: Laurent@2097: def GetModulesTableColnames(): Laurent@2097: _ = lambda x : x Laurent@2097: return [_("Name"), _("PDO alignment (bits)")] Laurent@2097: Laurent@2097: class LibraryEditorPanel(wx.ScrolledWindow): Laurent@2097: Laurent@2097: def __init__(self, parent, module_library, buttons): Laurent@2097: wx.ScrolledWindow.__init__(self, parent, Laurent@2097: style=wx.TAB_TRAVERSAL|wx.HSCROLL|wx.VSCROLL) Laurent@2097: self.Bind(wx.EVT_SIZE, self.OnResize) Laurent@2097: Laurent@2097: self.ModuleLibrary = module_library Laurent@2097: Laurent@2097: main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=5) Laurent@2097: main_sizer.AddGrowableCol(0) Laurent@2097: main_sizer.AddGrowableRow(1) Laurent@2097: main_sizer.AddGrowableRow(3) Laurent@2097: Laurent@2097: ESI_files_label = wx.StaticText(self, Laurent@2097: label=_("ESI Files:")) Laurent@2097: main_sizer.AddWindow(ESI_files_label, border=10, Laurent@2097: flag=wx.TOP|wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: folder_tree_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=0) Laurent@2097: folder_tree_sizer.AddGrowableCol(0) Laurent@2097: folder_tree_sizer.AddGrowableRow(0) Laurent@2097: main_sizer.AddSizer(folder_tree_sizer, border=10, Laurent@2097: flag=wx.GROW|wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: self.ESIFiles = FolderTree(self, self.GetPath(), editable=False) Laurent@2097: self.ESIFiles.SetFilter(".xml") Laurent@2097: self.ESIFiles.SetMinSize(wx.Size(600, 300)) Laurent@2097: folder_tree_sizer.AddWindow(self.ESIFiles, flag=wx.GROW) Laurent@2097: Laurent@2097: buttons_sizer = wx.BoxSizer(wx.VERTICAL) Laurent@2097: folder_tree_sizer.AddSizer(buttons_sizer, Laurent@2097: flag=wx.ALIGN_CENTER_VERTICAL) Laurent@2097: Laurent@2097: for idx, (name, bitmap, help, callback) in enumerate(buttons): Laurent@2097: button = wx.lib.buttons.GenBitmapButton(self, Laurent@2097: bitmap=GetBitmap(bitmap), Laurent@2097: size=wx.Size(28, 28), style=wx.NO_BORDER) Laurent@2097: button.SetToolTipString(help) Laurent@2097: setattr(self, name, button) Laurent@2097: if idx > 0: Laurent@2097: flag = wx.TOP Laurent@2097: else: Laurent@2097: flag = 0 Laurent@2097: if callback is None: Laurent@2097: callback = getattr(self, "On" + name, None) Laurent@2097: if callback is not None: Laurent@2097: self.Bind(wx.EVT_BUTTON, callback, button) Laurent@2097: buttons_sizer.AddWindow(button, border=10, flag=flag) Laurent@2097: Laurent@2097: modules_label = wx.StaticText(self, Laurent@2097: label=_("Modules library:")) Laurent@2097: main_sizer.AddSizer(modules_label, border=10, Laurent@2097: flag=wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: self.ModulesGrid = wx.gizmos.TreeListCtrl(self, Laurent@2097: style=wx.TR_DEFAULT_STYLE | Laurent@2097: wx.TR_ROW_LINES | Laurent@2097: wx.TR_COLUMN_LINES | Laurent@2097: wx.TR_HIDE_ROOT | Laurent@2097: wx.TR_FULL_ROW_HIGHLIGHT) Laurent@2097: self.ModulesGrid.SetMinSize(wx.Size(600, 300)) Laurent@2097: self.ModulesGrid.GetMainWindow().Bind(wx.EVT_LEFT_DCLICK, Laurent@2097: self.OnModulesGridLeftDClick) Laurent@2097: main_sizer.AddWindow(self.ModulesGrid, border=10, Laurent@2097: flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: self.SetSizer(main_sizer) Laurent@2097: Laurent@2097: for colname, colsize, colalign in zip(GetModulesTableColnames(), Laurent@2097: [400, 150], Laurent@2097: [wx.ALIGN_LEFT, wx.ALIGN_RIGHT]): Laurent@2097: self.ModulesGrid.AddColumn(_(colname), colsize, colalign) Laurent@2097: self.ModulesGrid.SetMainColumn(0) Laurent@2097: Laurent@2097: def GetPath(self): Laurent@2097: return self.ModuleLibrary.GetPath() Laurent@2097: Laurent@2097: def GetSelectedFilePath(self): Laurent@2097: return self.ESIFiles.GetPath() Laurent@2097: Laurent@2097: def RefreshView(self): Laurent@2097: self.ESIFiles.RefreshTree() Laurent@2097: self.RefreshModulesGrid() Laurent@2097: Laurent@2097: def RefreshModulesGrid(self): Laurent@2097: root = self.ModulesGrid.GetRootItem() Laurent@2097: if not root.IsOk(): Laurent@2097: root = self.ModulesGrid.AddRoot("Modules") Laurent@2097: self.GenerateModulesGridBranch(root, Laurent@2097: self.ModuleLibrary.GetModulesLibrary(), Laurent@2097: GetVariablesTableColnames()) Laurent@2097: self.ModulesGrid.Expand(root) Laurent@2097: Laurent@2097: def GenerateModulesGridBranch(self, root, modules, colnames): Laurent@2097: item, root_cookie = self.ModulesGrid.GetFirstChild(root) Laurent@2097: Laurent@2097: no_more_items = not item.IsOk() Laurent@2097: for module in modules: Laurent@2097: if no_more_items: Laurent@2097: item = self.ModulesGrid.AppendItem(root, "") Laurent@2097: self.ModulesGrid.SetItemText(item, module["name"], 0) Laurent@2097: if module["infos"] is not None: Laurent@2097: self.ModulesGrid.SetItemText(item, str(module["infos"]["alignment"]), 1) Laurent@2097: else: Laurent@2097: self.ModulesGrid.SetItemBackgroundColour(item, wx.LIGHT_GREY) Laurent@2097: self.ModulesGrid.SetItemPyData(item, module["infos"]) Laurent@2097: self.GenerateModulesGridBranch(item, module["children"], colnames) Laurent@2097: if not no_more_items: Laurent@2097: item, root_cookie = self.ModulesGrid.GetNextChild(root, root_cookie) Laurent@2097: no_more_items = not item.IsOk() Laurent@2097: Laurent@2097: if not no_more_items: Laurent@2097: to_delete = [] Laurent@2097: while item.IsOk(): Laurent@2097: to_delete.append(item) Laurent@2097: item, root_cookie = self.ModulesGrid.GetNextChild(root, root_cookie) Laurent@2097: for item in to_delete: Laurent@2097: self.ModulesGrid.Delete(item) Laurent@2097: Laurent@2097: def OnImportButton(self, event): Laurent@2097: dialog = wx.FileDialog(self, Laurent@2097: _("Choose an XML file"), Laurent@2097: os.getcwd(), "", Laurent@2097: _("XML files (*.xml)|*.xml|All files|*.*"), wx.OPEN) Laurent@2097: Laurent@2097: if dialog.ShowModal() == wx.ID_OK: Laurent@2097: filepath = dialog.GetPath() Laurent@2097: if self.ModuleLibrary.ImportModuleLibrary(filepath): Laurent@2097: wx.CallAfter(self.RefreshView) Laurent@2097: else: Laurent@2097: message = wx.MessageDialog(self, Laurent@2097: _("No such XML file: %s\n") % filepath, Laurent@2097: _("Error"), wx.OK|wx.ICON_ERROR) Laurent@2097: message.ShowModal() Laurent@2097: message.Destroy() Laurent@2097: dialog.Destroy() Laurent@2097: Laurent@2097: event.Skip() Laurent@2097: Laurent@2097: def OnDeleteButton(self, event): Laurent@2097: filepath = self.GetSelectedFilePath() Laurent@2097: if os.path.isfile(filepath): Laurent@2097: folder, filename = os.path.split(filepath) Laurent@2097: Laurent@2097: dialog = wx.MessageDialog(self, Laurent@2097: _("Do you really want to delete the file '%s'?") % filename, Laurent@2097: _("Delete File"), wx.YES_NO|wx.ICON_QUESTION) Laurent@2097: remove = dialog.ShowModal() == wx.ID_YES Laurent@2097: dialog.Destroy() Laurent@2097: Laurent@2097: if remove: Laurent@2097: os.remove(filepath) Laurent@2097: self.ModuleLibrary.LoadModules() Laurent@2097: wx.CallAfter(self.RefreshView) Laurent@2097: event.Skip() Laurent@2097: Laurent@2097: def OnModulesGridLeftDClick(self, event): Laurent@2097: item, flags, col = self.ModulesGrid.HitTest(event.GetPosition()) Laurent@2097: if item.IsOk(): Laurent@2097: entry_infos = self.ModulesGrid.GetItemPyData(item) Laurent@2097: if entry_infos is not None and col == 1: Laurent@2097: dialog = wx.TextEntryDialog(self, Laurent@2097: _("Set PDO alignment (bits):"), Laurent@2097: _("%s PDO alignment") % self.ModulesGrid.GetItemText(item), Laurent@2097: str(entry_infos["alignment"])) Laurent@2097: Laurent@2097: if dialog.ShowModal() == wx.ID_OK: Laurent@2097: try: Laurent@2097: self.ModuleLibrary.SetAlignment( Laurent@2097: entry_infos["vendor"], Laurent@2097: entry_infos["product_code"], Laurent@2097: entry_infos["revision_number"], Laurent@2097: int(dialog.GetValue())) Laurent@2097: wx.CallAfter(self.RefreshModulesGrid) Laurent@2097: except ValueError: Laurent@2097: message = wx.MessageDialog(self, Laurent@2097: _("Module PDO alignment must be an integer!"), Laurent@2097: _("Error"), wx.OK|wx.ICON_ERROR) Laurent@2097: message.ShowModal() Laurent@2097: message.Destroy() Laurent@2097: Laurent@2097: dialog.Destroy() Laurent@2097: Laurent@2097: event.Skip() Laurent@2097: Laurent@2097: def OnResize(self, event): Laurent@2097: self.GetBestSize() Laurent@2097: xstart, ystart = self.GetViewStart() Laurent@2097: window_size = self.GetClientSize() Laurent@2097: maxx, maxy = self.GetMinSize() Laurent@2058: posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)) Laurent@2058: posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)) Laurent@2097: self.Scroll(posx, posy) Laurent@2097: self.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, Laurent@2058: maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) Laurent@2058: event.Skip() Laurent@2058: Laurent@2097: class DatabaseManagementDialog(wx.Dialog): Laurent@2097: Laurent@2097: def __init__(self, parent, database): Laurent@2097: wx.Dialog.__init__(self, parent, Laurent@2097: size=wx.Size(700, 500), title=_('ESI Files Database management'), Laurent@2097: style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) Laurent@2097: Laurent@2097: main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) Laurent@2097: main_sizer.AddGrowableCol(0) Laurent@2097: main_sizer.AddGrowableRow(0) Laurent@2097: Laurent@2097: self.DatabaseEditor = LibraryEditorPanel(self, database, Laurent@2097: [("ImportButton", "ImportESI", _("Import file to ESI files database"), None), Laurent@2097: ("DeleteButton", "remove_element", _("Remove file from database"), None)]) Laurent@2097: main_sizer.AddWindow(self.DatabaseEditor, border=10, Laurent@2097: flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) Laurent@2097: button_sizer.GetAffirmativeButton().SetLabel(_("Add file to project")) Laurent@2097: button_sizer.GetCancelButton().SetLabel(_("Close")) Laurent@2097: main_sizer.AddSizer(button_sizer, border=10, Laurent@2097: flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) Laurent@2097: Laurent@2097: self.SetSizer(main_sizer) Laurent@2097: Laurent@2097: self.DatabaseEditor.RefreshView() Laurent@2097: Laurent@2097: def GetValue(self): Laurent@2097: return self.DatabaseEditor.GetSelectedFilePath() Laurent@2097: Laurent@2097: class LibraryEditor(ConfTreeNodeEditor): Laurent@2097: Laurent@2097: CONFNODEEDITOR_TABS = [ Laurent@2097: (_("Modules Library"), "_create_ModuleLibraryEditor")] Laurent@2097: Laurent@2097: def _create_ModuleLibraryEditor(self, prnt): Laurent@2097: self.ModuleLibraryEditor = LibraryEditorPanel(prnt, Laurent@2097: self.Controler.GetModulesLibraryInstance(), Laurent@2097: [("ImportButton", "ImportESI", _("Import ESI file"), None), Laurent@2097: ("AddButton", "ImportDatabase", _("Add file from ESI files database"), self.OnAddButton), Laurent@2097: ("DeleteButton", "remove_element", _("Remove file from library"), None)]) Laurent@2097: Laurent@2097: return self.ModuleLibraryEditor Laurent@2097: Laurent@2097: def __init__(self, parent, controler, window): Laurent@2097: ConfTreeNodeEditor.__init__(self, parent, controler, window) Laurent@2097: Laurent@2097: self.RefreshView() Laurent@2097: Laurent@2097: def RefreshView(self): Laurent@2097: ConfTreeNodeEditor.RefreshView(self) Laurent@2097: self.ModuleLibraryEditor.RefreshView() Laurent@2097: Laurent@2097: def OnAddButton(self, event): Laurent@2097: dialog = DatabaseManagementDialog(self, Laurent@2097: self.Controler.GetModulesDatabaseInstance()) Laurent@2097: Laurent@2097: if dialog.ShowModal() == wx.ID_OK: Laurent@2097: module_library = self.Controler.GetModulesLibraryInstance() Laurent@2097: module_library.ImportModuleLibrary(dialog.GetValue()) Laurent@2097: Laurent@2097: dialog.Destroy() Laurent@2097: Laurent@2097: wx.CallAfter(self.ModuleLibraryEditor.RefreshView) Laurent@2097: Laurent@2097: event.Skip() Laurent@2097: