Beremiz.py
changeset 0 215982c73cd6
child 1 6e9f24fd1b98
equal deleted inserted replaced
-1:000000000000 0:215982c73cd6
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of Beremiz, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C): Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #Lesser General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 from wxPython.wx import *
       
    26 import wx
       
    27 from time import localtime
       
    28 from datetime import datetime
       
    29 
       
    30 import sys, os
       
    31 base_folder = os.path.split(sys.path[0])[0]
       
    32 sys.path.append(os.path.join(base_folder, "plcopeneditor"))
       
    33 sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
       
    34 
       
    35 from PLCOpenEditor import PLCOpenEditor, ProjectDialog
       
    36 from PLCControler import PLCControler
       
    37 
       
    38 from networkedit import networkedit
       
    39 from nodelist import NodeList
       
    40 from nodemanager import NodeManager
       
    41 
       
    42 import os, re, platform, sys, time, traceback, getopt
       
    43 
       
    44 __version__ = "$Revision$"
       
    45 
       
    46 def create(parent):
       
    47     return Beremiz(parent)
       
    48 
       
    49 def usage():
       
    50     print "\nUsage of Beremiz.py :"
       
    51     print "\n   %s [Projectpath]\n"%sys.argv[0]
       
    52 
       
    53 try:
       
    54     opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
       
    55 except getopt.GetoptError:
       
    56     # print help information and exit:
       
    57     usage()
       
    58     sys.exit(2)
       
    59 
       
    60 for o, a in opts:
       
    61     if o in ("-h", "--help"):
       
    62         usage()
       
    63         sys.exit()
       
    64 
       
    65 projectOpen = None
       
    66 if len(args) > 1:
       
    67     usage()
       
    68     sys.exit()
       
    69 elif len(args) == 1:
       
    70     projectOpen = args[0]
       
    71 CWD = sys.path[0]
       
    72 
       
    73 [wxID_BEREMIZ, wxID_BEREMIZLOGCONSOLE, wxID_BEREMIZEDITPLCBUTTON,
       
    74  wxID_BEREMIZBUILDBUTTON, wxID_BEREMIZSIMULATEBUTTON,
       
    75  wxID_BEREMIZRUNBUTTON, wxID_BEREMIZBUSLIST,
       
    76  wxID_BEREMIZADDBUSBUTTON, wxID_BEREMIZDELETEBUSBUTTON,
       
    77 ] = [wx.NewId() for _init_ctrls in range(9)]
       
    78 
       
    79 [wxID_BEREMIZFILEMENUITEMS0, wxID_BEREMIZFILEMENUITEMS1, 
       
    80  wxID_BEREMIZFILEMENUITEMS2, wxID_BEREMIZFILEMENUITEMS3, 
       
    81  wxID_BEREMIZFILEMENUITEMS5, wxID_BEREMIZFILEMENUITEMS7, 
       
    82 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(6)]
       
    83 
       
    84 [wxID_BEREMIZEDITMENUITEMS0, wxID_BEREMIZEDITMENUITEMS2, 
       
    85  wxID_BEREMIZEDITMENUITEMS3, 
       
    86 ] = [wx.NewId() for _init_coll_EditMenu_Items in range(3)]
       
    87 
       
    88 [wxID_BEREMIZRUNMENUITEMS0, wxID_BEREMIZRUNMENUITEMS2, 
       
    89  wxID_BEREMIZRUNMENUITEMS3, wxID_BEREMIZRUNMENUITEMS5, 
       
    90 ] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
       
    91 
       
    92 [wxID_BEREMIZHELPMENUITEMS0, wxID_BEREMIZHELPMENUITEMS1, 
       
    93 ] = [wx.NewId() for _init_coll_HelpMenu_Items in range(2)]
       
    94 
       
    95 class Beremiz(wx.Frame):
       
    96     
       
    97     def _init_coll_FileMenu_Items(self, parent):
       
    98         # generated method, don't edit
       
    99 
       
   100         parent.Append(help='', id=wxID_BEREMIZFILEMENUITEMS0,
       
   101               kind=wx.ITEM_NORMAL, text=u'New\tCTRL+N')
       
   102         parent.Append(help='', id=wxID_BEREMIZFILEMENUITEMS1,
       
   103               kind=wx.ITEM_NORMAL, text=u'Open\tCTRL+O')
       
   104         parent.Append(help='', id=wxID_BEREMIZFILEMENUITEMS2,
       
   105               kind=wx.ITEM_NORMAL, text=u'Save\tCTRL+S')
       
   106         parent.Append(help='', id=wxID_BEREMIZFILEMENUITEMS3,
       
   107               kind=wx.ITEM_NORMAL, text=u'Close Project')
       
   108         parent.AppendSeparator()
       
   109         parent.Append(help='', id=wxID_BEREMIZFILEMENUITEMS5,
       
   110               kind=wx.ITEM_NORMAL, text=u'Properties')
       
   111         parent.AppendSeparator()
       
   112         parent.Append(help='', id=wxID_BEREMIZFILEMENUITEMS7,
       
   113               kind=wx.ITEM_NORMAL, text=u'Quit\tCTRL+Q')
       
   114         self.Bind(wx.EVT_MENU, self.OnNewProjectMenu,
       
   115               id=wxID_BEREMIZFILEMENUITEMS0)
       
   116         self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu,
       
   117               id=wxID_BEREMIZFILEMENUITEMS1)
       
   118         self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu,
       
   119               id=wxID_BEREMIZFILEMENUITEMS2)
       
   120         self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu,
       
   121               id=wxID_BEREMIZFILEMENUITEMS3)
       
   122         self.Bind(wx.EVT_MENU, self.OnPropertiesMenu,
       
   123               id=wxID_BEREMIZFILEMENUITEMS5)
       
   124         self.Bind(wx.EVT_MENU, self.OnQuitMenu,
       
   125               id=wxID_BEREMIZFILEMENUITEMS7)
       
   126         
       
   127     def _init_coll_EditMenu_Items(self, parent):
       
   128         # generated method, don't edit
       
   129 
       
   130         parent.Append(help='', id=wxID_BEREMIZEDITMENUITEMS0,
       
   131               kind=wx.ITEM_NORMAL, text=u'Edit PLC\tCTRL+R')
       
   132         parent.AppendSeparator()
       
   133         parent.Append(help='', id=wxID_BEREMIZEDITMENUITEMS2,
       
   134               kind=wx.ITEM_NORMAL, text=u'Add Bus')
       
   135         parent.Append(help='', id=wxID_BEREMIZEDITMENUITEMS3,
       
   136               kind=wx.ITEM_NORMAL, text=u'Delete Bus')
       
   137         self.Bind(wx.EVT_MENU, self.OnEditPLCMenu,
       
   138               id=wxID_BEREMIZEDITMENUITEMS0)
       
   139         self.Bind(wx.EVT_MENU, self.OnAddBusMenu,
       
   140               id=wxID_BEREMIZEDITMENUITEMS2)
       
   141         self.Bind(wx.EVT_MENU, self.OnDeleteBusMenu,
       
   142               id=wxID_BEREMIZEDITMENUITEMS3)
       
   143     
       
   144     def _init_coll_RunMenu_Items(self, parent):
       
   145         # generated method, don't edit
       
   146 
       
   147         parent.Append(help='', id=wxID_BEREMIZRUNMENUITEMS0,
       
   148               kind=wx.ITEM_NORMAL, text=u'Build\tCTRL+R')
       
   149         parent.AppendSeparator()
       
   150         parent.Append(help='', id=wxID_BEREMIZRUNMENUITEMS2,
       
   151               kind=wx.ITEM_NORMAL, text=u'Simulate')
       
   152         parent.Append(help='', id=wxID_BEREMIZRUNMENUITEMS3,
       
   153               kind=wx.ITEM_NORMAL, text=u'Run')
       
   154         parent.AppendSeparator()
       
   155         parent.Append(help='', id=wxID_BEREMIZRUNMENUITEMS5,
       
   156               kind=wx.ITEM_NORMAL, text=u'Save Log')
       
   157         self.Bind(wx.EVT_MENU, self.OnEditPLCMenu,
       
   158               id=wxID_BEREMIZRUNMENUITEMS0)
       
   159         self.Bind(wx.EVT_MENU, self.OnSimulateMenu,
       
   160               id=wxID_BEREMIZRUNMENUITEMS2)
       
   161         self.Bind(wx.EVT_MENU, self.OnRunMenu,
       
   162               id=wxID_BEREMIZRUNMENUITEMS3)
       
   163         self.Bind(wx.EVT_MENU, self.OnSaveLogMenu,
       
   164               id=wxID_BEREMIZRUNMENUITEMS5)
       
   165     
       
   166     def _init_coll_HelpMenu_Items(self, parent):
       
   167         # generated method, don't edit
       
   168 
       
   169         parent.Append(help='', id=wxID_BEREMIZHELPMENUITEMS0,
       
   170               kind=wx.ITEM_NORMAL, text=u'Beremiz\tF1')
       
   171         parent.Append(help='', id=wxID_BEREMIZHELPMENUITEMS1,
       
   172               kind=wx.ITEM_NORMAL, text=u'About')
       
   173         self.Bind(wx.EVT_MENU, self.OnBeremizMenu,
       
   174               id=wxID_BEREMIZHELPMENUITEMS0)
       
   175         self.Bind(wx.EVT_MENU, self.OnAboutMenu,
       
   176               id=wxID_BEREMIZHELPMENUITEMS1)
       
   177     
       
   178     def _init_coll_menuBar1_Menus(self, parent):
       
   179         # generated method, don't edit
       
   180 
       
   181         parent.Append(menu=self.FileMenu, title=u'File')
       
   182         parent.Append(menu=self.EditMenu, title=u'Edit')
       
   183         parent.Append(menu=self.RunMenu, title=u'Run')
       
   184         parent.Append(menu=self.HelpMenu, title=u'Help')
       
   185     
       
   186     def _init_utils(self):
       
   187         # generated method, don't edit
       
   188         self.menuBar1 = wx.MenuBar()
       
   189 
       
   190         self.FileMenu = wx.Menu(title=u'')
       
   191 
       
   192         self.EditMenu = wx.Menu(title=u'')
       
   193         
       
   194         self.RunMenu = wx.Menu(title=u'')
       
   195         
       
   196         self.HelpMenu = wx.Menu(title=u'')
       
   197         
       
   198         self._init_coll_menuBar1_Menus(self.menuBar1)
       
   199         self._init_coll_FileMenu_Items(self.FileMenu)
       
   200         self._init_coll_EditMenu_Items(self.EditMenu)
       
   201         self._init_coll_RunMenu_Items(self.RunMenu)
       
   202         self._init_coll_HelpMenu_Items(self.HelpMenu)
       
   203     
       
   204     def _init_coll_MainGridSizer_Items(self, parent):
       
   205         # generated method, don't edit
       
   206 
       
   207         parent.AddSizer(self.ControlPanelSizer, 0, border=0, flag=wxGROW)
       
   208         parent.AddWindow(self.LogConsole, 0, border=0, flag=wxGROW)
       
   209         
       
   210     def _init_coll_MainGridSizer_Growables(self, parent):
       
   211         # generated method, don't edit
       
   212 
       
   213         parent.AddGrowableCol(0)
       
   214         parent.AddGrowableRow(1)
       
   215     
       
   216     def _init_coll_ControlPanelSizer_Items(self, parent):
       
   217         # generated method, don't edit
       
   218         
       
   219         parent.AddSizer(self.ControlButtonSizer, 0, border=0, flag=0)
       
   220         parent.AddWindow(self.BusList, 0, border=0, flag=wxGROW)
       
   221         parent.AddSizer(self.BusButtonSizer, 0, border=0, flag=0)
       
   222         
       
   223         
       
   224     def _init_coll_ControlPanelSizer_Growables(self, parent):
       
   225         # generated method, don't edit
       
   226 
       
   227         parent.AddGrowableCol(1)
       
   228         parent.AddGrowableRow(0)
       
   229     
       
   230     def _init_coll_ControlButtonSizer_Items(self, parent):
       
   231         # generated method, don't edit
       
   232 
       
   233         parent.AddWindow(self.EditPLCButton, 0, border=0, flag=0)
       
   234         parent.AddWindow(self.BuildButton, 0, border=0, flag=0)
       
   235         parent.AddWindow(self.SimulateButton, 0, border=0, flag=0)
       
   236         parent.AddWindow(self.RunButton, 0, border=0, flag=0)
       
   237 
       
   238     def _init_coll_BusButtonSizer_Items(self, parent):
       
   239         # generated method, don't edit
       
   240 
       
   241         parent.AddWindow(self.AddBusButton, 0, border=0, flag=0)
       
   242         parent.AddWindow(self.DeleteBusButton, 0, border=0, flag=0)
       
   243         
       
   244     def _init_sizers(self):
       
   245         # generated method, don't edit
       
   246         self.MainGridSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2)
       
   247         
       
   248         self.ControlPanelSizer = wx.FlexGridSizer(cols=3, hgap=2, rows=1, vgap=2)
       
   249         
       
   250         self.ControlButtonSizer = wx.GridSizer(cols=2, hgap=2, rows=2, vgap=2)
       
   251         
       
   252         self.BusButtonSizer = wx.BoxSizer(wxVERTICAL)
       
   253         
       
   254         self._init_coll_MainGridSizer_Growables(self.MainGridSizer)
       
   255         self._init_coll_MainGridSizer_Items(self.MainGridSizer)
       
   256         self._init_coll_ControlPanelSizer_Growables(self.ControlPanelSizer)
       
   257         self._init_coll_ControlPanelSizer_Items(self.ControlPanelSizer)
       
   258         self._init_coll_ControlButtonSizer_Items(self.ControlButtonSizer)
       
   259         self._init_coll_BusButtonSizer_Items(self.BusButtonSizer)
       
   260         
       
   261         self.SetSizer(self.MainGridSizer)
       
   262     
       
   263     def _init_ctrls(self, prnt):
       
   264         # generated method, don't edit
       
   265         wx.Frame.__init__(self, id=wxID_BEREMIZ, name=u'Beremiz',
       
   266               parent=prnt, pos=wx.Point(0, 0), size=wx.Size(600, 300),
       
   267               style=wx.DEFAULT_FRAME_STYLE, title=u'Beremiz')
       
   268         self._init_utils()
       
   269         self.SetClientSize(wx.Size(600, 300))
       
   270         self.SetMenuBar(self.menuBar1)
       
   271         
       
   272         self.LogConsole = wx.TextCtrl(id=wxID_BEREMIZLOGCONSOLE, value='',
       
   273               name='LogConsole', parent=self, pos=wx.Point(0, 0),
       
   274               size=wx.Size(400, 200), style=wxTE_MULTILINE)
       
   275         
       
   276         self.EditPLCButton = wx.Button(id=wxID_BEREMIZEDITPLCBUTTON, label='Edit\nPLC',
       
   277               name='EditPLCButton', parent=self, pos=wx.Point(0, 0),
       
   278               size=wx.Size(48, 48), style=0)
       
   279         self.EditPLCButton.Bind(wx.EVT_BUTTON, self.OnEditPLCButton,
       
   280               id=wxID_BEREMIZEDITPLCBUTTON)
       
   281         
       
   282         self.BuildButton = wx.Button(id=wxID_BEREMIZBUILDBUTTON, label='Build',
       
   283               name='BuildButton', parent=self, pos=wx.Point(0, 0),
       
   284               size=wx.Size(48, 48), style=0)
       
   285         self.BuildButton.Bind(wx.EVT_BUTTON, self.OnBuildButton,
       
   286               id=wxID_BEREMIZBUILDBUTTON)
       
   287         
       
   288         self.SimulateButton = wx.Button(id=wxID_BEREMIZSIMULATEBUTTON, label='Simul',
       
   289               name='SimulateButton', parent=self, pos=wx.Point(0, 0),
       
   290               size=wx.Size(48, 48), style=0)
       
   291         self.EditPLCButton.Bind(wx.EVT_BUTTON, self.OnSimulateButton,
       
   292               id=wxID_BEREMIZSIMULATEBUTTON)
       
   293         
       
   294         self.RunButton = wx.Button(id=wxID_BEREMIZRUNBUTTON, label='Run',
       
   295               name='RunButton', parent=self, pos=wx.Point(0, 0),
       
   296               size=wx.Size(48, 48), style=0)
       
   297         self.RunButton.Bind(wx.EVT_BUTTON, self.OnRunButton,
       
   298               id=wxID_BEREMIZRUNBUTTON)
       
   299         
       
   300         self.BusList = wx.ListBox(choices=[], id=wxID_BEREMIZBUSLIST,
       
   301               name='BusList', parent=self, pos=wx.Point(0, 0),
       
   302               size=wx.Size(-1, -1), style=0)
       
   303         self.BusList.Bind(wx.EVT_LEFT_DCLICK, self.OnBusListDClick)
       
   304         
       
   305         self.AddBusButton = wx.Button(id=wxID_BEREMIZADDBUSBUTTON, label='Add',
       
   306               name='AddBusButton', parent=self, pos=wx.Point(0, 0),
       
   307               size=wx.Size(48, 48), style=0)
       
   308         self.AddBusButton.Bind(wx.EVT_BUTTON, self.OnAddBusButton,
       
   309               id=wxID_BEREMIZADDBUSBUTTON)
       
   310         
       
   311         self.DeleteBusButton = wx.Button(id=wxID_BEREMIZDELETEBUSBUTTON, label='Delete',
       
   312               name='DeleteBusButton', parent=self, pos=wx.Point(0, 0),
       
   313               size=wx.Size(48, 48), style=0)
       
   314         self.DeleteBusButton.Bind(wx.EVT_BUTTON, self.OnDeleteBusButton,
       
   315               id=wxID_BEREMIZDELETEBUSBUTTON)
       
   316         
       
   317         self._init_sizers()
       
   318     
       
   319     def __init__(self, parent):
       
   320         self._init_ctrls(parent)
       
   321         
       
   322         self.CurrentProjectPath = ""
       
   323         
       
   324         self.PLCManager = None
       
   325         self.PLCEditor = None
       
   326         self.BusManagers = {}
       
   327         
       
   328         self.RefreshButtons()
       
   329         self.RefreshMainMenu()
       
   330         
       
   331     def RefreshButtons(self):
       
   332         if self.CurrentProjectPath == "":
       
   333             self.LogConsole.Enable(False)
       
   334             self.EditPLCButton.Enable(False)
       
   335             self.BuildButton.Enable(False)
       
   336             self.SimulateButton.Enable(False)
       
   337             self.RunButton.Enable(False)
       
   338             self.BusList.Enable(False)
       
   339             self.AddBusButton.Enable(False)
       
   340             self.DeleteBusButton.Enable(False)
       
   341         else:
       
   342             self.LogConsole.Enable(True)
       
   343             self.EditPLCButton.Enable(True)
       
   344             self.BuildButton.Enable(True)
       
   345             self.SimulateButton.Enable(True)
       
   346             self.RunButton.Enable(True)
       
   347             self.BusList.Enable(True)
       
   348             self.AddBusButton.Enable(True)
       
   349             self.DeleteBusButton.Enable(True)
       
   350 
       
   351     def RefreshBusList(self):
       
   352         selected = self.BusList.GetStringSelection()
       
   353         self.BusList.Clear()
       
   354         busidlist = self.BusManagers.keys()
       
   355         busidlist.sort()
       
   356         for id in busidlist:
       
   357             bus_infos = self.BusManagers[id]
       
   358             self.BusList.Append("0x%2.2X\t%s\t%s"%(id, bus_infos["Type"], bus_infos["Name"]))
       
   359         if selected != "":
       
   360             self.BusList.SetStringSelection(selected)
       
   361 
       
   362     def RefreshMainMenu(self):
       
   363         if self.menuBar1:
       
   364             if self.CurrentProjectPath == "":
       
   365                 self.menuBar1.EnableTop(1, False)
       
   366                 self.menuBar1.EnableTop(2, False)
       
   367                 self.FileMenu.Enable(wxID_BEREMIZFILEMENUITEMS2, False)
       
   368                 self.FileMenu.Enable(wxID_BEREMIZFILEMENUITEMS3, False)
       
   369                 self.FileMenu.Enable(wxID_BEREMIZFILEMENUITEMS5, False)
       
   370             else:
       
   371                 self.menuBar1.EnableTop(1, True)
       
   372                 self.menuBar1.EnableTop(2, True)
       
   373                 self.FileMenu.Enable(wxID_BEREMIZFILEMENUITEMS2, True)
       
   374                 self.FileMenu.Enable(wxID_BEREMIZFILEMENUITEMS3, True)
       
   375                 self.FileMenu.Enable(wxID_BEREMIZFILEMENUITEMS5, True)
       
   376 
       
   377     def OnNewProjectMenu(self, event):
       
   378         if self.CurrentProjectPath != "":
       
   379             defaultpath = self.CurrentProjectPath
       
   380         else:
       
   381             defaultpath = os.getcwd()
       
   382         dialog = wxDirDialog(self , "Choose a project", defaultpath, wxDD_NEW_DIR_BUTTON)
       
   383         if dialog.ShowModal() == wxID_OK:
       
   384             projectpath = dialog.GetPath()
       
   385             dialog.Destroy()
       
   386             if os.path.isdir(projectpath) and len(os.listdir(projectpath)) == 0:
       
   387                 os.mkdir(os.path.join(projectpath, "eds"))
       
   388                 self.PLCManager = PLCControler()
       
   389                 plc_file = os.path.join(projectpath, "plc.xml")
       
   390                 dialog = ProjectDialog(self)
       
   391                 if dialog.ShowModal() == wxID_OK:
       
   392                     values = dialog.GetValues()
       
   393                     projectname = values.pop("projectName")
       
   394                     values["creationDateTime"] = datetime(*localtime()[:6])
       
   395                     self.PLCManager.CreateNewProject(projectname)
       
   396                     self.PLCManager.SetProjectProperties(values)
       
   397                     self.PLCManager.SaveXMLFile(plc_file)
       
   398                     self.CurrentProjectPath = projectpath
       
   399                 dialog.Destroy()
       
   400                 self.RefreshButtons()
       
   401                 self.RefreshMainMenu()
       
   402             else:
       
   403                 message = wxMessageDialog(self, "Folder choosen isn't empty. You can't use it for a new project!", "ERROR", wxOK|wxICON_ERROR)
       
   404                 message.ShowModal()
       
   405                 message.Destroy()
       
   406         event.Skip()
       
   407     
       
   408     def OnOpenProjectMenu(self, event):
       
   409         if self.CurrentProjectPath != "":
       
   410             defaultpath = self.CurrentProjectPath
       
   411         else:
       
   412             defaultpath = os.getcwd()
       
   413         dialog = wxDirDialog(self , "Choose a project", defaultpath, wxDD_NEW_DIR_BUTTON)
       
   414         if dialog.ShowModal() == wxID_OK:
       
   415             projectpath = dialog.GetPath()
       
   416             dialog.Destroy()
       
   417             if os.path.isdir(projectpath):
       
   418                 self.BusManagers = {}
       
   419                 configpath = os.path.join(projectpath, ".project")
       
   420                 if os.path.isfile(configpath):
       
   421                     file = open(configpath, "r")
       
   422                     for bus_id, bus_type, bus_name in [line.strip().split(" ") for line in file.readlines() if line.strip() != ""]:
       
   423                         if bus_type == "CanFestival":
       
   424                             id = int(bus_id, 16)
       
   425                             manager = NodeManager(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
       
   426                             nodelist = NodeList(manager)
       
   427                             result = nodelist.LoadProject(projectpath, bus_name)
       
   428                             if not result:
       
   429                                 self.BusManagers[id] = {"Name" : bus_name, "Type" : bus_type, "NodeList" : nodelist, "Editor" : None}
       
   430                             else:
       
   431                                 message = wxMessageDialog(self, result, "Error", wxOK|wxICON_ERROR)
       
   432                                 message.ShowModal()
       
   433                                 message.Destroy()
       
   434                         else:
       
   435                             self.BusManagers[id] = {"Name" : bus_name, "Type" : bus_type}
       
   436                     file.close()
       
   437                 self.PLCManager = PLCControler()
       
   438                 plc_file = os.path.join(projectpath, "plc.xml")
       
   439                 if os.path.isfile(plc_file):
       
   440                     self.PLCManager.OpenXMLFile(plc_file)
       
   441                     self.CurrentProjectPath = projectpath
       
   442                 else:
       
   443                     dialog = ProjectDialog(self)
       
   444                     if dialog.ShowModal() == wxID_OK:
       
   445                         values = dialog.GetValues()
       
   446                         projectname = values.pop("projectName")
       
   447                         values["creationDateTime"] = datetime(*localtime()[:6])
       
   448                         self.PLCManager.CreateNewProject(projectname)
       
   449                         self.PLCManager.SetProjectProperties(values)
       
   450                         self.PLCManager.SaveXMLFile(plc_file)
       
   451                         self.CurrentProjectPath = projectpath
       
   452                     dialog.Destroy()
       
   453                 self.RefreshBusList()
       
   454                 self.RefreshButtons()
       
   455                 self.RefreshMainMenu()
       
   456         event.Skip()
       
   457     
       
   458     def OnCloseProjectMenu(self, event):
       
   459         self.PLCManager = None
       
   460         self.CurrentProjectPath = projectpath
       
   461         self.RefreshButtons()
       
   462         self.RefreshMainMenu()
       
   463         event.Skip()
       
   464     
       
   465     def OnSaveProjectMenu(self, event):
       
   466         self.PLCManager.SaveXMLFile()
       
   467         cpjfilepath = os.path.join(self.CurrentProjectPath, "nodelist.cpj")
       
   468         file = open(cpjfilepath, "w")
       
   469         file.write("")
       
   470         file.close()
       
   471         configpath = os.path.join(self.CurrentProjectPath, ".project")
       
   472         file = open(configpath, "w")
       
   473         busidlist = self.BusManagers.keys()
       
   474         busidlist.sort()
       
   475         for id in busidlist:
       
   476             bus_infos = self.BusManagers[id]
       
   477             file.write("0x%2.2X %s %s\n"%(id, bus_infos["Type"], bus_infos["Name"]))
       
   478             bus_infos["NodeList"].SaveProject(bus_infos["Name"])
       
   479         file.close()
       
   480         event.Skip()
       
   481     
       
   482     def OnPropertiesMenu(self, event):
       
   483         event.Skip()
       
   484     
       
   485     def OnQuitMenu(self, event):
       
   486         self.Close()
       
   487         event.Skip()
       
   488     
       
   489     def OnEditPLCMenu(self, event):
       
   490         self.EditPLC()
       
   491         event.Skip()
       
   492     
       
   493     def OnAddBusMenu(self, event):
       
   494         self.AddBus()
       
   495         event.Skip()
       
   496     
       
   497     def OnDeleteBusMenu(self, event):
       
   498         self.DeleteBus()
       
   499         event.Skip()
       
   500 
       
   501     def OnSimulateMenu(self, event):
       
   502         event.Skip()
       
   503     
       
   504     def OnRunMenu(self, event):
       
   505         event.Skip()
       
   506     
       
   507     def OnSaveLogMenu(self, event):
       
   508         event.Skip()
       
   509     
       
   510     def OnBeremizMenu(self, event):
       
   511         event.Skip()
       
   512     
       
   513     def OnAboutMenu(self, event):
       
   514         event.Skip()
       
   515 
       
   516     def OnEditPLCButton(self, event):
       
   517         self.EditPLC()
       
   518         event.Skip()
       
   519     
       
   520     def OnBuildButton(self, event):
       
   521         event.Skip()
       
   522     
       
   523     def OnSimulateButton(self, event):
       
   524         event.Skip()
       
   525         
       
   526     def OnRunButton(self, event):
       
   527         event.Skip()
       
   528     
       
   529     def OnAddBusButton(self, event):
       
   530         self.AddBus()
       
   531         event.Skip()
       
   532     
       
   533     def OnDeleteBusButton(self, event):
       
   534         self.DeleteBus()
       
   535         event.Skip()
       
   536     
       
   537     def OnBusListDClick(self, event):
       
   538         selected = self.BusList.GetSelection()
       
   539         busidlist = self.BusManagers.keys()
       
   540         busidlist.sort()
       
   541         bus_infos = self.BusManagers[busidlist[selected]]
       
   542         if bus_infos["Type"] == "CanFestival":
       
   543             if bus_infos["Editor"] == None:
       
   544                 netedit = networkedit(self, bus_infos["NodeList"])
       
   545                 netedit.SetBusId(busidlist[selected])
       
   546                 netedit.Show()
       
   547                 bus_infos["Editor"] = netedit
       
   548         event.Skip()
       
   549     
       
   550     def CloseEditor(self, bus_id):
       
   551         if self.BusManagers.get(bus_id, None) != None:
       
   552             self.BusManagers[bus_id]["Editor"] = None
       
   553     
       
   554     def AddBus(self):
       
   555         dialog = AddBusDialog(self)
       
   556         if dialog.ShowModal() == wxID_OK:
       
   557             values = dialog.GetValues()
       
   558             if values["busID"].startswith("0x"):
       
   559                 bus_id = int(values["busID"], 16)
       
   560             else:
       
   561                 bus_id = int(values["busID"])
       
   562             if self.BusManagers.get(bus_id, None) == None:
       
   563                 if values["busType"] == "CanFestival":
       
   564                     manager = NodeManager(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
       
   565                     nodelist = NodeList(manager)
       
   566                     result = nodelist.LoadProject(self.CurrentProjectPath, values["busName"])
       
   567                     if not result:
       
   568                         self.BusManagers[bus_id] = {"Name" : values["busName"], "Type" : values["busType"], "NodeList" : nodelist, "Editor" : None}
       
   569                     else:
       
   570                         message = wxMessageDialog(self, result, "Error", wxOK|wxICON_ERROR)
       
   571                         message.ShowModal()
       
   572                         message.Destroy()
       
   573                 else:
       
   574                     self.BusManagers[bus_id] = {"Name" : values["busName"], "Type" : values["busType"]}
       
   575             else:
       
   576                 message = wxMessageDialog(self, "The bus ID \"0x%2.2X\" is already used!"%bus_id, "Error", wxOK|wxICON_ERROR)
       
   577                 message.ShowModal()
       
   578                 message.Destroy()
       
   579             self.RefreshBusList()
       
   580         dialog.Destroy()
       
   581     
       
   582     def DeleteBus(self):
       
   583         busidlist = self.BusManagers.keys()
       
   584         busidlist.sort()
       
   585         list = ["0x%2.2X\t%s\t%s"%(id, self.BusManagers[id]["Type"], self.BusManagers[id]["Name"]) for id in busidlist]
       
   586         dialog = wxSingleChoiceDialog(self, "Select Bus to delete:", "Bus Delete", list, wxOK|wxCANCEL)
       
   587         if dialog.ShowModal() == wxID_OK:
       
   588             selected = dialog.GetSelection()
       
   589             editor = self.BusManagers[busidlist[selected]]["Editor"]
       
   590             if editor:
       
   591                 editor.Close()
       
   592             self.BusManagers.pop(busidlist[selected])
       
   593             self.RefreshBusList()
       
   594         dialog.Destroy()
       
   595     
       
   596     def EditPLC(self):
       
   597         if not self.PLCEditor:
       
   598             self.PLCEditor = PLCOpenEditor(self, self.PLCManager)
       
   599             self.PLCEditor.RefreshProjectTree()
       
   600             self.PLCEditor.RefreshFileMenu()
       
   601             self.PLCEditor.RefreshEditMenu()
       
   602             self.PLCEditor.RefreshToolBar()
       
   603             self.PLCEditor.Show()
       
   604 
       
   605 #-------------------------------------------------------------------------------
       
   606 #                             Add Bus Dialog
       
   607 #-------------------------------------------------------------------------------
       
   608 
       
   609 [wxID_ADDBUSDIALOG, wxID_ADDBUSDIALOGMAINPANEL, 
       
   610  wxID_ADDBUSDIALOGBUSID, wxID_ADDBUSDIALOGBUSNAME, 
       
   611  wxID_ADDBUSDIALOGBUSTYPE, wxID_ADDBUSDIALOGSTATICTEXT1,
       
   612  wxID_ADDBUSDIALOGSTATICTEXT2, wxID_ADDBUSDIALOGSTATICTEXT3,
       
   613 ] = [wx.NewId() for _init_ctrls in range(8)]
       
   614 
       
   615 class AddBusDialog(wx.Dialog):
       
   616     def _init_coll_flexGridSizer1_Items(self, parent):
       
   617         # generated method, don't edit
       
   618 
       
   619         parent.AddWindow(self.MainPanel, 0, border=0, flag=0)
       
   620 
       
   621     def _init_sizers(self):
       
   622         # generated method, don't edit
       
   623         self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
   624 
       
   625         self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
       
   626 
       
   627         self.SetSizer(self.flexGridSizer1)
       
   628 
       
   629     def _init_ctrls(self, prnt):
       
   630         # generated method, don't edit
       
   631         wx.Dialog.__init__(self, id=wxID_ADDBUSDIALOG,
       
   632               name='PouDialog', parent=prnt, pos=wx.Point(376, 183),
       
   633               size=wx.Size(300, 200), style=wx.DEFAULT_DIALOG_STYLE,
       
   634               title='Create a new POU')
       
   635         self.SetClientSize(wx.Size(300, 200))
       
   636 
       
   637         self.MainPanel = wx.Panel(id=wxID_ADDBUSDIALOGMAINPANEL,
       
   638               name='MainPanel', parent=self, pos=wx.Point(0, 0),
       
   639               size=wx.Size(300, 200), style=wx.TAB_TRAVERSAL)
       
   640         self.MainPanel.SetAutoLayout(True)
       
   641 
       
   642         self.staticText1 = wx.StaticText(id=wxID_ADDBUSDIALOGSTATICTEXT1,
       
   643               label='Bus ID:', name='staticText1', parent=self.MainPanel,
       
   644               pos=wx.Point(24, 24), size=wx.Size(95, 17), style=0)
       
   645 
       
   646         self.BusId = wx.TextCtrl(id=wxID_ADDBUSDIALOGBUSID,
       
   647               name='BusId', parent=self.MainPanel, pos=wx.Point(104, 24), 
       
   648               size=wx.Size(150, 24), style=0)
       
   649 
       
   650         self.staticText2 = wx.StaticText(id=wxID_ADDBUSDIALOGSTATICTEXT2,
       
   651               label='Bus Type:', name='staticText2', parent=self.MainPanel,
       
   652               pos=wx.Point(24, 64), size=wx.Size(95, 17), style=0)
       
   653 
       
   654         self.BusType = wx.Choice(id=wxID_ADDBUSDIALOGBUSTYPE,
       
   655               name='BusType', parent=self.MainPanel, pos=wx.Point(104, 64),
       
   656               size=wx.Size(150, 24), style=0)
       
   657         
       
   658         self.staticText3 = wx.StaticText(id=wxID_ADDBUSDIALOGSTATICTEXT3,
       
   659               label='Bus Name:', name='staticText3', parent=self.MainPanel,
       
   660               pos=wx.Point(24, 104), size=wx.Size(95, 17), style=0)
       
   661 
       
   662         self.BusName = wx.TextCtrl(id=wxID_ADDBUSDIALOGBUSNAME,
       
   663               name='BusName', parent=self.MainPanel, pos=wx.Point(104, 104),
       
   664               size=wx.Size(150, 24), style=0)
       
   665         
       
   666         self._init_sizers()
       
   667 
       
   668     def __init__(self, parent):
       
   669         self._init_ctrls(parent)
       
   670         self.ButtonSizer = self.CreateButtonSizer(wxOK|wxCANCEL|wxCENTRE)
       
   671         self.flexGridSizer1.Add(self.ButtonSizer, 1, wxALIGN_RIGHT)
       
   672         
       
   673         for option in ["CanFestival","SVGUI"]:
       
   674             self.BusType.Append(option)
       
   675         
       
   676         EVT_BUTTON(self, self.ButtonSizer.GetAffirmativeButton().GetId(), self.OnOK)
       
   677     
       
   678     def OnOK(self, event):
       
   679         error = []
       
   680         bus_id = self.BusId.GetValue()
       
   681         if bus_id == "":
       
   682             error.append("Bus ID")
       
   683         if self.BusType.GetStringSelection() == "":
       
   684             error.append("Bus Type")
       
   685         if self.BusName.GetValue() == "":
       
   686             error.append("Bus Name")
       
   687         if len(error) > 0:
       
   688             text = ""
       
   689             for i, item in enumerate(error):
       
   690                 if i == 0:
       
   691                     text += item
       
   692                 elif i == len(error) - 1:
       
   693                     text += " and %s"%item
       
   694                 else:
       
   695                     text += ", %s"%item 
       
   696             message = wxMessageDialog(self, "Form isn't complete. %s must be filled!"%text, "Error", wxOK|wxICON_ERROR)
       
   697             message.ShowModal()
       
   698             message.Destroy()
       
   699         elif bus_id.startswith("0x"):
       
   700             try:
       
   701                 bus_id = int(bus_id, 16)
       
   702                 self.EndModal(wxID_OK)
       
   703             except:
       
   704                 message = wxMessageDialog(self, "Bus ID must be a decimal or hexadecimal number!", "Error", wxOK|wxICON_ERROR)
       
   705                 message.ShowModal()
       
   706                 message.Destroy()
       
   707         elif not bus_id.startswith("-"):
       
   708             try:
       
   709                 bus_id = int(bus_id)
       
   710                 self.EndModal(wxID_OK)
       
   711             except:
       
   712                 message = wxMessageDialog(self, "Bus ID must be a decimal or hexadecimal number!", "Error", wxOK|wxICON_ERROR)
       
   713                 message.ShowModal()
       
   714                 message.Destroy()
       
   715         else:
       
   716             message = wxMessageDialog(self, "Bus Id must be greater than 0!", "Error", wxOK|wxICON_ERROR)
       
   717             message.ShowModal()
       
   718             message.Destroy()
       
   719 
       
   720     def SetValues(self, values):
       
   721         for item, value in values.items():
       
   722             if item == "busID":
       
   723                 self.BusID.SetValue(value)
       
   724             elif item == "busType":
       
   725                 self.BusType.SetStringSelection(value)
       
   726             elif item == "busName":
       
   727                 self.BusName.SetValue(value)
       
   728                 
       
   729     def GetValues(self):
       
   730         values = {}
       
   731         values["busID"] = self.BusId.GetValue()
       
   732         values["busType"] = self.BusType.GetStringSelection()
       
   733         values["busName"] = self.BusName.GetValue()
       
   734         return values
       
   735 
       
   736 #-------------------------------------------------------------------------------
       
   737 #                               Exception Handler
       
   738 #-------------------------------------------------------------------------------
       
   739 
       
   740 Max_Traceback_List_Size = 20
       
   741 
       
   742 def Display_Exception_Dialog(e_type,e_value,e_tb):
       
   743     trcbck_lst = []
       
   744     for i,line in enumerate(traceback.extract_tb(e_tb)):
       
   745         trcbck = " " + str(i+1) + ". "
       
   746         if line[0].find(os.getcwd()) == -1:
       
   747             trcbck += "file : " + str(line[0]) + ",   "
       
   748         else:
       
   749             trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ",   "
       
   750         trcbck += "line : " + str(line[1]) + ",   " + "function : " + str(line[2])
       
   751         trcbck_lst.append(trcbck)
       
   752         
       
   753     # Allow clicking....
       
   754     cap = wx.Window_GetCapture()
       
   755     if cap:
       
   756         cap.ReleaseMouse()
       
   757 
       
   758     dlg = wx.SingleChoiceDialog(None, 
       
   759         """
       
   760 An error happens.
       
   761 
       
   762 Click on OK for saving an error report.
       
   763 
       
   764 Please contact LOLITech at:
       
   765 +33 (0)3 29 52 95 67
       
   766 bugs_Beremiz@lolitech.fr
       
   767 
       
   768 
       
   769 Error:
       
   770 """ +
       
   771         str(e_type) + " : " + str(e_value), 
       
   772         "Error",
       
   773         trcbck_lst)
       
   774     try:
       
   775         res = (dlg.ShowModal() == wx.ID_OK)
       
   776     finally:
       
   777         dlg.Destroy()
       
   778 
       
   779     return res
       
   780 
       
   781 def Display_Error_Dialog(e_value):
       
   782     message = wxMessageDialog(None, str(e_value), "Error", wxOK|wxICON_ERROR)
       
   783     message.ShowModal()
       
   784     message.Destroy()
       
   785 
       
   786 def get_last_traceback(tb):
       
   787     while tb.tb_next:
       
   788         tb = tb.tb_next
       
   789     return tb
       
   790 
       
   791 
       
   792 def format_namespace(d, indent='    '):
       
   793     return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
       
   794 
       
   795 
       
   796 ignored_exceptions = [] # a problem with a line in a module is only reported once per session
       
   797 
       
   798 def wxAddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
       
   799     
       
   800     def handle_exception(e_type, e_value, e_traceback):
       
   801         traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
       
   802         last_tb = get_last_traceback(e_traceback)
       
   803         ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
       
   804         if str(e_value).startswith("!!!"):
       
   805             Display_Error_Dialog(e_value)
       
   806         elif ex not in ignored_exceptions:
       
   807             result = Display_Exception_Dialog(e_type,e_value,e_traceback)
       
   808             if result:
       
   809                 ignored_exceptions.append(ex)
       
   810                 info = {
       
   811                     'app-title' : wx.GetApp().GetAppName(), # app_title
       
   812                     'app-version' : app_version,
       
   813                     'wx-version' : wx.VERSION_STRING,
       
   814                     'wx-platform' : wx.Platform,
       
   815                     'python-version' : platform.python_version(), #sys.version.split()[0],
       
   816                     'platform' : platform.platform(),
       
   817                     'e-type' : e_type,
       
   818                     'e-value' : e_value,
       
   819                     'date' : time.ctime(),
       
   820                     'cwd' : os.getcwd(),
       
   821                     }
       
   822                 if e_traceback:
       
   823                     info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
       
   824                     last_tb = get_last_traceback(e_traceback)
       
   825                     exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
       
   826                     info['locals'] = format_namespace(exception_locals)
       
   827                     if 'self' in exception_locals:
       
   828                         info['self'] = format_namespace(exception_locals['self'].__dict__)
       
   829                 
       
   830                 output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w')
       
   831                 lst = info.keys()
       
   832                 lst.sort()
       
   833                 for a in lst:
       
   834                     output.write(a+":\n"+str(info[a])+"\n\n")
       
   835 
       
   836     #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
       
   837     sys.excepthook = handle_exception
       
   838 
       
   839 if __name__ == '__main__':
       
   840     app = wxPySimpleApp()
       
   841     wxInitAllImageHandlers()
       
   842     
       
   843     # Install a exception handle for bug reports
       
   844     wxAddExceptHook(os.getcwd(),__version__)
       
   845     
       
   846     frame = Beremiz(None)
       
   847 
       
   848     frame.Show()
       
   849     app.MainLoop()