]> rtime.felk.cvut.cz Git - CanFestival-3.git/blob - objdictgen/networkedit.py
Problem with String size in C file generated fixed
[CanFestival-3.git] / objdictgen / networkedit.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 #This file is part of CanFestival, a library implementing CanOpen Stack. 
5 #
6 #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
7 #
8 #See COPYING file for copyrights details.
9 #
10 #This library is free software; you can redistribute it and/or
11 #modify it under the terms of the GNU Lesser General Public
12 #License as published by the Free Software Foundation; either
13 #version 2.1 of the License, or (at your option) any later version.
14 #
15 #This library is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 #Lesser General Public License for more details.
19 #
20 #You should have received a copy of the GNU Lesser General Public
21 #License along with this library; if not, write to the Free Software
22 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24 import wx
25 import wx.grid
26
27 from types import *
28 import os, re, platform, sys, time, traceback, getopt
29
30 __version__ = "$Revision: 1.22 $"
31
32 from nodelist import *
33 from nodemanager import *
34 from subindextable import *
35 from commondialogs import *
36 from doc_index.DS301_index import *
37
38 ScriptDirectory = os.path.split(os.path.realpath(__file__))[0]
39
40 try:
41     import wx.html
42
43     EVT_HTML_URL_CLICK = wx.NewId()
44
45     class HtmlWindowUrlClick(wx.PyEvent):
46         def __init__(self, linkinfo):
47             wx.PyEvent.__init__(self)
48             self.SetEventType(EVT_HTML_URL_CLICK)
49             self.linkinfo = (linkinfo.GetHref(), linkinfo.GetTarget())
50             
51     class UrlClickHtmlWindow(wx.html.HtmlWindow):
52         """ HTML window that generates and OnLinkClicked event.
53
54         Use this to avoid having to override HTMLWindow
55         """
56         def OnLinkClicked(self, linkinfo):
57             wx.PostEvent(self, HtmlWindowUrlClick(linkinfo))
58         
59         def Bind(self, event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY):
60             if event == HtmlWindowUrlClick:
61                 self.Connect(-1, -1, EVT_HTML_URL_CLICK, handler)
62             else:
63                 wx.html.HtmlWindow.Bind(event, handler, source=source, id=id, id2=id2)
64     
65 #-------------------------------------------------------------------------------
66 #                                Html Frame
67 #-------------------------------------------------------------------------------
68
69     [ID_HTMLFRAME, ID_HTMLFRAMEHTMLCONTENT] = [wx.NewId() for _init_ctrls in range(2)]
70
71     class HtmlFrame(wx.Frame):
72         def _init_ctrls(self, prnt):
73             # generated method, don't edit
74             wx.Frame.__init__(self, id=ID_HTMLFRAME, name='HtmlFrame',
75                   parent=prnt, pos=wx.Point(320, 231), size=wx.Size(853, 616),
76                   style=wx.DEFAULT_FRAME_STYLE, title='')
77             self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
78             
79             self.HtmlContent = UrlClickHtmlWindow(id=ID_HTMLFRAMEHTMLCONTENT,
80                   name='HtmlContent', parent=self, pos=wx.Point(0, 0),
81                   size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO|wx.html.HW_NO_SELECTION)
82             self.HtmlContent.Bind(HtmlWindowUrlClick, self.OnLinkClick)
83
84         def __init__(self, parent, opened):
85             self._init_ctrls(parent)
86             self.HtmlFrameOpened = opened
87         
88         def SetHtmlCode(self, htmlcode):
89             self.HtmlContent.SetPage(htmlcode)
90             
91         def SetHtmlPage(self, htmlpage):
92             self.HtmlContent.LoadPage(htmlpage)
93             
94         def OnCloseFrame(self, event):
95             self.HtmlFrameOpened.remove(self.GetTitle())
96             event.Skip()
97         
98         def OnLinkClick(self, event):
99             url = event.linkinfo[0]
100             try:
101                 import webbrowser
102             except ImportError:
103                 wx.MessageBox('Please point your browser at: %s' % url)
104             else:
105                 webbrowser.open(url)
106     
107     Html_Window = True
108 except:
109     Html_Window = False
110
111
112 [ID_NETWORKEDIT, ID_NETWORKEDITNETWORKNODES, 
113  ID_NETWORKEDITHELPBAR,
114 ] = [wx.NewId() for _init_ctrls in range(3)]
115
116 [ID_NETWORKEDITNETWORKMENUBUILDMASTER, 
117 ] = [wx.NewId() for _init_coll_AddMenu_Items in range(1)]
118
119 [ID_NETWORKEDITEDITMENUNODEINFOS, ID_NETWORKEDITEDITMENUDS301PROFILE, 
120  ID_NETWORKEDITEDITMENUDS302PROFILE, ID_NETWORKEDITEDITMENUOTHERPROFILE, 
121 ] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
122
123 [ID_NETWORKEDITADDMENUSDOSERVER, ID_NETWORKEDITADDMENUSDOCLIENT, 
124  ID_NETWORKEDITADDMENUPDOTRANSMIT, ID_NETWORKEDITADDMENUPDORECEIVE, 
125  ID_NETWORKEDITADDMENUMAPVARIABLE, ID_NETWORKEDITADDMENUUSERTYPE, 
126 ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)]
127
128 class networkedit(wx.Frame):
129     def _init_coll_MenuBar_Menus(self, parent):
130         if self.ModeSolo:
131             parent.Append(menu=self.FileMenu, title='File')
132         parent.Append(menu=self.NetworkMenu, title='Network')
133         parent.Append(menu=self.EditMenu, title='Edit')
134         parent.Append(menu=self.AddMenu, title='Add')
135         parent.Append(menu=self.HelpMenu, title='Help')
136
137     def _init_coll_FileMenu_Items(self, parent):
138         parent.Append(help='', id=wx.ID_NEW,
139               kind=wx.ITEM_NORMAL, text='New\tCTRL+N')
140         parent.Append(help='', id=wx.ID_OPEN,
141               kind=wx.ITEM_NORMAL, text='Open\tCTRL+O')
142         parent.Append(help='', id=wx.ID_CLOSE,
143               kind=wx.ITEM_NORMAL, text='Close\tCTRL+W')
144         parent.AppendSeparator()
145         parent.Append(help='', id=wx.ID_SAVE,
146               kind=wx.ITEM_NORMAL, text='Save\tCTRL+S')
147         parent.AppendSeparator()
148         parent.Append(help='', id=wx.ID_EXIT,
149               kind=wx.ITEM_NORMAL, text='Exit')
150         self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
151         self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
152         self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE)
153         self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
154         self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
155
156     def _init_coll_NetworkMenu_Items(self, parent):
157         parent.Append(help='', id=wx.ID_ADD,
158               kind=wx.ITEM_NORMAL, text='Add Slave Node')
159         parent.Append(help='', id=wx.ID_DELETE,
160               kind=wx.ITEM_NORMAL, text='Remove Slave Node')
161         parent.AppendSeparator()
162         parent.Append(help='', id=ID_NETWORKEDITNETWORKMENUBUILDMASTER,
163               kind=wx.ITEM_NORMAL, text='Build Master Dictionary')
164         self.Bind(wx.EVT_MENU, self.OnAddSlaveMenu, id=wx.ID_ADD)
165         self.Bind(wx.EVT_MENU, self.OnRemoveSlaveMenu, id=wx.ID_DELETE)
166 ##        self.Bind(wx.EVT_MENU, self.OnBuildMasterMenu,
167 ##              id=ID_NETWORKEDITNETWORKMENUBUILDMASTER)
168
169     def _init_coll_EditMenu_Items(self, parent):
170         parent.Append(help='', id=wx.ID_REFRESH,
171               kind=wx.ITEM_NORMAL, text='Refresh\tCTRL+R')
172         parent.AppendSeparator()
173         parent.Append(help='', id=wx.ID_UNDO,
174               kind=wx.ITEM_NORMAL, text='Undo\tCTRL+Z')
175         parent.Append(help='', id=wx.ID_REDO,
176               kind=wx.ITEM_NORMAL, text='Redo\tCTRL+Y')
177         parent.AppendSeparator()
178         parent.Append(help='', id=ID_NETWORKEDITEDITMENUNODEINFOS,
179               kind=wx.ITEM_NORMAL, text='Node infos')
180         parent.Append(help='', id=ID_NETWORKEDITEDITMENUDS301PROFILE,
181               kind=wx.ITEM_NORMAL, text='DS-301 Profile')
182         parent.Append(help='', id=ID_NETWORKEDITEDITMENUDS302PROFILE,
183               kind=wx.ITEM_NORMAL, text='DS-302 Profile')
184         parent.Append(help='', id=ID_NETWORKEDITEDITMENUOTHERPROFILE,
185               kind=wx.ITEM_NORMAL, text='Other Profile')
186         self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH)
187         self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO)
188         self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO)
189         self.Bind(wx.EVT_MENU, self.OnNodeInfosMenu,
190               id=ID_NETWORKEDITEDITMENUNODEINFOS)
191         self.Bind(wx.EVT_MENU, self.OnCommunicationMenu,
192               id=ID_NETWORKEDITEDITMENUDS301PROFILE)
193         self.Bind(wx.EVT_MENU, self.OnOtherCommunicationMenu,
194               id=ID_NETWORKEDITEDITMENUDS302PROFILE)
195         self.Bind(wx.EVT_MENU, self.OnEditProfileMenu,
196               id=ID_NETWORKEDITEDITMENUOTHERPROFILE)
197
198     def _init_coll_AddMenu_Items(self, parent):
199         parent.Append(help='', id=ID_NETWORKEDITADDMENUSDOSERVER,
200               kind=wx.ITEM_NORMAL, text='SDO Server')
201         parent.Append(help='', id=ID_NETWORKEDITADDMENUSDOCLIENT,
202               kind=wx.ITEM_NORMAL, text='SDO Client')
203         parent.Append(help='', id=ID_NETWORKEDITADDMENUPDOTRANSMIT,
204               kind=wx.ITEM_NORMAL, text='PDO Transmit')
205         parent.Append(help='', id=ID_NETWORKEDITADDMENUPDORECEIVE,
206               kind=wx.ITEM_NORMAL, text='PDO Receive')
207         parent.Append(help='', id=ID_NETWORKEDITADDMENUMAPVARIABLE,
208               kind=wx.ITEM_NORMAL, text='Map Variable')
209         parent.Append(help='', id=ID_NETWORKEDITADDMENUUSERTYPE,
210               kind=wx.ITEM_NORMAL, text='User Type')
211         self.Bind(wx.EVT_MENU, self.OnAddSDOServerMenu,
212               id=ID_NETWORKEDITADDMENUSDOSERVER)
213         self.Bind(wx.EVT_MENU, self.OnAddSDOClientMenu,
214               id=ID_NETWORKEDITADDMENUSDOCLIENT)
215         self.Bind(wx.EVT_MENU, self.OnAddPDOTransmitMenu,
216               id=ID_NETWORKEDITADDMENUPDOTRANSMIT)
217         self.Bind(wx.EVT_MENU, self.OnAddPDOReceiveMenu,
218               id=ID_NETWORKEDITADDMENUPDORECEIVE)
219         self.Bind(wx.EVT_MENU, self.OnAddMapVariableMenu,
220               id=ID_NETWORKEDITADDMENUMAPVARIABLE)
221         self.Bind(wx.EVT_MENU, self.OnAddUserTypeMenu,
222               id=ID_NETWORKEDITADDMENUUSERTYPE)
223
224     def _init_coll_HelpMenu_Items(self, parent):
225         parent.Append(help='', id=wx.ID_HELP,
226               kind=wx.ITEM_NORMAL, text='DS-301 Standard\tF1')
227         self.Bind(wx.EVT_MENU, self.OnHelpDS301Menu, id=wx.ID_HELP)
228         parent.Append(help='', id=wx.ID_HELP_CONTEXT,
229               kind=wx.ITEM_NORMAL, text='CAN Festival Docs\tF2')
230         self.Bind(wx.EVT_MENU, self.OnHelpCANFestivalMenu, id=wx.ID_HELP_CONTEXT)
231         if Html_Window and self.ModeSolo:
232             parent.Append(help='', id=wx.ID_ABOUT,
233                   kind=wx.ITEM_NORMAL, text='About')
234             self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
235
236     def _init_coll_HelpBar_Fields(self, parent):
237         parent.SetFieldsCount(3)
238
239         parent.SetStatusText(number=0, text='')
240         parent.SetStatusText(number=1, text='')
241         parent.SetStatusText(number=2, text='')
242
243         parent.SetStatusWidths([100, 110, -1])
244
245     def _init_utils(self):
246         self.MenuBar = wx.MenuBar()
247         self.MenuBar.SetEvtHandlerEnabled(True)
248         
249         if self.ModeSolo:
250             self.FileMenu = wx.Menu(title='')
251         self.NetworkMenu = wx.Menu(title='')
252         self.EditMenu = wx.Menu(title='')
253         self.AddMenu = wx.Menu(title='')
254         self.HelpMenu = wx.Menu(title='')
255
256         self._init_coll_MenuBar_Menus(self.MenuBar)
257         if self.ModeSolo:
258             self._init_coll_FileMenu_Items(self.FileMenu)
259         self._init_coll_NetworkMenu_Items(self.NetworkMenu)
260         self._init_coll_EditMenu_Items(self.EditMenu)
261         self._init_coll_AddMenu_Items(self.AddMenu)
262         self._init_coll_HelpMenu_Items(self.HelpMenu)
263
264     def _init_ctrls(self, prnt):
265         wx.Frame.__init__(self, id=ID_NETWORKEDIT, name='networkedit',
266               parent=prnt, pos=wx.Point(149, 178), size=wx.Size(1000, 700),
267               style=wx.DEFAULT_FRAME_STYLE, title='Networkedit')
268         self._init_utils()
269         self.SetClientSize(wx.Size(1000, 700))
270         self.SetMenuBar(self.MenuBar)
271         self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
272         if not self.ModeSolo:
273             self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
274             accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL, 83, wx.ID_SAVE)])
275             self.SetAcceleratorTable(accel)
276
277         self.NetworkNodes = wx.Notebook(id=ID_NETWORKEDITNETWORKNODES,
278               name='NetworkNodes', parent=self, pos=wx.Point(0, 0),
279               size=wx.Size(0, 0), style=wx.NB_LEFT)
280         self.NetworkNodes.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED,
281               self.OnNodeSelectedChanged, id=ID_NETWORKEDITNETWORKNODES)
282
283         self.HelpBar = wx.StatusBar(id=ID_NETWORKEDITHELPBAR, name='HelpBar',
284               parent=self, style=wx.ST_SIZEGRIP)
285         self._init_coll_HelpBar_Fields(self.HelpBar)
286         self.SetStatusBar(self.HelpBar)
287
288     def __init__(self, parent, nodelist = None, projectOpen = None):
289         self.ModeSolo = nodelist == None
290         self._init_ctrls(parent)
291         self.HtmlFrameOpened = []
292         self.BusId = None
293         
294         icon = wx.Icon(os.path.join(ScriptDirectory,"networkedit.ico"),wx.BITMAP_TYPE_ICO)
295         self.SetIcon(icon)
296                  
297         if self.ModeSolo:
298             self.Manager = NodeManager()
299             if projectOpen:
300                 self.NodeList = NodeList(self.Manager)
301                 result = self.NodeList.LoadProject(projectOpen)
302                 if not result:
303                     self.RefreshNetworkNodes()
304             else:
305                 self.NodeList = None
306         else:
307             self.NodeList = nodelist
308             self.Manager = self.NodeList.GetManager()
309             self.NodeList.SetCurrentSelected(0)
310             self.RefreshNetworkNodes()
311             self.RefreshProfileMenu()
312             self.NetworkNodes.SetFocus()
313         
314         self.RefreshBufferState()
315         self.RefreshTitle()
316         self.RefreshMainMenu()
317
318     def SetBusId(self, bus_id):
319         self.BusId = bus_id
320
321     def GetBusId(self):
322         return self.BusId
323
324     def GetCurrentNodeId(self):
325         selected = self.NetworkNodes.GetSelection()
326         # At init selected = -1
327         if selected > 0:
328             window = self.NetworkNodes.GetPage(selected)
329             return window.GetIndex()
330         else:
331             return 0
332
333     def OnCloseFrame(self, event):
334         if not self.ModeSolo and getattr(self, "_onclose", None) != None:
335             self._onclose()
336         event.Skip()
337
338     def OnChar(self, event):
339         if event.ControlDown() and event.GetKeyCode() == 83 and getattr(self, "_onsave", None) != None:
340             self._onsave()
341         event.Skip()
342
343     def OnQuitMenu(self, event):
344         self.Close()
345         event.Skip()
346
347     def OnAddSDOServerMenu(self, event):
348         self.Manager.AddSDOServerToCurrent()
349         self.RefreshBufferState()
350         self.RefreshCurrentIndexList()
351         event.Skip()
352     
353     def OnAddSDOClientMenu(self, event):
354         self.Manager.AddSDOClientToCurrent()
355         self.RefreshBufferState()
356         self.RefreshCurrentIndexList()
357         event.Skip()
358
359     def OnAddPDOTransmitMenu(self, event):
360         self.Manager.AddPDOTransmitToCurrent()
361         self.RefreshBufferState()
362         self.RefreshCurrentIndexList()
363         event.Skip()
364
365     def OnAddPDOReceiveMenu(self, event):
366         self.Manager.AddPDOReceiveToCurrent()
367         self.RefreshBufferState()
368         self.RefreshCurrentIndexList()
369         event.Skip()
370
371     def OnAddMapVariableMenu(self, event):
372         self.AddMapVariable()
373         event.Skip()
374
375     def OnAddUserTypeMenu(self, event):
376         self.AddUserType()
377         event.Skip()
378
379     def OnNodeSelectedChanged(self, event):
380         selected = event.GetSelection()
381         # At init selected = -1
382         if selected >= 0:
383             window = self.NetworkNodes.GetPage(selected)
384             self.NodeList.SetCurrentSelected(window.GetIndex())
385         wx.CallAfter(self.RefreshMainMenu)
386         wx.CallAfter(self.RefreshStatusBar)
387         event.Skip()
388
389 #-------------------------------------------------------------------------------
390 #                         Load and Save Funtions
391 #-------------------------------------------------------------------------------
392
393     def OnNewProjectMenu(self, event):
394         if self.NodeList:
395             defaultpath = os.path.dirname(self.NodeList.GetRoot())
396         else:
397             defaultpath = os.getcwd()
398         dialog = wx.DirDialog(self , "Choose a project", defaultpath, wx.DD_NEW_DIR_BUTTON)
399         if dialog.ShowModal() == wx.ID_OK:
400             projectpath = dialog.GetPath()
401             if os.path.isdir(projectpath) and len(os.listdir(projectpath)) == 0:
402                 manager = NodeManager()
403                 nodelist = NodeList(manager)
404                 result = nodelist.LoadProject(projectpath)
405                 if not result:
406                     self.Manager = manager
407                     self.NodeList = nodelist
408                     self.NodeList.SetCurrentSelected(0)
409                                         
410                     self.RefreshNetworkNodes()
411                     self.RefreshBufferState()
412                     self.RefreshTitle()
413                     self.RefreshProfileMenu()
414                     self.RefreshMainMenu()
415                 else:
416                     message = wx.MessageDialog(self, result, "ERROR", wx.OK|wx.ICON_ERROR)
417                     message.ShowModal()
418                     message.Destroy()
419         event.Skip()
420
421     def OnOpenProjectMenu(self, event):
422         if self.NodeList:
423             defaultpath = os.path.dirname(self.NodeList.GetRoot())
424         else:
425             defaultpath = os.getcwd()
426         dialog = wx.DirDialog(self , "Choose a project", defaultpath, 0)
427         if dialog.ShowModal() == wx.ID_OK:
428             projectpath = dialog.GetPath()
429             if os.path.isdir(projectpath):
430                 manager = NodeManager()
431                 nodelist = NodeList(manager)
432                 result = nodelist.LoadProject(projectpath)
433                 if not result:
434                     self.Manager = manager
435                     self.NodeList = nodelist
436                     self.NodeList.SetCurrentSelected(0)
437                     
438                     self.RefreshNetworkNodes()
439                     self.RefreshBufferState()
440                     self.RefreshTitle()
441                     self.RefreshProfileMenu()
442                     self.RefreshMainMenu()
443                 else:
444                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
445                     message.ShowModal()
446                     message.Destroy()
447         dialog.Destroy()
448         event.Skip()
449
450     def OnSaveProjectMenu(self, event):
451         if not self.ModeSolo and getattr(self, "_onsave", None) != None:
452             self._onsave()
453         else:
454             result = self.NodeList.SaveProject()
455             if result:
456                 message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
457                 message.ShowModal()
458                 message.Destroy()
459         event.Skip()
460
461     def OnCloseProjectMenu(self, event):
462         if self.NodeList:
463             if self.NodeList.HasChanged():
464                 dialog = wx.MessageDialog(self, "There are changes, do you want to save?",  "Close Project", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
465                 answer = dialog.ShowModal()
466                 dialog.Destroy()
467                 if answer == wx.ID_YES:
468                     result = self.NodeList.SaveProject()
469                     if result:
470                         message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
471                         message.ShowModal()
472                         message.Destroy()
473                 elif answer == wx.ID_NO:
474                     self.NodeList.ForceChanged(False)
475             if not self.NodeList.HasChanged():
476                 self.Manager = None
477                 self.NodeList = None
478                 self.RefreshNetworkNodes()
479                 self.RefreshTitle()
480                 self.RefreshMainMenu()
481         event.Skip()
482
483 #-------------------------------------------------------------------------------
484 #                             Slave Nodes Management
485 #-------------------------------------------------------------------------------
486
487     def OnAddSlaveMenu(self, event):
488         dialog = AddSlaveDialog(self)
489         dialog.SetNodeList(self.NodeList)
490         if dialog.ShowModal() == wx.ID_OK:
491             values = dialog.GetValues()
492             result = self.NodeList.AddSlaveNode(values["slaveName"], values["slaveNodeID"], values["edsFile"])
493             if not result:
494                 new_editingpanel = EditingPanel(self.NetworkNodes, self, self.NodeList, False)
495                 new_editingpanel.SetIndex(values["slaveNodeID"])
496                 idx = self.NodeList.GetOrderNumber(values["slaveNodeID"])
497                 self.NetworkNodes.InsertPage(idx, new_editingpanel, "")
498                 self.NodeList.SetCurrentSelected(idx)
499                 self.NetworkNodes.SetSelection(idx)
500                 self.RefreshBufferState()
501             else:
502                 message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
503                 message.ShowModal()
504                 message.Destroy()
505         dialog.Destroy()
506         event.Skip()
507
508     def OnRemoveSlaveMenu(self, event):
509         slavenames = self.NodeList.GetSlaveNames()
510         slaveids = self.NodeList.GetSlaveIDs()
511         dialog = wx.SingleChoiceDialog(self, "Choose a slave to remove", "Remove slave", slavenames)
512         if dialog.ShowModal() == wx.ID_OK:
513             choice = dialog.GetSelection()
514             result = self.NodeList.RemoveSlaveNode(slaveids[choice])
515             if not result:
516                 slaveids.pop(choice)
517                 current = self.NetworkNodes.GetSelection()
518                 self.NetworkNodes.DeletePage(choice + 1)
519                 if self.NetworkNodes.GetPageCount() > 0:
520                     new_selection = min(current, self.NetworkNodes.GetPageCount() - 1)
521                     self.NetworkNodes.SetSelection(new_selection)
522                     if new_selection > 0:
523                         self.NodeList.SetCurrentSelected(slaveids[new_selection - 1])
524                     self.RefreshBufferState()
525             else:
526                 message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
527                 message.ShowModal()
528                 message.Destroy()
529         event.Skip()
530
531 #-------------------------------------------------------------------------------
532 #                             Refresh Functions
533 #-------------------------------------------------------------------------------
534
535     def RefreshTitle(self):
536         if self.NodeList != None:
537             self.SetTitle("Networkedit - %s"%self.NodeList.GetNetworkName())
538         else:
539             self.SetTitle("Networkedit")
540
541     def OnRefreshMenu(self, event):
542         self.RefreshCurrentIndexList()
543         event.Skip()
544
545     def RefreshCurrentIndexList(self):
546         selected = self.NetworkNodes.GetSelection()
547         if selected == 0:
548             window = self.NetworkNodes.GetPage(selected)
549             window.RefreshIndexList()
550         else:
551             pass
552
553     def RefreshNetworkNodes(self):
554         if self.NetworkNodes.GetPageCount() > 0:
555             self.NetworkNodes.DeleteAllPages()
556         if self.NodeList:
557             new_editingpanel = EditingPanel(self.NetworkNodes, self, self.Manager)
558             new_editingpanel.SetIndex(0)
559             self.NetworkNodes.AddPage(new_editingpanel, "")
560             for idx in self.NodeList.GetSlaveIDs():
561                 new_editingpanel = EditingPanel(self.NetworkNodes, self, self.NodeList, False)
562                 new_editingpanel.SetIndex(idx)
563                 self.NetworkNodes.AddPage(new_editingpanel, "")
564
565     def RefreshStatusBar(self):
566         if self:
567             selected = self.NetworkNodes.GetSelection()
568             if self.HelpBar and selected >= 0:
569                 window = self.NetworkNodes.GetPage(selected)
570                 selection = window.GetSelection()
571                 if selection:
572                     index, subIndex = selection
573                     if self.NodeList.IsCurrentEntry(index):
574                         self.HelpBar.SetStatusText("Index: 0x%04X"%index, 0)
575                         self.HelpBar.SetStatusText("Subindex: 0x%02X"%subIndex, 1)
576                         entryinfos = self.NodeList.GetEntryInfos(index)
577                         name = entryinfos["name"]
578                         category = "Optional"
579                         if entryinfos["need"]:
580                             category = "Mandatory"
581                         struct = "VAR"
582                         number = ""
583                         if entryinfos["struct"] & OD_IdenticalIndexes:
584                             number = " possibly defined %d times"%entryinfos["nbmax"]
585                         if entryinfos["struct"] & OD_IdenticalSubindexes:
586                             struct = "REC"
587                         elif entryinfos["struct"] & OD_MultipleSubindexes:
588                             struct = "ARRAY"
589                         text = "%s: %s entry of struct %s%s."%(name,category,struct,number)
590                         self.HelpBar.SetStatusText(text, 2)
591                     else:
592                         for i in xrange(3):
593                             self.HelpBar.SetStatusText("", i)
594                 else:
595                     for i in xrange(3):
596                         self.HelpBar.SetStatusText("", i)
597
598     def RefreshMainMenu(self):
599         if self.MenuBar:
600             self.NetworkMenu.Enable(ID_NETWORKEDITNETWORKMENUBUILDMASTER, False)
601             if self.NodeList == None:
602                 if self.ModeSolo:
603                     self.MenuBar.EnableTop(1, False)
604                     self.MenuBar.EnableTop(2, False)
605                     self.MenuBar.EnableTop(3, False)
606                     if self.FileMenu:
607                         self.FileMenu.Enable(wx.ID_CLOSE, False)
608                         self.FileMenu.Enable(wx.ID_SAVE, False)
609                 else:
610                     self.MenuBar.EnableTop(0, False)
611                     self.MenuBar.EnableTop(1, False)
612                     self.MenuBar.EnableTop(2, False)
613             else:
614                 if self.ModeSolo:
615                     self.MenuBar.EnableTop(1, True)
616                     if self.FileMenu:
617                         self.FileMenu.Enable(wx.ID_CLOSE, True)
618                         self.FileMenu.Enable(wx.ID_SAVE, True)
619                     if self.NetworkNodes.GetSelection() == 0:
620                         self.MenuBar.EnableTop(2, True)
621                         self.MenuBar.EnableTop(3, True)
622                     else:
623                         self.MenuBar.EnableTop(2, False)      
624                         self.MenuBar.EnableTop(3, False)
625                 else:
626                     self.MenuBar.EnableTop(0, True)
627                     if self.NetworkNodes.GetSelection() == 0:
628                         self.MenuBar.EnableTop(1, True)
629                         self.MenuBar.EnableTop(2, True)
630                     else:
631                         self.MenuBar.EnableTop(1, False)      
632                         self.MenuBar.EnableTop(2, False)
633
634     def RefreshProfileMenu(self):
635         if self.EditMenu:
636             profile = self.Manager.GetCurrentProfileName()
637             edititem = self.EditMenu.FindItemById(ID_NETWORKEDITEDITMENUOTHERPROFILE)
638             if edititem:
639                 length = self.AddMenu.GetMenuItemCount()
640                 for i in xrange(length-6):
641                     additem = self.AddMenu.FindItemByPosition(6)
642                     self.AddMenu.Delete(additem.GetId())
643                 if profile not in ("None", "DS-301"):
644                     edititem.SetText("%s Profile"%profile)
645                     edititem.Enable(True)
646                     self.AddMenu.AppendSeparator()
647                     for text, indexes in self.Manager.GetCurrentSpecificMenu():
648                         new_id = wx.NewId()
649                         self.AddMenu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=text)
650                         self.Bind(wx.EVT_MENU, self.GetProfileCallBack(text), id=new_id)
651                 else:
652                     edititem.SetText("Other Profile")
653                     edititem.Enable(False)
654
655     def GetProfileCallBack(self, text):
656         def ProfileCallBack(event):
657             self.Manager.AddSpecificEntryToCurrent(text)
658             self.RefreshBufferState()
659             self.RefreshCurrentIndexList()
660             event.Skip()
661         return ProfileCallBack
662
663 #-------------------------------------------------------------------------------
664 #                              Buffer Functions
665 #-------------------------------------------------------------------------------
666
667     def RefreshBufferState(self):
668         if self.NodeList:
669             nodeID = self.Manager.GetCurrentNodeID()
670             if nodeID != None:
671                 nodename = "0x%2.2X %s"%(nodeID, self.Manager.GetCurrentNodeName())
672             else:
673                 nodename = self.Manager.GetCurrentNodeName()
674             self.NetworkNodes.SetPageText(0, nodename)
675             for idx, name in enumerate(self.NodeList.GetSlaveNames()):
676                 self.NetworkNodes.SetPageText(idx + 1, name)
677             self.RefreshTitle()
678
679     def OnUndoMenu(self, event):
680         self.Manager.LoadCurrentPrevious()
681         self.RefreshCurrentIndexList()
682         self.RefreshBufferState()
683         event.Skip()
684
685     def OnRedoMenu(self, event):
686         self.Manager.LoadCurrentNext()
687         self.RefreshCurrentIndexList()
688         self.RefreshBufferState()
689         event.Skip()
690
691 #-------------------------------------------------------------------------------
692 #                                Help Method
693 #-------------------------------------------------------------------------------
694
695     def OnHelpDS301Menu(self, event):
696         find_index = False
697         selected = self.NetworkNodes.GetSelection()
698         if selected >= 0:
699             window = self.NetworkNodes.GetPage(selected)
700             result = window.GetSelection()
701             if result:
702                 find_index = True
703                 index, subIndex = result
704                 result = OpenPDFDocIndex(index, ScriptDirectory)
705                 if type(result) == StringType:
706                     message = wx.MessageDialog(self, result, "ERROR", wx.OK|wx.ICON_ERROR)
707                     message.ShowModal()
708                     message.Destroy()
709         if not find_index:
710             result = OpenPDFDocIndex(None, ScriptDirectory)
711             if type(result) == StringType:
712                 message = wx.MessageDialog(self, result, "ERROR", wx.OK|wx.ICON_ERROR)
713                 message.ShowModal()
714                 message.Destroy()
715         event.Skip()
716         
717     def OnHelpCANFestivalMenu(self, event):
718         #self.OpenHtmlFrame("CAN Festival Reference", os.path.join(ScriptDirectory, "doc/canfestival.html"), wx.Size(1000, 600))
719         if wx.Platform == '__WXMSW__':
720             readerpath = get_acroversion()
721             readerexepath = os.path.join(readerpath,"AcroRd32.exe")
722             if(os.path.isfile(readerexepath)):
723                 os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"'%os.path.join(ScriptDirectory, "doc","manual_en.pdf"))
724         else:
725             os.system("xpdf -remote CANFESTIVAL %s %d &"%(os.path.join(ScriptDirectory, "doc/manual_en.pdf"),16))
726         event.Skip()
727
728     def OnAboutMenu(self, event):
729         self.OpenHtmlFrame("About CAN Festival", os.path.join(ScriptDirectory, "doc/about.html"), wx.Size(500, 450))
730         event.Skip()
731
732     def OpenHtmlFrame(self, title, file, size):
733         if title not in self.HtmlFrameOpened:
734             self.HtmlFrameOpened.append(title)
735             window = HtmlFrame(self, self.HtmlFrameOpened)
736             window.SetTitle(title)
737             window.SetHtmlPage(file)
738             window.SetClientSize(size)
739             window.Show()
740
741 #-------------------------------------------------------------------------------
742 #                          Editing Profiles functions
743 #-------------------------------------------------------------------------------
744
745     def OnCommunicationMenu(self, event):
746         dictionary,current = self.Manager.GetCurrentCommunicationLists()
747         self.EditProfile("Edit DS-301 Profile", dictionary, current)
748         event.Skip()
749     
750     def OnOtherCommunicationMenu(self, event):
751         dictionary,current = self.Manager.GetCurrentDS302Lists()
752         self.EditProfile("Edit DS-301 Profile", dictionary, current)
753         event.Skip()
754     
755     def OnEditProfileMenu(self, event):
756         title = "Edit %s Profile"%self.Manager.GetCurrentProfileName()
757         dictionary,current = self.Manager.GetCurrentProfileLists()
758         self.EditProfile(title, dictionary, current)
759         event.Skip()
760     
761     def EditProfile(self, title, dictionary, current):
762         dialog = CommunicationDialog(self)
763         dialog.SetTitle(title)
764         dialog.SetIndexDictionary(dictionary)
765         dialog.SetCurrentList(current)
766         dialog.RefreshLists()
767         if dialog.ShowModal() == wx.ID_OK:
768             new_profile = dialog.GetCurrentList()
769             addinglist = []
770             removinglist = []
771             for index in new_profile:
772                 if index not in current:
773                     addinglist.append(index)
774             for index in current:
775                 if index not in new_profile:
776                     removinglist.append(index)
777             self.Manager.ManageEntriesOfCurrent(addinglist, removinglist)
778             self.Manager.BufferCurrentNode()
779             self.RefreshBufferState()
780             self.RefreshCurrentIndexList()
781         dialog.Destroy()
782
783 #-------------------------------------------------------------------------------
784 #                         Edit Node informations function
785 #-------------------------------------------------------------------------------
786
787     def OnNodeInfosMenu(self, event):
788         dialog = NodeInfosDialog(self)
789         name, id, type, description = self.Manager.GetCurrentNodeInfos()
790         defaultstringsize = self.Manager.GetCurrentNodeDefaultStringSize()
791         dialog.SetValues(name, id, type, description, defaultstringsize)
792         if dialog.ShowModal() == wx.ID_OK:
793             name, id, type, description, defaultstringsize = dialog.GetValues()
794             self.Manager.SetCurrentNodeInfos(name, id, type, description)
795             self.Manager.SetCurrentNodeDefaultStringSize(defaultstringsize)
796             self.RefreshBufferState()
797             self.RefreshProfileMenu()
798             selected = self.NetworkNodes.GetSelection()
799             if selected >= 0:
800                 window = self.NetworkNodes.GetPage(selected)
801                 window.RefreshTable()
802         event.Skip()
803
804
805 #-------------------------------------------------------------------------------
806 #                           Add User Types and Variables
807 #-------------------------------------------------------------------------------
808         
809     def AddMapVariable(self):
810         index = self.Manager.GetCurrentNextMapIndex()
811         if index:
812             dialog = MapVariableDialog(self)
813             dialog.SetIndex(index)
814             if dialog.ShowModal() == wx.ID_OK:
815                 index, name, struct, number = dialog.GetValues()
816                 result = self.Manager.AddMapVariableToCurrent(index, name, struct, number)
817                 if type(result) != StringType:
818                     self.RefreshBufferState()
819                     self.RefreshCurrentIndexList()
820                 else:
821                     message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
822                     message.ShowModal()
823                     message.Destroy()
824             dialog.Destroy()
825         else:
826             message = wx.MessageDialog(self, result, "No map variable index left!", wx.OK|wx.ICON_ERROR)
827             message.ShowModal()
828             message.Destroy()
829         
830     def AddUserType(self):
831         dialog = UserTypeDialog(self)
832         dialog.SetTypeList(self.Manager.GetCustomisableTypes())
833         if dialog.ShowModal() == wx.ID_OK:
834             type, min, max, length = dialog.GetValues()
835             result = self.Manager.AddUserTypeToCurrent(type, min, max, length)
836             if not IsOfType(result, StringType):
837                 self.RefreshBufferState()
838                 self.RefreshCurrentIndexList()
839             else:
840                 message = wx.MessageDialog(self, result, "Error", wx.OK|wx.ICON_ERROR)
841                 message.ShowModal()
842                 message.Destroy()
843         dialog.Destroy()
844
845     def OpenMasterDCFDialog(self, node_id):
846         self.NetworkNodes.SetSelection(0)
847         self.NetworkNodes.GetPage(0).OpenDCFDialog(node_id)
848
849 #-------------------------------------------------------------------------------
850 #                               Exception Handler
851 #-------------------------------------------------------------------------------
852
853 Max_Traceback_List_Size = 20
854
855 def Display_Exception_Dialog(e_type,e_value,e_tb):
856     trcbck_lst = []
857     for i,line in enumerate(traceback.extract_tb(e_tb)):
858         trcbck = " " + str(i+1) + ". "
859         if line[0].find(os.getcwd()) == -1:
860             trcbck += "file : " + str(line[0]) + ",   "
861         else:
862             trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ",   "
863         trcbck += "line : " + str(line[1]) + ",   " + "function : " + str(line[2])
864         trcbck_lst.append(trcbck)
865         
866     # Allow clicking....
867     cap = wx.Window_GetCapture()
868     if cap:
869         cap.ReleaseMouse()
870
871     dlg = wx.SingleChoiceDialog(None, 
872         """
873 An error happens.
874
875 Click on OK for saving an error report.
876
877 Please contact LOLITech at:
878 +33 (0)3 29 52 95 67
879 bugs_networkedit@lolitech.fr
880
881
882 Error:
883 """ +
884         str(e_type) + " : " + str(e_value), 
885         "Error",
886         trcbck_lst)
887     try:
888         res = (dlg.ShowModal() == wx.ID_OK)
889     finally:
890         dlg.Destroy()
891
892     return res
893
894 def Display_Error_Dialog(e_value):
895     message = wx.MessageDialog(None, str(e_value), "Error", wx.OK|wx.ICON_ERROR)
896     message.ShowModal()
897     message.Destroy()
898
899 def get_last_traceback(tb):
900     while tb.tb_next:
901         tb = tb.tb_next
902     return tb
903
904
905 def format_namespace(d, indent='    '):
906     return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
907
908
909 ignored_exceptions = [] # a problem with a line in a module is only reported once per session
910
911 def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
912     
913     def handle_exception(e_type, e_value, e_traceback):
914         traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
915         last_tb = get_last_traceback(e_traceback)
916         ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
917         if str(e_value).startswith("!!!"):
918             Display_Error_Dialog(e_value)
919         elif ex not in ignored_exceptions:
920             ignored_exceptions.append(ex)
921             result = Display_Exception_Dialog(e_type,e_value,e_traceback)
922             if result:
923                 info = {
924                     'app-title' : wx.GetApp().GetAppName(), # app_title
925                     'app-version' : app_version,
926                     'wx-version' : wx.VERSION_STRING,
927                     'wx-platform' : wx.Platform,
928                     'python-version' : platform.python_version(), #sys.version.split()[0],
929                     'platform' : platform.platform(),
930                     'e-type' : e_type,
931                     'e-value' : e_value,
932                     'date' : time.ctime(),
933                     'cwd' : os.getcwd(),
934                     }
935                 if e_traceback:
936                     info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
937                     last_tb = get_last_traceback(e_traceback)
938                     exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
939                     info['locals'] = format_namespace(exception_locals)
940                     if 'self' in exception_locals:
941                         info['self'] = format_namespace(exception_locals['self'].__dict__)
942                 
943                 output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w')
944                 lst = info.keys()
945                 lst.sort()
946                 for a in lst:
947                     output.write(a+":\n"+str(info[a])+"\n\n")
948
949     #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
950     sys.excepthook = handle_exception
951
952 if __name__ == '__main__':
953     def usage():
954         print "\nUsage of networkedit.py :"
955         print "\n   %s [Projectpath]\n"%sys.argv[0]
956
957     try:
958         opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
959     except getopt.GetoptError:
960         # print help information and exit:
961         usage()
962         sys.exit(2)
963
964     for o, a in opts:
965         if o in ("-h", "--help"):
966             usage()
967             sys.exit()
968
969     if len(args) == 0:
970         projectOpen = None 
971     elif len(args) == 1:
972         projectOpen = args[0]
973     else:
974         usage()
975         sys.exit(2)
976     
977     app = wx.PySimpleApp()
978     wx.InitAllImageHandlers()
979     
980     # Install a exception handle for bug reports
981     AddExceptHook(os.getcwd(),__version__)
982     
983     frame = networkedit(None, projectOpen=projectOpen)
984
985     frame.Show()
986     app.MainLoop()