def build(self):

        #Build project
        self.cflags = ["-DPLC_MD5=" + self.calc_md5()]

        if toolchain_gcc.build(self):
            #Run objcopy on success
            self.CTRInstance.logger.write("   [OBJCOPY]  " + self.exe +
                                          " -> " + self.exe + ".bin\n")

            #objcpy = [self.toolchain_prefix + "objcopy",  "-O", "ihex", self.exe_path, self.exe_path + ".hex"]
            objcpy = [
                self.toolchain_prefix + "objcopy", "-O", "binary",
                self.exe_path, self.exe_path + ".bin"
            ]
            ProcessLogger(self.CTRInstance.logger, objcpy).spin()

            self.CTRInstance.logger.write("Output size:\n")

            size = [self.toolchain_prefix + "size", self.exe_path]
            ProcessLogger(self.CTRInstance.logger, size).spin()

            return True

        return False
示例#2
0
 def StartLocalRuntime(self, taskbaricon=True):
     if (self.local_runtime is None) or (self.local_runtime.exitcode is not None):
         # create temporary directory for runtime working directory
         self.local_runtime_tmpdir = tempfile.mkdtemp()
         # choose an arbitrary random port for runtime
         self.runtime_port = int(random.random() * 1000) + 61131
         if hasattr(sys, "frozen"):
             cmdd = "%s \"%s\" -p %s -i localhost %s %s" % (
                 os.path.join(os.path.dirname(sys.executable), "Python36", "python.exe"),
                 Bpath("Beremiz_service.py"),
                 self.runtime_port,
                 {False: "-x 0", True: "-x 1"}[taskbaricon],
                 self.local_runtime_tmpdir)
         else:
             cmdd = "\"%s\" \"%s\" -p %s -i localhost %s %s" % (
                 sys.executable,
                 Bpath("Beremiz_service.py"),
                 self.runtime_port,
                 {False: "-x 0", True: "-x 1"}[taskbaricon],
                 self.local_runtime_tmpdir)
         self.local_runtime = ProcessLogger(
             self.Log,
             cmdd,
             no_gui=True,
             timeout=500, keyword=self.local_runtime_tmpdir,
             cwd=self.local_runtime_tmpdir)
         self.local_runtime.spin()
         self.Log.write(_('Beremiz_service Started.'))
     return self.runtime_port
示例#3
0
    async def NewPLC(self, md5sum, data, extrafiles):
        if self.MatchMD5(md5sum) == False:
            res = None
            failure = None

            await self.HandleSerialTransaction(SETRTCTransaction())

            self.confnodesroot.logger.write_warning(
                _("Will now upload firmware to PLC.\nThis may take some time, don't close the program.\n"))
            self.TransactionLock.acquire()
            # Will now boot target
            res, failure = await self._HandleSerialTransaction(BOOTTransaction(), False)
            time.sleep(3)
            # Close connection
            self.Connection.Close()
            # bootloader command
            # data contains full command line except serial port string which is passed to % operator
            # cmd = data % self.Connection.port
            cmd = [token % {"serial_port": self.Connection.port} for token in data]
            # wrapper to run command in separate window
            cmdhead = []
            cmdtail = []
            if os.name in ("nt", "ce"):
                # cmdwrap = "start \"Loading PLC, please wait...\" /wait %s \r"
                cmdhead.append("cmd")
                cmdhead.append("/c")
                cmdhead.append("start")
                cmdhead.append("Loading PLC, please wait...")
                cmdhead.append("/wait")
            else:
                # cmdwrap = "xterm -e %s \r"
                cmdhead.append("xterm")
                cmdhead.append("-e")
                # Load a program
                # try:
                # os.system( cmdwrap % command )
            # except Exception,e:
            #    failure = str(e)
            command = cmdhead + cmd + cmdtail;
            status, result, err_result = ProcessLogger(self.confnodesroot.logger, command).spin()
            """
                    TODO: Process output?
            """
            # Reopen connection
            self.Connection.Open()
            self.TransactionLock.release()

            if failure is not None:
                self.confnodesroot.logger.write_warning(failure + "\n")
                return False
            else:
                await self.StopPLC()
                return self.PLCStatus == "Stopped"
        else:
            await self.StopPLC()
            return self.PLCStatus == "Stopped"
示例#4
0
    def build(self):
        srcfiles = []
        cflags = []
        wholesrcdata = ""
        for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS:
            # Get CFiles list to give it to makefile
            for CFile, CFLAGS in CFilesAndCFLAGS:
                CFileName = os.path.basename(CFile)
                wholesrcdata += self.concat_deps(CFileName)
                srcfiles.append(CFileName)
                if CFLAGS not in cflags:
                    cflags.append(CFLAGS)

        oldmd5 = self.md5key
        self.md5key = hashlib.md5(wholesrcdata).hexdigest()
        props = self.CTRInstance.GetProjectProperties()
        self.md5key += '#'.join([
            props[key]
            for key in ['companyName', 'projectName', 'productName']
        ])
        self.md5key += '#'  #+','.join(map(str,time.localtime()))
        # Store new PLC filename based on md5 key
        f = open(self._GetMD5FileName(), "w")
        f.write(self.md5key)
        f.close()

        if oldmd5 != self.md5key:
            target = self.CTRInstance.GetTarget().getcontent()
            beremizcommand = {
                "src": ' '.join(srcfiles),
                "cflags": ' '.join(cflags),
                "md5": self.md5key,
                "buildpath": self.buildpath
            }

            command = [
                token % beremizcommand
                for token in target.getCommand().split(' ')
            ]

            # Call Makefile to build PLC code and link it with target specific code
            status, result, err_result = ProcessLogger(self.CTRInstance.logger,
                                                       command).spin()
            if status:
                self.md5key = None
                self.CTRInstance.logger.write_error(
                    _("C compilation failed.\n"))
                return False
            return True
        else:
            self.CTRInstance.logger.write(
                _("Source didn't change, no build.\n"))
            return True
示例#5
0
 def getXenoConfig(self, flagsname):
     """ Get xeno-config from target parameters """
     xeno_config = self.CTRInstance.GetTarget().getcontent().getXenoConfig()
     if xeno_config:
         from util.ProcessLogger import ProcessLogger
         status, result, err_result = ProcessLogger(self.CTRInstance.logger,
                                                    xeno_config + " --skin=native --"+flagsname,
                                                    no_stdout=True).spin()
         if status:
             self.CTRInstance.logger.write_error(_("Unable to get Xenomai's %s \n") % flagsname)
         return [result.strip()]
     return []
示例#6
0
 def StartLocalRuntime(self, taskbaricon=True):
     if (self.local_runtime is None) or (self.local_runtime.exitcode
                                         is not None):
         # create temporary directory for runtime working directory
         self.local_runtime_tmpdir = tempfile.mkdtemp()
         # choose an arbitrary random port for runtime
         self.runtime_port = int(random.random() * 1000) + 61131
         # launch local runtime
         self.local_runtime = ProcessLogger(
             self.Log,
             "\"%s\" \"%s\" -p %s -i localhost %s %s" %
             (sys.executable, Bpath("Beremiz_service.py"),
              self.runtime_port, {
                  False: "-x 0",
                  True: "-x 1"
              }[taskbaricon], self.local_runtime_tmpdir),
             no_gui=False,
             timeout=500,
             keyword=self.local_runtime_tmpdir,
             cwd=self.local_runtime_tmpdir)
         self.local_runtime.spin()
     return self.runtime_port
    def build(self):
        srcfiles = []
        cflags = []
        wholesrcdata = ""
        for _Location, CFilesAndCFLAGS, _DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS:
            # Get CFiles list to give it to makefile
            for CFile, CFLAGS in CFilesAndCFLAGS:
                CFileName = os.path.basename(CFile)
                wholesrcdata += self.concat_deps(CFileName)
                srcfiles.append(CFileName)
                if CFLAGS not in cflags:
                    cflags.append(CFLAGS)

        oldmd5 = self.md5key
        self.md5key = hashlib.md5(wholesrcdata).hexdigest()

        # Store new PLC filename based on md5 key
        f = open(self._GetMD5FileName(), "w", encoding='utf-8')
        f.write(self.md5key)
        f.close()

        if oldmd5 != self.md5key:
            target = self.CTRInstance.GetTarget().getcontent()
            beremizcommand = {
                "src": ' '.join(srcfiles),
                "cflags": ' '.join(cflags),
                "md5": self.md5key,
                "buildpath": self.buildpath
            }

            # clean sequence of multiple whitespaces
            cmd = re.sub(r"[ ]+", " ", target.getCommand().strip())

            command = [token % beremizcommand for token in cmd.split(' ')]

            # Call Makefile to build PLC code and link it with target specific code
            status, _result, _err_result = ProcessLogger(
                self.CTRInstance.logger, command).spin()
            if status:
                self.md5key = None
                self.CTRInstance.logger.write_error(
                    _("C compilation failed.\n"))
                return False
            return True
        else:
            self.CTRInstance.logger.write(
                _("Source didn't change, no build.\n"))
            return True
示例#8
0
 def StartLocalRuntime(self, taskbaricon=True):
     if (self.local_runtime is None) or (self.local_runtime.exitcode is not None):
         # create temporary directory for runtime working directory
         self.local_runtime_tmpdir = tempfile.mkdtemp()
         # choose an arbitrary random port for runtime
         self.runtime_port = int(random.random() * 1000) + 61131
         # launch local runtime
         self.local_runtime = ProcessLogger(
             self.Log,
             '"%s" "%s" -p %s -i localhost %s %s'
             % (
                 sys.executable,
                 Bpath("Beremiz_service.py"),
                 self.runtime_port,
                 {False: "-x 0", True: "-x 1"}[taskbaricon],
                 self.local_runtime_tmpdir,
             ),
             no_gui=False,
             timeout=500,
             keyword="working",
             cwd=self.local_runtime_tmpdir,
         )
         self.local_runtime.spin()
     return self.runtime_port
    def build(self):
        # Retrieve toolchain user parameters
        toolchain_params = self.CTRInstance.GetTarget().getcontent()
        self.compiler = toolchain_params.getCompiler()
        self.linker = toolchain_params.getLinker()

        Builder_CFLAGS = ' '.join(self.getBuilderCFLAGS())

        ######### GENERATE OBJECT FILES ########################################
        obns = []
        objs = []
        relink = self.GetBinaryCode() is None
        for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS:
            if CFilesAndCFLAGS:
                if Location:
                    self.CTRInstance.logger.write(
                        ".".join(map(str, Location)) + " :\n")
                else:
                    self.CTRInstance.logger.write(_("PLC :\n"))

            for CFile, CFLAGS in CFilesAndCFLAGS:
                if CFile.endswith(".c"):
                    bn = os.path.basename(CFile)
                    obn = os.path.splitext(bn)[0] + ".o"
                    objectfilename = os.path.splitext(CFile)[0] + ".o"

                    match = self.check_and_update_hash_and_deps(bn)

                    if match:
                        self.CTRInstance.logger.write("   [pass]  " + bn +
                                                      " -> " + obn + "\n")
                    else:
                        relink = True

                        self.CTRInstance.logger.write("   [CC]  " + bn +
                                                      " -> " + obn + "\n")

                        status, result, err_result = ProcessLogger(
                            self.CTRInstance.logger,
                            "\"%s\" -c \"%s\" -o \"%s\" %s %s" %
                            (self.compiler, CFile, objectfilename,
                             Builder_CFLAGS, CFLAGS)).spin()

                        if status:
                            self.srcmd5.pop(bn)
                            self.CTRInstance.logger.write_error(
                                _("C compilation of %s failed.\n") % bn)
                            return False
                    obns.append(obn)
                    objs.append(objectfilename)
                elif CFile.endswith(".o"):
                    obns.append(os.path.basename(CFile))
                    objs.append(CFile)

        ######### GENERATE library FILE ########################################
        # Link all the object files into one binary file
        self.CTRInstance.logger.write(_("Linking :\n"))
        if relink:
            objstring = []

            # Generate list .o files
            listobjstring = '"' + '"  "'.join(objs) + '"'

            ALLldflags = ' '.join(self.getBuilderLDFLAGS())

            self.CTRInstance.logger.write("   [CC]  " + ' '.join(obns) +
                                          " -> " + self.exe + "\n")

            status, result, err_result = ProcessLogger(
                self.CTRInstance.logger,
                "\"%s\" %s -o \"%s\" %s" % (self.linker, listobjstring,
                                            self.exe_path, ALLldflags)).spin()

            if status:
                return False

        else:
            self.CTRInstance.logger.write("   [pass]  " + ' '.join(obns) +
                                          " -> " + self.exe + "\n")

        # Calculate md5 key and get data for the new created PLC
        data = self.GetBinaryCode()
        self.md5key = hashlib.md5(data).hexdigest()

        # Store new PLC filename based on md5 key
        f = open(self._GetMD5FileName(), "w")
        f.write(self.md5key)
        f.close()

        return True
示例#10
0
class Beremiz(IDEFrame):
    def _init_utils(self):
        self.ConfNodeMenu = wx.Menu(title='')
        self.RecentProjectsMenu = wx.Menu(title='')

        IDEFrame._init_utils(self)

    def _init_coll_FileMenu_Items(self, parent):
        AppendMenu(parent,
                   help='',
                   id=wx.ID_NEW,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'New') + '\tCTRL+N')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_OPEN,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Open') + '\tCTRL+O')
        parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"),
                          self.RecentProjectsMenu)
        parent.AppendSeparator()
        AppendMenu(parent,
                   help='',
                   id=wx.ID_SAVE,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Save') + '\tCTRL+S')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_SAVEAS,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Save as') + '\tCTRL+SHIFT+S')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_CLOSE,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Close Tab') + '\tCTRL+W')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_CLOSE_ALL,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Close Project') + '\tCTRL+SHIFT+W')
        parent.AppendSeparator()
        AppendMenu(parent,
                   help='',
                   id=wx.ID_PAGE_SETUP,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Page Setup') + '\tCTRL+ALT+P')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_PREVIEW,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Preview') + '\tCTRL+SHIFT+P')
        AppendMenu(parent,
                   help='',
                   id=wx.ID_PRINT,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Print') + '\tCTRL+P')
        parent.AppendSeparator()
        AppendMenu(parent,
                   help='',
                   id=wx.ID_EXIT,
                   kind=wx.ITEM_NORMAL,
                   text=_(u'Quit') + '\tCTRL+Q')

        self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
        self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
        self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
        self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
        self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)

        self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
                               (wx.ID_OPEN, "open", _(u'Open'), None),
                               (wx.ID_SAVE, "save", _(u'Save'), None),
                               (wx.ID_SAVEAS, "saveas",
                                _(u'Save As...'), None),
                               (wx.ID_PRINT, "print", _(u'Print'), None)])

    def _RecursiveAddMenuItems(self, menu, items):
        for name, text, helpstr, children in items:
            if len(children) > 0:
                new_menu = wx.Menu(title='')
                menu.AppendMenu(wx.ID_ANY, text, new_menu)
                self._RecursiveAddMenuItems(new_menu, children)
            else:
                item = menu.Append(wx.ID_ANY, text, helpstr)
                self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), item)

    def _init_coll_AddMenu_Items(self, parent):
        IDEFrame._init_coll_AddMenu_Items(self, parent, False)
        self._RecursiveAddMenuItems(parent, GetAddMenuItems())

    def _init_coll_HelpMenu_Items(self, parent):
        def handler(event):
            return wx.MessageBox(version.GetCommunityHelpMsg(),
                                 _(u'Community support'),
                                 wx.OK | wx.ICON_INFORMATION)

        item = parent.Append(wx.ID_ANY, _(u'Community support'), '')
        self.Bind(wx.EVT_MENU, handler, item)

        parent.Append(help='',
                      id=wx.ID_ABOUT,
                      kind=wx.ITEM_NORMAL,
                      text=_(u'About'))
        self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)

    def _init_coll_ConnectionStatusBar_Fields(self, parent):
        parent.SetFieldsCount(3)

        parent.SetStatusText(number=0, text='')
        parent.SetStatusText(number=1, text='')
        parent.SetStatusText(number=2, text='')

        parent.SetStatusWidths([-1, 300, 200])

    def _init_ctrls(self, prnt):
        IDEFrame._init_ctrls(self, prnt)

        self.EditMenuSize = self.EditMenu.GetMenuItemCount()

        inspectorID = wx.NewId()
        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID)
        accels = [
            wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord('I'),
                                inspectorID)
        ]

        for method, shortcut in [("Stop", wx.WXK_F4), ("Run", wx.WXK_F5),
                                 ("Transfer", wx.WXK_F6),
                                 ("Connect", wx.WXK_F7),
                                 ("Build", wx.WXK_F11)]:

            def OnMethodGen(obj, meth):
                def OnMethod(evt):
                    if obj.CTR is not None:
                        obj.CTR.CallMethod('_' + meth)
                    wx.CallAfter(self.RefreshStatusToolBar)

                return OnMethod

            newid = wx.NewId()
            self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid)
            accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)]

        self.SetAcceleratorTable(wx.AcceleratorTable(accels))

        self.LogConsole = CustomStyledTextCtrl(name='LogConsole',
                                               parent=self.BottomNoteBook,
                                               pos=wx.Point(0, 0),
                                               size=wx.Size(0, 0))
        self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged)
        self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged)
        self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI,
                             self.OnLogConsoleUpdateUI)
        self.LogConsole.SetReadOnly(True)
        self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR)

        # Define Log Console styles
        self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT,
                                     "face:%(mono)s,size:%(size)d" % faces)
        self.LogConsole.StyleClearAll()
        self.LogConsole.StyleSetSpec(
            1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces)
        self.LogConsole.StyleSetSpec(
            2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)

        # Define Log Console markers
        self.LogConsole.SetMarginSensitive(1, True)
        self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
        self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED")

        self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT)

        self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK,
                             self.OnLogConsoleMarginClick)
        self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED,
                             self.OnLogConsoleModified)

        self.MainTabs["LogConsole"] = (self.LogConsole, _("Console"))
        self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"])
        # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT)

        self.LogViewer = LogViewer(self.BottomNoteBook, self)
        self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log"))
        self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"])
        # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT)

        StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition,
                                   wx.DefaultSize,
                                   wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
        StatusToolBar.SetToolBitmapSize(wx.Size(25, 25))
        StatusToolBar.Realize()
        self.Panes["StatusToolBar"] = StatusToolBar
        self.AUIManager.AddPane(
            StatusToolBar,
            wx.aui.AuiPaneInfo().Name("StatusToolBar").Caption(
                _("Status ToolBar")).ToolbarPane().Top().Position(
                    1).LeftDockable(False).RightDockable(False))

        self.AUIManager.Update()

        self.ConnectionStatusBar = esb.EnhancedStatusBar(self,
                                                         style=wx.ST_SIZEGRIP)
        self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
        self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar,
                                          -1,
                                          range=100)
        self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar,
                                           esb.ESB_EXACT_FIT,
                                           esb.ESB_EXACT_FIT, 2)
        self.ProgressStatusBar.Hide()
        self.SetStatusBar(self.ConnectionStatusBar)

    def __init_execute_path(self):
        if os.name == 'nt':
            # on windows, desktop shortcut launches Beremiz.py
            # with working dir set to mingw/bin.
            # then we prefix CWD to PATH in order to ensure that
            # commands invoked by build process by default are
            # found here.
            os.environ["PATH"] = os.getcwd() + ';' + os.environ["PATH"]

    def __init__(self,
                 parent,
                 projectOpen=None,
                 buildpath=None,
                 ctr=None,
                 debug=True):
        # Add beremiz's icon in top left corner of the frame
        self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO)
        self.__init_execute_path()

        IDEFrame.__init__(self, parent, debug)
        self.Log = LogPseudoFile(self.LogConsole, self.SelectTab)

        self.local_runtime = None
        self.runtime_port = None
        self.local_runtime_tmpdir = None

        self.LastPanelSelected = None

        # Define Tree item icon list
        self.LocationImageList = wx.ImageList(16, 16)
        self.LocationImageDict = {}

        # Icons for location items
        for imgname, itemtype in [("CONFIGURATION", LOCATION_CONFNODE),
                                  ("RESOURCE", LOCATION_MODULE),
                                  ("PROGRAM", LOCATION_GROUP),
                                  ("VAR_INPUT", LOCATION_VAR_INPUT),
                                  ("VAR_OUTPUT", LOCATION_VAR_OUTPUT),
                                  ("VAR_LOCAL", LOCATION_VAR_MEMORY)]:
            self.LocationImageDict[itemtype] = self.LocationImageList.Add(
                GetBitmap(imgname))

        # Icons for other items
        for imgname, itemtype in [("Extension", ITEM_CONFNODE)]:
            self.TreeImageDict[itemtype] = self.TreeImageList.Add(
                GetBitmap(imgname))

        if projectOpen is not None:
            projectOpen = DecodeFileSystemPath(projectOpen, False)

        if projectOpen is not None and os.path.isdir(projectOpen):
            self.CTR = ProjectController(self, self.Log)
            self.Controler = self.CTR
            result, _err = self.CTR.LoadProject(projectOpen, buildpath)
            if not result:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(os.path.abspath(projectOpen))
                self.RefreshAfterLoad()
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
        else:
            self.CTR = ctr
            self.Controler = ctr
            if ctr is not None:
                ctr.SetAppFrame(self, self.Log)
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshAfterLoad()
        if self.EnableDebug:
            self.DebugVariablePanel.SetDataProducer(self.CTR)

        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)

        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        self.RefreshAll()
        self.LogConsole.SetFocus()

    def RefreshTitle(self):
        name = _("Beremiz")
        if self.CTR is not None:
            projectname = self.CTR.GetProjectName()
            if self.CTR.ProjectTestModified():
                projectname = "~%s~" % projectname
            self.SetTitle("%s - %s" % (name, projectname))
        else:
            self.SetTitle(name)

    def StartLocalRuntime(self, taskbaricon=True):
        if (self.local_runtime is None) or (self.local_runtime.exitcode
                                            is not None):
            # create temporary directory for runtime working directory
            self.local_runtime_tmpdir = tempfile.mkdtemp()
            # choose an arbitrary random port for runtime
            self.runtime_port = int(random.random() * 1000) + 61131
            # launch local runtime
            self.local_runtime = ProcessLogger(
                self.Log,
                "\"%s\" \"%s\" -p %s -i localhost %s %s" %
                (sys.executable, Bpath("Beremiz_service.py"),
                 self.runtime_port, {
                     False: "-x 0",
                     True: "-x 1"
                 }[taskbaricon], self.local_runtime_tmpdir),
                no_gui=False,
                timeout=500,
                keyword=self.local_runtime_tmpdir,
                cwd=self.local_runtime_tmpdir)
            self.local_runtime.spin()
        return self.runtime_port

    def KillLocalRuntime(self):
        if self.local_runtime is not None:
            # shutdown local runtime
            self.local_runtime.kill(gently=False)
            # clear temp dir
            shutil.rmtree(self.local_runtime_tmpdir)

            self.local_runtime = None

    def OnOpenWidgetInspector(self, evt):
        # Activate the widget inspection tool
        from wx.lib.inspection import InspectionTool
        if not InspectionTool().initialized:
            InspectionTool().Init()

        # Find a widget to be selected in the tree.  Use either the
        # one under the cursor, if any, or this frame.
        wnd = wx.FindWindowAtPointer()
        if not wnd:
            wnd = self
        InspectionTool().Show(wnd, True)

    def OnLogConsoleFocusChanged(self, event):
        self.RefreshEditMenu()
        event.Skip()

    def OnLogConsoleUpdateUI(self, event):
        self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True)
        event.Skip()

    def OnLogConsoleMarginClick(self, event):
        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
        wx.CallAfter(self.SearchLineForError,
                     self.LogConsole.GetLine(line_idx))
        event.Skip()

    def OnLogConsoleModified(self, event):
        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
        line = self.LogConsole.GetLine(line_idx)
        if line:
            result = MATIEC_ERROR_MODEL.match(line)
            if result is not None:
                self.LogConsole.MarkerAdd(line_idx, 0)
        event.Skip()

    def SearchLineForError(self, line):
        if self.CTR is not None:
            result = MATIEC_ERROR_MODEL.match(line)
            if result is not None:
                first_line, first_column, last_line, last_column, _error = result.groups(
                )
                self.CTR.ShowError(self.Log,
                                   (int(first_line), int(first_column)),
                                   (int(last_line), int(last_column)))

    def CheckSaveBeforeClosing(self, title=_("Close Project")):
        """Function displaying an Error dialog in PLCOpenEditor.

        :returns: False if closing cancelled.
        """
        if self.CTR.ProjectTestModified():
            dialog = wx.MessageDialog(
                self, _("There are changes, do you want to save?"), title,
                wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION)
            answer = dialog.ShowModal()
            dialog.Destroy()
            if answer == wx.ID_YES:
                self.CTR.SaveProject()
            elif answer == wx.ID_CANCEL:
                return False

        for idx in xrange(self.TabsOpened.GetPageCount()):
            window = self.TabsOpened.GetPage(idx)
            if not window.CheckSaveBeforeClosing():
                return False

        return True

    def GetTabInfos(self, tab):
        if isinstance(tab, EditorPanel) and \
           not isinstance(tab, (Viewer,
                                TextViewer,
                                ResourceEditor,
                                ConfigurationEditor,
                                DataTypeEditor)):
            return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName())
        elif (isinstance(tab, TextViewer)
              and (tab.Controler is None
                   or isinstance(tab.Controler, MiniTextControler))):
            return ("confnode", None, tab.GetInstancePath())
        else:
            return IDEFrame.GetTabInfos(self, tab)

    def LoadTab(self, notebook, page_infos):
        if page_infos[0] == "confnode":
            if page_infos[1] is None:
                confnode = self.CTR
            else:
                confnode = self.CTR.GetChildByName(page_infos[1])
            return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:]))
        else:
            return IDEFrame.LoadTab(self, notebook, page_infos)

    # Strange hack required by WAMP connector, using twisted.
    # Twisted reactor needs to be stopped only before quit,
    # since it cannot be restarted
    ToDoBeforeQuit = []

    def AddToDoBeforeQuit(self, Thing):
        self.ToDoBeforeQuit.append(Thing)

    def TryCloseFrame(self):
        if self.CTR is None or self.CheckSaveBeforeClosing(
                _("Close Application")):
            if self.CTR is not None:
                self.CTR.KillDebugThread()
            self.KillLocalRuntime()

            self.SaveLastState()

            for Thing in self.ToDoBeforeQuit:
                Thing()
            self.ToDoBeforeQuit = []

            return True
        return False

    def OnCloseFrame(self, event):
        if self.TryCloseFrame():
            self.LogConsole.Disconnect(-1, -1, wx.wxEVT_KILL_FOCUS)
            event.Skip()
        else:
            # prevent event to continue, i.e. cancel closing
            event.Veto()

    def RefreshFileMenu(self):
        self.RefreshRecentProjectsMenu()

        MenuToolBar = self.Panes["MenuToolBar"]
        if self.CTR is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                window = self.TabsOpened.GetPage(selected)
                viewer_is_modified = window.IsModified()
                is_viewer = isinstance(window, Viewer)
            else:
                viewer_is_modified = is_viewer = False
            if self.TabsOpened.GetPageCount() > 0:
                self.FileMenu.Enable(wx.ID_CLOSE, True)
                if is_viewer:
                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
                    self.FileMenu.Enable(wx.ID_PRINT, True)
                    MenuToolBar.EnableTool(wx.ID_PRINT, True)
                else:
                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
                    self.FileMenu.Enable(wx.ID_PRINT, False)
                    MenuToolBar.EnableTool(wx.ID_PRINT, False)
            else:
                self.FileMenu.Enable(wx.ID_CLOSE, False)
                self.FileMenu.Enable(wx.ID_PREVIEW, False)
                self.FileMenu.Enable(wx.ID_PRINT, False)
                MenuToolBar.EnableTool(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
            project_modified = self.CTR.ProjectTestModified(
            ) or viewer_is_modified
            self.FileMenu.Enable(wx.ID_SAVE, project_modified)
            MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
            self.FileMenu.Enable(wx.ID_SAVEAS, True)
            MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
        else:
            self.FileMenu.Enable(wx.ID_CLOSE, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
            self.FileMenu.Enable(wx.ID_PREVIEW, False)
            self.FileMenu.Enable(wx.ID_PRINT, False)
            MenuToolBar.EnableTool(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_SAVE, False)
            MenuToolBar.EnableTool(wx.ID_SAVE, False)
            self.FileMenu.Enable(wx.ID_SAVEAS, False)
            MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)

    def RefreshRecentProjectsMenu(self):
        try:
            recent_projects = map(DecodeFileSystemPath,
                                  self.GetConfigEntry("RecentProjects", []))
        except Exception:
            recent_projects = []

        while self.RecentProjectsMenu.GetMenuItemCount() > 0:
            item = self.RecentProjectsMenu.FindItemByPosition(0)
            self.RecentProjectsMenu.RemoveItem(item)

        self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS,
                             len(recent_projects) > 0)
        for idx, projectpath in enumerate(recent_projects):
            text = u'&%d: %s' % (idx + 1, projectpath)

            item = self.RecentProjectsMenu.Append(wx.ID_ANY, text, '')
            self.Bind(wx.EVT_MENU,
                      self.GenerateOpenRecentProjectFunction(projectpath),
                      item)

    def GenerateOpenRecentProjectFunction(self, projectpath):
        def OpenRecentProject(event):
            if self.CTR is not None and not self.CheckSaveBeforeClosing():
                return

            self.OpenProject(projectpath)

        return OpenRecentProject

    def GenerateMenuRecursive(self, items, menu):
        for kind, infos in items:
            if isinstance(kind, list):
                text, id = infos
                submenu = wx.Menu('')
                self.GenerateMenuRecursive(kind, submenu)
                menu.AppendMenu(id, text, submenu)
            elif kind == wx.ITEM_SEPARATOR:
                menu.AppendSeparator()
            else:
                text, id, _help, callback = infos
                AppendMenu(menu, help='', id=id, kind=kind, text=text)
                if callback is not None:
                    self.Bind(wx.EVT_MENU, callback, id=id)

    def RefreshEditorToolBar(self):
        IDEFrame.RefreshEditorToolBar(self)
        self.AUIManager.GetPane("EditorToolBar").Position(2)
        self.AUIManager.GetPane("StatusToolBar").Position(1)
        self.AUIManager.Update()

    def RefreshStatusToolBar(self):
        StatusToolBar = self.Panes["StatusToolBar"]
        StatusToolBar.ClearTools()
        StatusToolBar.SetMinSize(StatusToolBar.GetToolBitmapSize())

        if self.CTR is not None:

            for confnode_method in self.CTR.StatusMethods:
                if "method" in confnode_method and confnode_method.get(
                        "shown", True):
                    tool = StatusToolBar.AddSimpleTool(
                        wx.ID_ANY,
                        GetBitmap(confnode_method.get("bitmap", "Unknown")),
                        confnode_method["tooltip"])
                    self.Bind(
                        wx.EVT_MENU,
                        self.GetMenuCallBackFunction(
                            confnode_method["method"]), tool)

            StatusToolBar.Realize()
            self.AUIManager.GetPane("StatusToolBar").BestSize(
                StatusToolBar.GetBestSize()).Show()
        else:
            self.AUIManager.GetPane("StatusToolBar").Hide()
        self.AUIManager.GetPane("EditorToolBar").Position(2)
        self.AUIManager.GetPane("StatusToolBar").Position(1)
        self.AUIManager.Update()

    def RefreshEditMenu(self):
        IDEFrame.RefreshEditMenu(self)
        if self.FindFocus() == self.LogConsole:
            self.EditMenu.Enable(wx.ID_COPY, True)
            self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True)

        if self.CTR is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                panel = self.TabsOpened.GetPage(selected)
            else:
                panel = None
            if panel != self.LastPanelSelected:
                for i in xrange(self.EditMenuSize,
                                self.EditMenu.GetMenuItemCount()):
                    item = self.EditMenu.FindItemByPosition(self.EditMenuSize)
                    if item is not None:
                        if item.IsSeparator():
                            self.EditMenu.RemoveItem(item)
                        else:
                            self.EditMenu.Delete(item.GetId())
                self.LastPanelSelected = panel
                if panel is not None:
                    items = panel.GetConfNodeMenuItems()
                else:
                    items = []
                if len(items) > 0:
                    self.EditMenu.AppendSeparator()
                    self.GenerateMenuRecursive(items, self.EditMenu)
            if panel is not None:
                panel.RefreshConfNodeMenu(self.EditMenu)
        else:
            for i in xrange(self.EditMenuSize,
                            self.EditMenu.GetMenuItemCount()):
                item = self.EditMenu.FindItemByPosition(i)
                if item is not None:
                    if item.IsSeparator():
                        self.EditMenu.RemoveItem(item)
                    else:
                        self.EditMenu.Delete(item.GetId())
            self.LastPanelSelected = None
        self.MenuBar.UpdateMenus()

    def RefreshAll(self):
        self.RefreshStatusToolBar()

    def GetMenuCallBackFunction(self, method):
        """ Generate the callbackfunc for a given CTR method"""
        def OnMenu(event):
            # Disable button to prevent re-entrant call
            event.GetEventObject().Disable()
            # Call
            getattr(self.CTR, method)()
            # Re-enable button
            event.GetEventObject().Enable()

        return OnMenu

    def GetConfigEntry(self, entry_name, default):
        return cPickle.loads(
            str(self.Config.Read(entry_name, cPickle.dumps(default))))

    def ResetConnectionStatusBar(self):
        for field in xrange(self.ConnectionStatusBar.GetFieldsCount()):
            self.ConnectionStatusBar.SetStatusText('', field)

    def ResetView(self):
        IDEFrame.ResetView(self)
        if self.CTR is not None:
            self.CTR.CloseProject()
        self.CTR = None
        self.Log.flush()
        if self.EnableDebug:
            self.DebugVariablePanel.SetDataProducer(None)
            self.ResetConnectionStatusBar()

    def RefreshConfigRecentProjects(self, projectpath, err=False):
        try:
            recent_projects = map(DecodeFileSystemPath,
                                  self.GetConfigEntry("RecentProjects", []))
        except Exception:
            recent_projects = []
        if projectpath in recent_projects:
            recent_projects.remove(projectpath)
        if not err:
            recent_projects.insert(0, projectpath)
        self.Config.Write(
            "RecentProjects",
            cPickle.dumps(
                map(EncodeFileSystemPath,
                    recent_projects[:MAX_RECENT_PROJECTS])))
        self.Config.Flush()

    def ResetPerspective(self):
        IDEFrame.ResetPerspective(self)
        self.RefreshStatusToolBar()

    def OnNewProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        try:
            defaultpath = DecodeFileSystemPath(
                self.Config.Read("lastopenedfolder"))
        except Exception:
            defaultpath = os.path.expanduser("~")

        dialog = wx.DirDialog(self,
                              _("Choose an empty directory for new project"),
                              defaultpath)
        if dialog.ShowModal() == wx.ID_OK:
            projectpath = dialog.GetPath()
            self.Config.Write(
                "lastopenedfolder",
                EncodeFileSystemPath(os.path.dirname(projectpath)))
            self.Config.Flush()
            self.ResetView()
            ctr = ProjectController(self, self.Log)
            result = ctr.NewProject(projectpath)
            if not result:
                self.CTR = ctr
                self.Controler = self.CTR
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(projectpath)
                if self.EnableDebug:
                    self.DebugVariablePanel.SetDataProducer(self.CTR)
                self.RefreshAfterLoad()
                IDEFrame.OnAddNewProject(self, event)
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
            self.RefreshAll()
            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
        dialog.Destroy()

    def OnOpenProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        try:
            defaultpath = DecodeFileSystemPath(
                self.Config.Read("lastopenedfolder"))
        except Exception:
            defaultpath = os.path.expanduser("~")

        dialog = wx.DirDialog(self,
                              _("Choose a project"),
                              defaultpath,
                              style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        if dialog.ShowModal() == wx.ID_OK:
            self.OpenProject(dialog.GetPath())
        dialog.Destroy()

    def OpenProject(self, projectpath):
        if os.path.isdir(projectpath):
            self.Config.Write(
                "lastopenedfolder",
                EncodeFileSystemPath(os.path.dirname(projectpath)))
            self.Config.Flush()
            self.ResetView()
            self.CTR = ProjectController(self, self.Log)
            self.Controler = self.CTR
            result, err = self.CTR.LoadProject(projectpath)
            if not result:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                if self.EnableDebug:
                    self.DebugVariablePanel.SetDataProducer(self.CTR)
                self.RefreshAfterLoad()
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
            self.RefreshAll()
            self.SearchResultPanel.ResetSearchResults()
        else:
            self.ShowErrorMessage(
                _("\"%s\" folder is not a valid Beremiz project\n") %
                projectpath)
            err = True
        self.RefreshConfigRecentProjects(projectpath, err)
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)

    def OnCloseProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        self.ResetView()
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
        self.RefreshAll()

    def RefreshAfterLoad(self):
        self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)

    def RefreshAfterSave(self):
        self.RefreshAll()
        self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)

    def OnSaveProjectMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Save()
        if self.CTR is not None:
            self.CTR.SaveProject()
            self.RefreshAfterSave()

    def OnSaveProjectAsMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.SaveAs()
        if self.CTR is not None:
            self.CTR.SaveProjectAs()
            self.RefreshAfterSave()
            self.RefreshConfigRecentProjects(self.CTR.ProjectPath)

    def OnQuitMenu(self, event):
        self.Close()

    def OnAboutMenu(self, event):
        info = version.GetAboutDialogInfo()
        ShowAboutDialog(self, info)

    def OnProjectTreeItemBeginEdit(self, event):
        selected = event.GetItem()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE:
            event.Veto()
        else:
            IDEFrame.OnProjectTreeItemBeginEdit(self, event)

    def OnProjectTreeRightUp(self, event):
        item = event.GetItem()
        item_infos = self.ProjectTree.GetPyData(item)

        if item_infos["type"] == ITEM_CONFNODE:
            confnode_menu = wx.Menu(title='')

            confnode = item_infos["confnode"]
            if confnode is not None:
                menu_items = confnode.GetContextualMenuItems()
                if menu_items is not None:
                    for text, helpstr, callback in menu_items:
                        item = confnode_menu.Append(wx.ID_ANY, text, helpstr)
                        self.Bind(wx.EVT_MENU, callback, item)
                else:
                    for name, XSDClass, helpstr in confnode.CTNChildrenTypes:
                        if not hasattr(XSDClass, 'CTNMaxCount') or not confnode.Children.get(name) \
                                or len(confnode.Children[name]) < XSDClass.CTNMaxCount:
                            item = confnode_menu.Append(
                                wx.ID_ANY,
                                _("Add") + " " + name, helpstr)
                            self.Bind(
                                wx.EVT_MENU,
                                self.GetAddConfNodeFunction(name, confnode),
                                item)
            item = confnode_menu.Append(wx.ID_ANY, _("Delete"))
            self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), item)

            self.PopupMenu(confnode_menu)
            confnode_menu.Destroy()

            event.Skip()
        elif item_infos["type"] == ITEM_RESOURCE:
            # prevent last resource to be delted
            parent = self.ProjectTree.GetItemParent(item)
            parent_name = self.ProjectTree.GetItemText(parent)
            if parent_name == _("Resources"):
                IDEFrame.OnProjectTreeRightUp(self, event)
        else:
            IDEFrame.OnProjectTreeRightUp(self, event)

    def OnProjectTreeItemActivated(self, event):
        selected = event.GetItem()
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_CONFNODE:
            item_infos["confnode"]._OpenView()
            event.Skip()
        elif item_infos["type"] == ITEM_PROJECT:
            self.CTR._OpenView()
        else:
            IDEFrame.OnProjectTreeItemActivated(self, event)

    def ProjectTreeItemSelect(self, select_item):
        if select_item is not None and select_item.IsOk():
            item_infos = self.ProjectTree.GetPyData(select_item)
            if item_infos["type"] == ITEM_CONFNODE:
                item_infos["confnode"]._OpenView(onlyopened=True)
            elif item_infos["type"] == ITEM_PROJECT:
                self.CTR._OpenView(onlyopened=True)
            else:
                IDEFrame.ProjectTreeItemSelect(self, select_item)

    def GetProjectElementWindow(self, element, tagname):
        is_a_CTN_tagname = len(tagname.split("::")) == 1
        if is_a_CTN_tagname:
            confnode = self.CTR.GetChildByName(tagname)
            return confnode.GetView()
        else:
            return IDEFrame.GetProjectElementWindow(self, element, tagname)

    def SelectProjectTreeItem(self, tagname):
        if self.ProjectTree is not None:
            root = self.ProjectTree.GetRootItem()
            if root.IsOk():
                words = tagname.split("::")
                if len(words) == 1:
                    if tagname == "Project":
                        self.SelectedItem = root
                        self.ProjectTree.SelectItem(root)
                        self.ResetSelectedItem()
                    else:
                        return self.RecursiveProjectTreeItemSelection(
                            root, [(word, ITEM_CONFNODE)
                                   for word in tagname.split(".")])
                elif words[0] == "R":
                    return self.RecursiveProjectTreeItemSelection(
                        root, [(words[2], ITEM_RESOURCE)])
                elif not os.path.exists(words[0]):
                    IDEFrame.SelectProjectTreeItem(self, tagname)

    def GetAddConfNodeFunction(self, name, confnode=None):
        def AddConfNodeMenuFunction(event):
            wx.CallAfter(self.AddConfNode, name, confnode)

        return AddConfNodeMenuFunction

    def GetDeleteMenuFunction(self, confnode):
        def DeleteMenuFunction(event):
            wx.CallAfter(self.DeleteConfNode, confnode)

        return DeleteMenuFunction

    def AddConfNode(self, ConfNodeType, confnode=None):
        if self.CTR.CheckProjectPathPerm():
            ConfNodeName = "%s_0" % ConfNodeType
            if confnode is not None:
                confnode.CTNAddChild(ConfNodeName, ConfNodeType)
            else:
                self.CTR.CTNAddChild(ConfNodeName, ConfNodeType)
            self._Refresh(TITLE, FILEMENU, PROJECTTREE)

    def DeleteConfNode(self, confnode):
        if self.CTR.CheckProjectPathPerm():
            dialog = wx.MessageDialog(
                self,
                _("Really delete node '%s'?") % confnode.CTNName(),
                _("Remove %s node") % confnode.CTNType,
                wx.YES_NO | wx.NO_DEFAULT)
            if dialog.ShowModal() == wx.ID_YES:
                confnode.CTNRemove()
                del confnode
                self._Refresh(TITLE, FILEMENU, PROJECTTREE)
            dialog.Destroy()

# -------------------------------------------------------------------------------
#                        Highlights showing functions
# -------------------------------------------------------------------------------

    def ShowHighlight(self, infos, start, end, highlight_type):
        config_name = self.Controler.GetProjectMainConfigurationName()
        if config_name is not None and infos[0] == ComputeConfigurationName(
                config_name):
            self.CTR._OpenView()
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                viewer = self.TabsOpened.GetPage(selected)
                viewer.AddHighlight(infos[1:], start, end, highlight_type)
        else:
            IDEFrame.ShowHighlight(self, infos, start, end, highlight_type)
示例#11
0
    async def build(self):
        res = False
        gitPath = os.path.join(self.app_path, 'tool', 'Git', 'cmd')
        os.environ['PATH'] += ';' + gitPath
        if exists(os.path.join(gitPath, 'git.exe')):
            self.CTRInstance.logger.write("git path:%s\n" % gitPath)
        else:
            self.CTRInstance.logger.write_error("git not found:%s\n" % gitPath)
            return res
        old_path = os.getcwd()
        os.chdir(self.prjpath)
        try:
            f = 'src/POUS.cpp'
            if exists(f):
                os.remove(f)
            md5key = self.calc_source_md5()
            # 生成platformio.ini
            self.getBoard()

            appname = self.CTRInstance.GetProject().getcontentHeader()
            appname = lazy_pinyin(appname['projectName'])
            appname = ''.join(appname)
            appname = appname.replace(" ", "")
            build_flags = self.build_flags + self.CTRInstance.LDFLAGS + [
                '-D SWNAME=\\"%s\\"' % appname,
                '-DPLC_MD5=%s' % md5key
            ]

            config = self.CTRInstance.GetTarget().getcontent()
            attrib = {}
            optimization = True
            for key, value in config.attrib.items():
                if value == 'false':
                    attrib[key] = False
                elif value == 'true':
                    attrib[key] = True
                else:
                    attrib[key] = value
                if key == 'STATIC': self.static = attrib[key]
                if key == 'optimization': optimization = attrib[key]
            libs = self.libs + self.CTRInstance.Libs
            if self.mode == 'APP' and 'modbus' in libs:
                libs.remove('modbus')
            # 检查
            # if 'IP' not in attrib:
            #     raise Exception(_("Please input default ip!"))

            def_dict = {
                'LOCAL_PACKAGE': True if appchannel != 'alpha' else False,
                'ONLINE_DEBUG': True
            }
            def_dict.update(attrib)

            packages = package_get(self.CTRInstance.logger, def_dict, libs,
                                   staticPackage if self.static else [],
                                   self.app_path)
            package_all = package_get(self.CTRInstance.logger, def_dict,
                                      libs + self.lib_other, [], self.app_path)
            platform = self.getPlatform()
            for att in def_dict:
                val = def_dict[att]
                if val:
                    build_flags.append('-D %s' % att)
                    extra = extra_flag.get(att) or []
                    for e in extra:
                        build_flags.append('-D %s' % e)
            build_flags = list(set(build_flags))

            self.platformio_ini(platform,
                                self.framework,
                                self.board,
                                build_flags,
                                package_prog=packages,
                                package_test=package_all,
                                upload_protocol=self.upload_protocol,
                                options=self.options)

            # shutil.copy(os.path.join(self.app_path, 'rtconfig.h'), 'src/rtconfig.h')
            # shutil.copy(os.path.join(self.app_path, 'rtconfig_cm3.h'), 'src/rtconfig_cm3.h')
            # shutil.copy(os.path.join(self.app_path, 'rtconfig_stm32.h'), 'src/rtconfig_stm32.h')
            # if not exists('src/spiffs_config.h'):
            #     shutil.copy(os.path.join(self.app_path, 'spiffs_config.h'), 'src/spiffs_config.h')
            shutil.copy(
                os.path.join(os.path.dirname(__file__), 'extra_script.py'),
                'extra_script.py')
            # shutil.copy(os.path.join(self.app_path, 'tool', 'makefsdata.exe'), 'makefsdata.exe')
            # shutil.copy(os.path.join(self.app_path, 'tool', 'msvcr100d.dll'), 'msvcr100d.dll')

            # 输出buildnumer
            json_build = 'build.json'
            if exists(json_build):
                with open(json_build, 'r', encoding='utf-8') as f:
                    build = json.load(f)
                    build['build'] += 1
                    # if build.get('platform') != platform:
                    #     build['platform'] = platform
                    #     self.CTRInstance.logger.write(_("Platform Changed . Clean Project ... \n"))
                    # os.system("runas /user:Administrator rd /s /q src")
                    os.system("rd /s /q .pio")
                with open(json_build, 'w', encoding='utf-8') as f:
                    json.dump(build, f)
            else:
                build = {'build': 1}
                with open(json_build, 'w', encoding='utf-8') as f:
                    json.dump(build, f)
            buildnumber_c = "src/buildnumber.c"
            f = open(buildnumber_c, "w+")
            f.write("const unsigned short build_number=%d;" % build['build'])
            f.close()
            # f = 'src/LOGGER.c'
            # if exists(f):
            #     os.remove(f)
            # self.CTRInstance.logger.write(_("update Compiler Env ... \n"))
            # status, _result, _err_result = ProcessLogger(self.CTRInstance.logger, "pio --version", no_gui=False, ).spin()
            # status, _result, _err_result = ProcessLogger(self.CTRInstance.logger, "pio update", no_gui=False, ).spin()
            # status, _result, _err_result = ProcessLogger(self.CTRInstance.logger, "pio lib update", no_gui=False, ).spin()
            # if status:
            #     self.CTRInstance.logger.write_error(_("update  failed.\n"))
            #     os.chdir(old_path)
            #     return False
            self.CTRInstance.logger.write(_("Compiling ... \n"))
            cmd = 'pio.exe'
            application_path = os.path.dirname(sys.executable)
            pio_paths = [
                os.path.join(self.app_path, 'Python36', 'Scripts'),
                os.path.join(application_path, 'Scripts')
            ]
            pio = self.findObject(
                pio_paths, lambda p: os.path.isfile(os.path.join(p, cmd)))
            # if hasattr(sys, "frozen"):
            #     cmdd = "%s/Python36/python.exe %s/%s run --disable-auto-clean" % (
            #         mt.replace('\\', '/'), pio.replace('\\', '/'), cmd)
            # else:
            if optimization:
                cmdd = "%s/%s run --disable-auto-clean" % (pio.replace(
                    '\\', '/'), cmd)
            else:
                cmdd = "%s/%s debug --disable-auto-clean" % (pio.replace(
                    '\\', '/'), cmd)
            cmdd = cmdd.replace('/', '\\')
            self.CTRInstance.logger.write(cmdd)
            status, _result, _err_result = ProcessLogger(
                self.CTRInstance.logger,
                cmdd,
                no_gui=True,
            ).spin()
            if status:
                self.CTRInstance.logger.write_error(
                    _("C compilation  failed.\n"))
                os.chdir(old_path)
            f = open(self._GetMD5FileName(), "w", encoding='utf-8')
            f.write(md5key)
            f.close()
            res = True
        except Exception as e:
            print(traceback.format_exc())
            self.CTRInstance.logger.write_error(_("C Build failed.\n"))
        os.chdir(old_path)
        return res
示例#12
0
class Beremiz(IDEFrame):
    def _init_utils(self):
        self.ConfNodeMenu = wx.Menu(title="")
        self.RecentProjectsMenu = wx.Menu(title="")

        IDEFrame._init_utils(self)

    def _init_coll_FileMenu_Items(self, parent):
        AppendMenu(parent, help="", id=wx.ID_NEW, kind=wx.ITEM_NORMAL, text=_(u"New") + "\tCTRL+N")
        AppendMenu(parent, help="", id=wx.ID_OPEN, kind=wx.ITEM_NORMAL, text=_(u"Open") + "\tCTRL+O")
        parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu)
        parent.AppendSeparator()
        AppendMenu(parent, help="", id=wx.ID_SAVE, kind=wx.ITEM_NORMAL, text=_(u"Save") + "\tCTRL+S")
        AppendMenu(parent, help="", id=wx.ID_SAVEAS, kind=wx.ITEM_NORMAL, text=_(u"Save as") + "\tCTRL+SHIFT+S")
        AppendMenu(parent, help="", id=wx.ID_CLOSE, kind=wx.ITEM_NORMAL, text=_(u"Close Tab") + "\tCTRL+W")
        AppendMenu(
            parent, help="", id=wx.ID_CLOSE_ALL, kind=wx.ITEM_NORMAL, text=_(u"Close Project") + "\tCTRL+SHIFT+W"
        )
        parent.AppendSeparator()
        AppendMenu(parent, help="", id=wx.ID_PAGE_SETUP, kind=wx.ITEM_NORMAL, text=_(u"Page Setup") + "\tCTRL+ALT+P")
        AppendMenu(parent, help="", id=wx.ID_PREVIEW, kind=wx.ITEM_NORMAL, text=_(u"Preview") + "\tCTRL+SHIFT+P")
        AppendMenu(parent, help="", id=wx.ID_PRINT, kind=wx.ITEM_NORMAL, text=_(u"Print") + "\tCTRL+P")
        parent.AppendSeparator()
        AppendMenu(parent, help="", id=wx.ID_EXIT, kind=wx.ITEM_NORMAL, text=_(u"Quit") + "\tCTRL+Q")

        self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
        self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
        self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
        self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
        self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
        self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)

        self.AddToMenuToolBar(
            [
                (wx.ID_NEW, "new", _(u"New"), None),
                (wx.ID_OPEN, "open", _(u"Open"), None),
                (wx.ID_SAVE, "save", _(u"Save"), None),
                (wx.ID_SAVEAS, "saveas", _(u"Save As..."), None),
                (wx.ID_PRINT, "print", _(u"Print"), None),
            ]
        )

    def _RecursiveAddMenuItems(self, menu, items):
        for name, text, help, children in items:
            new_id = wx.NewId()
            if len(children) > 0:
                new_menu = wx.Menu(title="")
                menu.AppendMenu(new_id, text, new_menu)
                self._RecursiveAddMenuItems(new_menu, children)
            else:
                AppendMenu(menu, help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text)
                self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), id=new_id)

    def _init_coll_AddMenu_Items(self, parent):
        IDEFrame._init_coll_AddMenu_Items(self, parent, False)
        self._RecursiveAddMenuItems(parent, GetAddMenuItems())

    def _init_coll_HelpMenu_Items(self, parent):
        parent.Append(help="", id=wx.ID_ABOUT, kind=wx.ITEM_NORMAL, text=_(u"About"))
        self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)

    def _init_coll_ConnectionStatusBar_Fields(self, parent):
        parent.SetFieldsCount(3)

        parent.SetStatusText(number=0, text="")
        parent.SetStatusText(number=1, text="")
        parent.SetStatusText(number=2, text="")

        parent.SetStatusWidths([-1, 300, 200])

    def _init_ctrls(self, prnt):
        IDEFrame._init_ctrls(self, prnt)

        self.EditMenuSize = self.EditMenu.GetMenuItemCount()

        inspectorID = wx.NewId()
        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID)
        accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("I"), inspectorID)]
        for method, shortcut in [
            ("Stop", wx.WXK_F4),
            ("Run", wx.WXK_F5),
            ("Transfer", wx.WXK_F6),
            ("Connect", wx.WXK_F7),
            ("Build", wx.WXK_F11),
        ]:

            def OnMethodGen(obj, meth):
                def OnMethod(evt):
                    if obj.CTR is not None:
                        obj.CTR.CallMethod("_" + meth)
                    wx.CallAfter(self.RefreshStatusToolBar)

                return OnMethod

            newid = wx.NewId()
            self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid)
            accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)]

        self.SetAcceleratorTable(wx.AcceleratorTable(accels))

        self.LogConsole = CustomStyledTextCtrl(
            name="LogConsole", parent=self.BottomNoteBook, pos=wx.Point(0, 0), size=wx.Size(0, 0)
        )
        self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged)
        self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged)
        self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI)
        self.LogConsole.SetReadOnly(True)
        self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR)

        # Define Log Console styles
        self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
        self.LogConsole.StyleClearAll()
        self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces)
        self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)

        # Define Log Console markers
        self.LogConsole.SetMarginSensitive(1, True)
        self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
        self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED")

        self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT)

        self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick)
        self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified)

        self.MainTabs["LogConsole"] = (self.LogConsole, _("Console"))
        self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"])
        # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT)

        self.LogViewer = LogViewer(self.BottomNoteBook, self)
        self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log"))
        self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"])
        # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT)

        StatusToolBar = wx.ToolBar(
            self, -1, wx.DefaultPosition, wx.DefaultSize, wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER
        )
        StatusToolBar.SetToolBitmapSize(wx.Size(25, 25))
        StatusToolBar.Realize()
        self.Panes["StatusToolBar"] = StatusToolBar
        self.AUIManager.AddPane(
            StatusToolBar,
            wx.aui.AuiPaneInfo()
            .Name("StatusToolBar")
            .Caption(_("Status ToolBar"))
            .ToolbarPane()
            .Top()
            .Position(1)
            .LeftDockable(False)
            .RightDockable(False),
        )

        self.AUIManager.Update()

        self.ConnectionStatusBar = wx.StatusBar(self, style=wx.ST_SIZEGRIP)
        self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
        self.SetStatusBar(self.ConnectionStatusBar)

    def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True):
        IDEFrame.__init__(self, parent, debug)
        self.Log = LogPseudoFile(self.LogConsole, self.SelectTab)

        self.local_runtime = None
        self.runtime_port = None
        self.local_runtime_tmpdir = None

        self.LastPanelSelected = None

        # Define Tree item icon list
        self.LocationImageList = wx.ImageList(16, 16)
        self.LocationImageDict = {}

        # Icons for location items
        for imgname, itemtype in [
            ("CONFIGURATION", LOCATION_CONFNODE),
            ("RESOURCE", LOCATION_MODULE),
            ("PROGRAM", LOCATION_GROUP),
            ("VAR_INPUT", LOCATION_VAR_INPUT),
            ("VAR_OUTPUT", LOCATION_VAR_OUTPUT),
            ("VAR_LOCAL", LOCATION_VAR_MEMORY),
        ]:
            self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname))

        # Icons for other items
        for imgname, itemtype in [("Extension", ITEM_CONFNODE)]:
            self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))

        # Add beremiz's icon in top left corner of the frame
        self.SetIcon(wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO))

        if projectOpen is not None:
            projectOpen = DecodeFileSystemPath(projectOpen, False)

        if projectOpen is not None and os.path.isdir(projectOpen):
            self.CTR = ProjectController(self, self.Log)
            self.Controler = self.CTR
            result = self.CTR.LoadProject(projectOpen, buildpath)
            if not result:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(os.path.abspath(projectOpen))
                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
        else:
            self.CTR = ctr
            self.Controler = ctr
            if ctr is not None:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
        if self.EnableDebug:
            self.DebugVariablePanel.SetDataProducer(self.CTR)

        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)

        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
        self.RefreshAll()
        self.LogConsole.SetFocus()

    def RefreshTitle(self):
        name = _("Beremiz")
        if self.CTR is not None:
            projectname = self.CTR.GetProjectName()
            if self.CTR.ProjectTestModified():
                projectname = "~%s~" % projectname
            self.SetTitle("%s - %s" % (name, projectname))
        else:
            self.SetTitle(name)

    def StartLocalRuntime(self, taskbaricon=True):
        if (self.local_runtime is None) or (self.local_runtime.exitcode is not None):
            # create temporary directory for runtime working directory
            self.local_runtime_tmpdir = tempfile.mkdtemp()
            # choose an arbitrary random port for runtime
            self.runtime_port = int(random.random() * 1000) + 61131
            # launch local runtime
            self.local_runtime = ProcessLogger(
                self.Log,
                '"%s" "%s" -p %s -i localhost %s %s'
                % (
                    sys.executable,
                    Bpath("Beremiz_service.py"),
                    self.runtime_port,
                    {False: "-x 0", True: "-x 1"}[taskbaricon],
                    self.local_runtime_tmpdir,
                ),
                no_gui=False,
                timeout=500,
                keyword="working",
                cwd=self.local_runtime_tmpdir,
            )
            self.local_runtime.spin()
        return self.runtime_port

    def KillLocalRuntime(self):
        if self.local_runtime is not None:
            # shutdown local runtime
            self.local_runtime.kill(gently=False)
            # clear temp dir
            shutil.rmtree(self.local_runtime_tmpdir)

            self.local_runtime = None

    def OnOpenWidgetInspector(self, evt):
        # Activate the widget inspection tool
        from wx.lib.inspection import InspectionTool

        if not InspectionTool().initialized:
            InspectionTool().Init()

        # Find a widget to be selected in the tree.  Use either the
        # one under the cursor, if any, or this frame.
        wnd = wx.FindWindowAtPointer()
        if not wnd:
            wnd = self
        InspectionTool().Show(wnd, True)

    def OnLogConsoleFocusChanged(self, event):
        self.RefreshEditMenu()
        event.Skip()

    def OnLogConsoleUpdateUI(self, event):
        self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True)
        event.Skip()

    def OnLogConsoleMarginClick(self, event):
        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
        wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx))
        event.Skip()

    def OnLogConsoleModified(self, event):
        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
        line = self.LogConsole.GetLine(line_idx)
        if line:
            result = MATIEC_ERROR_MODEL.match(line)
            if result is not None:
                self.LogConsole.MarkerAdd(line_idx, 0)
        event.Skip()

    def SearchLineForError(self, line):
        if self.CTR is not None:
            result = MATIEC_ERROR_MODEL.match(line)
            if result is not None:
                first_line, first_column, last_line, last_column, error = result.groups()
                infos = self.CTR.ShowError(
                    self.Log, (int(first_line), int(first_column)), (int(last_line), int(last_column))
                )

    ## Function displaying an Error dialog in PLCOpenEditor.
    #  @return False if closing cancelled.
    def CheckSaveBeforeClosing(self, title=_("Close Project")):
        if self.CTR.ProjectTestModified():
            dialog = wx.MessageDialog(
                self, _("There are changes, do you want to save?"), title, wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION
            )
            answer = dialog.ShowModal()
            dialog.Destroy()
            if answer == wx.ID_YES:
                self.CTR.SaveProject()
            elif answer == wx.ID_CANCEL:
                return False

        for idx in xrange(self.TabsOpened.GetPageCount()):
            window = self.TabsOpened.GetPage(idx)
            if not window.CheckSaveBeforeClosing():
                return False

        return True

    def GetTabInfos(self, tab):
        if isinstance(tab, EditorPanel) and not isinstance(
            tab, (Viewer, TextViewer, ResourceEditor, ConfigurationEditor, DataTypeEditor)
        ):
            return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName())
        elif isinstance(tab, TextViewer) and (tab.Controler is None or isinstance(tab.Controler, MiniTextControler)):
            return ("confnode", None, tab.GetInstancePath())
        else:
            return IDEFrame.GetTabInfos(self, tab)

    def LoadTab(self, notebook, page_infos):
        if page_infos[0] == "confnode":
            if page_infos[1] is None:
                confnode = self.CTR
            else:
                confnode = self.CTR.GetChildByName(page_infos[1])
            return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:]))
        else:
            return IDEFrame.LoadTab(self, notebook, page_infos)

    # Strange hack required by WAMP connector, using twisted.
    # Twisted reactor needs to be stopped only before quit,
    # since it cannot be restarted
    ToDoBeforeQuit = []

    def AddToDoBeforeQuit(self, Thing):
        self.ToDoBeforeQuit.append(Thing)

    def OnCloseFrame(self, event):
        for evt_type in [wx.EVT_SET_FOCUS, wx.EVT_KILL_FOCUS, wx.stc.EVT_STC_UPDATEUI]:
            self.LogConsole.Unbind(evt_type)
        if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")):
            if self.CTR is not None:
                self.CTR.KillDebugThread()
            self.KillLocalRuntime()

            self.SaveLastState()

            for Thing in self.ToDoBeforeQuit:
                Thing()
            self.ToDoBeforeQuit = []

            event.Skip()
        else:
            event.Veto()

    def RefreshFileMenu(self):
        self.RefreshRecentProjectsMenu()

        MenuToolBar = self.Panes["MenuToolBar"]
        if self.CTR is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                window = self.TabsOpened.GetPage(selected)
                viewer_is_modified = window.IsModified()
                is_viewer = isinstance(window, Viewer)
            else:
                viewer_is_modified = is_viewer = False
            if self.TabsOpened.GetPageCount() > 0:
                self.FileMenu.Enable(wx.ID_CLOSE, True)
                if is_viewer:
                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
                    self.FileMenu.Enable(wx.ID_PRINT, True)
                    MenuToolBar.EnableTool(wx.ID_PRINT, True)
                else:
                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
                    self.FileMenu.Enable(wx.ID_PRINT, False)
                    MenuToolBar.EnableTool(wx.ID_PRINT, False)
            else:
                self.FileMenu.Enable(wx.ID_CLOSE, False)
                self.FileMenu.Enable(wx.ID_PREVIEW, False)
                self.FileMenu.Enable(wx.ID_PRINT, False)
                MenuToolBar.EnableTool(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
            project_modified = self.CTR.ProjectTestModified() or viewer_is_modified
            self.FileMenu.Enable(wx.ID_SAVE, project_modified)
            MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
            self.FileMenu.Enable(wx.ID_SAVEAS, True)
            MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
        else:
            self.FileMenu.Enable(wx.ID_CLOSE, False)
            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
            self.FileMenu.Enable(wx.ID_PREVIEW, False)
            self.FileMenu.Enable(wx.ID_PRINT, False)
            MenuToolBar.EnableTool(wx.ID_PRINT, False)
            self.FileMenu.Enable(wx.ID_SAVE, False)
            MenuToolBar.EnableTool(wx.ID_SAVE, False)
            self.FileMenu.Enable(wx.ID_SAVEAS, False)
            MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
            self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)

    def RefreshRecentProjectsMenu(self):
        try:
            recent_projects = map(DecodeFileSystemPath, self.GetConfigEntry("RecentProjects", []))
        except:
            recent_projects = []
        self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0)
        for idx, projectpath in enumerate(recent_projects):
            text = u"%d: %s" % (idx + 1, projectpath)

            if idx < self.RecentProjectsMenu.GetMenuItemCount():
                item = self.RecentProjectsMenu.FindItemByPosition(idx)
                id = item.GetId()
                item.SetItemLabel(text)
                self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType())
            else:
                id = wx.NewId()
                AppendMenu(self.RecentProjectsMenu, help="", id=id, kind=wx.ITEM_NORMAL, text=text)
            self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id)

    def GenerateOpenRecentProjectFunction(self, projectpath):
        def OpenRecentProject(event):
            if self.CTR is not None and not self.CheckSaveBeforeClosing():
                return

            self.OpenProject(projectpath)

        return OpenRecentProject

    def GenerateMenuRecursive(self, items, menu):
        for kind, infos in items:
            if isinstance(kind, ListType):
                text, id = infos
                submenu = wx.Menu("")
                self.GenerateMenuRecursive(kind, submenu)
                menu.AppendMenu(id, text, submenu)
            elif kind == wx.ITEM_SEPARATOR:
                menu.AppendSeparator()
            else:
                text, id, help, callback = infos
                AppendMenu(menu, help="", id=id, kind=kind, text=text)
                if callback is not None:
                    self.Bind(wx.EVT_MENU, callback, id=id)

    def RefreshEditorToolBar(self):
        IDEFrame.RefreshEditorToolBar(self)
        self.AUIManager.GetPane("EditorToolBar").Position(2)
        self.AUIManager.GetPane("StatusToolBar").Position(1)
        self.AUIManager.Update()

    def RefreshStatusToolBar(self):
        StatusToolBar = self.Panes["StatusToolBar"]
        StatusToolBar.ClearTools()

        if self.CTR is not None:

            for confnode_method in self.CTR.StatusMethods:
                if "method" in confnode_method and confnode_method.get("shown", True):
                    id = wx.NewId()
                    StatusToolBar.AddSimpleTool(
                        id, GetBitmap(confnode_method.get("bitmap", "Unknown")), confnode_method["tooltip"]
                    )
                    self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id)

            StatusToolBar.Realize()
            self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show()
        else:
            self.AUIManager.GetPane("StatusToolBar").Hide()
        self.AUIManager.GetPane("EditorToolBar").Position(2)
        self.AUIManager.GetPane("StatusToolBar").Position(1)
        self.AUIManager.Update()

    def RefreshEditMenu(self):
        IDEFrame.RefreshEditMenu(self)
        if self.FindFocus() == self.LogConsole:
            self.EditMenu.Enable(wx.ID_COPY, True)
            self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True)

        if self.CTR is not None:
            selected = self.TabsOpened.GetSelection()
            if selected >= 0:
                panel = self.TabsOpened.GetPage(selected)
            else:
                panel = None
            if panel != self.LastPanelSelected:
                for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()):
                    item = self.EditMenu.FindItemByPosition(self.EditMenuSize)
                    if item is not None:
                        if item.IsSeparator():
                            self.EditMenu.RemoveItem(item)
                        else:
                            self.EditMenu.Delete(item.GetId())
                self.LastPanelSelected = panel
                if panel is not None:
                    items = panel.GetConfNodeMenuItems()
                else:
                    items = []
                if len(items) > 0:
                    self.EditMenu.AppendSeparator()
                    self.GenerateMenuRecursive(items, self.EditMenu)
            if panel is not None:
                panel.RefreshConfNodeMenu(self.EditMenu)
        else:
            for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()):
                item = self.EditMenu.FindItemByPosition(i)
                if item is not None:
                    if item.IsSeparator():
                        self.EditMenu.RemoveItem(item)
                    else:
                        self.EditMenu.Delete(item.GetId())
            self.LastPanelSelected = None
        self.MenuBar.UpdateMenus()

    def RefreshAll(self):
        self.RefreshStatusToolBar()

    def GetMenuCallBackFunction(self, method):
        """ Generate the callbackfunc for a given CTR method"""

        def OnMenu(event):
            # Disable button to prevent re-entrant call
            event.GetEventObject().Disable()
            # Call
            getattr(self.CTR, method)()
            # Re-enable button
            event.GetEventObject().Enable()

        return OnMenu

    def GetConfigEntry(self, entry_name, default):
        return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default))))

    def ResetConnectionStatusBar(self):
        for field in xrange(self.ConnectionStatusBar.GetFieldsCount()):
            self.ConnectionStatusBar.SetStatusText("", field)

    def ResetView(self):
        IDEFrame.ResetView(self)
        self.ConfNodeInfos = {}
        if self.CTR is not None:
            self.CTR.CloseProject()
        self.CTR = None
        self.Log.flush()
        if self.EnableDebug:
            self.DebugVariablePanel.SetDataProducer(None)
            self.ResetConnectionStatusBar()

    def RefreshConfigRecentProjects(self, projectpath):
        try:
            recent_projects = map(DecodeFileSystemPath, self.GetConfigEntry("RecentProjects", []))
        except:
            recent_projects = []
        if projectpath in recent_projects:
            recent_projects.remove(projectpath)
        recent_projects.insert(0, projectpath)
        self.Config.Write(
            "RecentProjects", cPickle.dumps(map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))
        )
        self.Config.Flush()

    def ResetPerspective(self):
        IDEFrame.ResetPerspective(self)
        self.RefreshStatusToolBar()

    def OnNewProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        try:
            defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
        except:
            defaultpath = os.path.expanduser("~")

        dialog = wx.DirDialog(self, _("Choose a project"), defaultpath)
        if dialog.ShowModal() == wx.ID_OK:
            projectpath = dialog.GetPath()
            self.Config.Write("lastopenedfolder", EncodeFileSystemPath(os.path.dirname(projectpath)))
            self.Config.Flush()
            self.ResetView()
            ctr = ProjectController(self, self.Log)
            result = ctr.NewProject(projectpath)
            if not result:
                self.CTR = ctr
                self.Controler = self.CTR
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(projectpath)
                if self.EnableDebug:
                    self.DebugVariablePanel.SetDataProducer(self.CTR)
                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
            self.RefreshAll()
            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
        dialog.Destroy()

    def OnOpenProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        try:
            defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
        except:
            defaultpath = os.path.expanduser("~")

        dialog = wx.DirDialog(
            self, _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
        )
        if dialog.ShowModal() == wx.ID_OK:
            self.OpenProject(dialog.GetPath())
        dialog.Destroy()

    def OpenProject(self, projectpath):
        if os.path.isdir(projectpath):
            self.Config.Write("lastopenedfolder", EncodeFileSystemPath(os.path.dirname(projectpath)))
            self.Config.Flush()
            self.ResetView()
            self.CTR = ProjectController(self, self.Log)
            self.Controler = self.CTR
            result = self.CTR.LoadProject(projectpath)
            if not result:
                self.LibraryPanel.SetController(self.Controler)
                self.ProjectTree.Enable(True)
                self.PouInstanceVariablesPanel.SetController(self.Controler)
                self.RefreshConfigRecentProjects(projectpath)
                if self.EnableDebug:
                    self.DebugVariablePanel.SetDataProducer(self.CTR)
                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
            else:
                self.ResetView()
                self.ShowErrorMessage(result)
            self.RefreshAll()
        else:
            self.ShowErrorMessage(_('"%s" folder is not a valid Beremiz project\n') % projectpath)
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)

    def OnCloseProjectMenu(self, event):
        if self.CTR is not None and not self.CheckSaveBeforeClosing():
            return

        self.ResetView()
        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
        self.RefreshAll()

    def OnSaveProjectMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.Save()
        if self.CTR is not None:
            self.CTR.SaveProject()
            self.RefreshAll()
            self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)

    def OnSaveProjectAsMenu(self, event):
        selected = self.TabsOpened.GetSelection()
        if selected != -1:
            window = self.TabsOpened.GetPage(selected)
            window.SaveAs()
        if self.CTR is not None:
            self.CTR.SaveProjectAs()
            self.RefreshAll()
            self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)

    def OnQuitMenu(self, event):
        self.Close()

    def OnAboutMenu(self, event):
        OpenHtmlFrame(self, _("About Beremiz"), Bpath("doc", "about.html"), wx.Size(550, 500))

    def OnProjectTreeItemBeginEdit(self, event):
        selected = event.GetItem()
        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE:
            event.Veto()
        else:
            IDEFrame.OnProjectTreeItemBeginEdit(self, event)

    def OnProjectTreeRightUp(self, event):
        item = event.GetItem()
        item_infos = self.ProjectTree.GetPyData(item)

        if item_infos["type"] == ITEM_CONFNODE:
            confnode_menu = wx.Menu(title="")

            confnode = item_infos["confnode"]
            if confnode is not None:
                menu_items = confnode.GetContextualMenuItems()
                if menu_items is not None:
                    for text, help, callback in menu_items:
                        new_id = wx.NewId()
                        confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text)
                        self.Bind(wx.EVT_MENU, callback, id=new_id)
                else:
                    for name, XSDClass, help in confnode.CTNChildrenTypes:
                        new_id = wx.NewId()
                        confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Add") + " " + name)
                        self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), id=new_id)

            new_id = wx.NewId()
            AppendMenu(confnode_menu, help="", id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
            self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id)

            self.PopupMenu(confnode_menu)
            confnode_menu.Destroy()

            event.Skip()
        elif item_infos["type"] == ITEM_RESOURCE:
            # prevent last resource to be delted
            parent = self.ProjectTree.GetItemParent(item)
            parent_name = self.ProjectTree.GetItemText(parent)
            if parent_name == _("Resources"):
                IDEFrame.OnProjectTreeRightUp(self, event)
        else:
            IDEFrame.OnProjectTreeRightUp(self, event)

    def OnProjectTreeItemActivated(self, event):
        selected = event.GetItem()
        name = self.ProjectTree.GetItemText(selected)
        item_infos = self.ProjectTree.GetPyData(selected)
        if item_infos["type"] == ITEM_CONFNODE:
            item_infos["confnode"]._OpenView()
            event.Skip()
        elif item_infos["type"] == ITEM_PROJECT:
            self.CTR._OpenView()
        else:
            IDEFrame.OnProjectTreeItemActivated(self, event)

    def ProjectTreeItemSelect(self, select_item):
        if select_item is not None and select_item.IsOk():
            name = self.ProjectTree.GetItemText(select_item)
            item_infos = self.ProjectTree.GetPyData(select_item)
            if item_infos["type"] == ITEM_CONFNODE:
                item_infos["confnode"]._OpenView(onlyopened=True)
            elif item_infos["type"] == ITEM_PROJECT:
                self.CTR._OpenView(onlyopened=True)
            else:
                IDEFrame.ProjectTreeItemSelect(self, select_item)

    def SelectProjectTreeItem(self, tagname):
        if self.ProjectTree is not None:
            root = self.ProjectTree.GetRootItem()
            if root.IsOk():
                words = tagname.split("::")
                if len(words) == 1:
                    if tagname == "Project":
                        self.SelectedItem = root
                        self.ProjectTree.SelectItem(root)
                        self.ResetSelectedItem()
                    else:
                        return self.RecursiveProjectTreeItemSelection(
                            root, [(word, ITEM_CONFNODE) for word in tagname.split(".")]
                        )
                elif words[0] == "R":
                    return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)])
                elif not os.path.exists(words[0]):
                    IDEFrame.SelectProjectTreeItem(self, tagname)

    def GetAddConfNodeFunction(self, name, confnode=None):
        def AddConfNodeMenuFunction(event):
            wx.CallAfter(self.AddConfNode, name, confnode)

        return AddConfNodeMenuFunction

    def GetDeleteMenuFunction(self, confnode):
        def DeleteMenuFunction(event):
            wx.CallAfter(self.DeleteConfNode, confnode)

        return DeleteMenuFunction

    def AddConfNode(self, ConfNodeType, confnode=None):
        if self.CTR.CheckProjectPathPerm():
            ConfNodeName = "%s_0" % ConfNodeType
            if confnode is not None:
                confnode.CTNAddChild(ConfNodeName, ConfNodeType)
            else:
                self.CTR.CTNAddChild(ConfNodeName, ConfNodeType)
            self._Refresh(TITLE, FILEMENU, PROJECTTREE)

    def DeleteConfNode(self, confnode):
        if self.CTR.CheckProjectPathPerm():
            dialog = wx.MessageDialog(
                self,
                _("Really delete node '%s'?") % confnode.CTNName(),
                _("Remove %s node") % confnode.CTNType,
                wx.YES_NO | wx.NO_DEFAULT,
            )
            if dialog.ShowModal() == wx.ID_YES:
                confnode.CTNRemove()
                del confnode
                self._Refresh(TITLE, FILEMENU, PROJECTTREE)
            dialog.Destroy()

    # -------------------------------------------------------------------------------
    #                        Highlights showing functions
    # -------------------------------------------------------------------------------

    def ShowHighlight(self, infos, start, end, highlight_type):
        config_name = self.Controler.GetProjectMainConfigurationName()
        if config_name is not None and infos[0] == self.Controler.ComputeConfigurationName(config_name):
            self.CTR._OpenView()
            selected = self.TabsOpened.GetSelection()
            if selected != -1:
                viewer = self.TabsOpened.GetPage(selected)
                viewer.AddHighlight(infos[1:], start, end, highlight_type)
        else:
            IDEFrame.ShowHighlight(self, infos, start, end, highlight_type)