wxPopen.py
author laurent
Thu, 08 Oct 2009 11:26:40 +0200
changeset 411 8261c8f1e365
parent 361 331d698e1118
child 421 c9ec111ad275
permissions -rw-r--r--
Bug on Debug trying to start (and stop) before PLC started fixed.
Adding support for detecting platform default settings for target type and canfestival node.
Clear tests folder, leaving only multi-platform tests.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of Beremiz, a Integrated Development Environment for
#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#General Public License for more details.
#
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


import time
import wx
import subprocess, ctypes
import threading
import os
if os.name == 'posix':
    from signal import SIGTERM, SIGKILL

    
class outputThread(threading.Thread):
    """
    Thread is used to print the output of a command to the stdout
    """
    def __init__(self, Proc, fd, callback=None, endcallback=None):
        threading.Thread.__init__(self)
        self.killed = False
        self.finished = False
        self.retval = None
        self.Proc = Proc
        self.callback = callback
        self.endcallback = endcallback
        self.fd = fd

    def run(self):
        outchunk = None
        self.retval = None
        while outchunk != '' and not self.killed :
            outchunk = self.fd.readline()
            if self.callback : self.callback(outchunk)
        while self.retval is None and not self.killed :
            self.retval = self.Proc.poll()
            outchunk = self.fd.readline()
            if self.callback : self.callback(outchunk)
        while outchunk != '' and not self.killed :
            outchunk = self.fd.readline()
        if self.endcallback:
            try:
                err = self.Proc.wait()
            except:
                err = self.retval
            self.finished = True
            wx.CallAfter(self.endcallback, self.Proc.pid, err)

class ProcessLogger:
    def __init__(self, logger, Command, finish_callback=None, no_stdout=False, no_stderr=False, no_gui=True):
        self.logger = logger
        self.Command_str = Command
        self.Command = []
        for i,word in enumerate(Command.replace("'",'"').split('"')):
            if i % 2 == 0:
                word = word.strip()
                if len(word) > 0:
                    self.Command.extend(word.split())
            else:
                self.Command.append(word)

        self.finish_callback = finish_callback
        self.no_stdout = no_stdout
        self.no_stderr = no_stderr
        self.startupinfo = None
        self.errlen = 0
        self.outlen = 0
        self.exitcode = None
        self.outdata = ""
        self.errdata = ""
        self.finished = False
        
        popenargs= {
               "cwd":os.getcwd(),
               "stdin":subprocess.PIPE, 
               "stdout":subprocess.PIPE, 
               "stderr":subprocess.PIPE}
        if no_gui == True and wx.Platform == '__WXMSW__':
            self.startupinfo = subprocess.STARTUPINFO()
            self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            popenargs["startupinfo"] = self.startupinfo
        elif wx.Platform == '__WXGTK__':
            popenargs["shell"] = False  #True
        
        self.Proc = subprocess.Popen( self.Command, **popenargs )

        self.outt = outputThread(
                      self.Proc,
                      self.Proc.stdout,
                      self.output,
                      self.finish) 
        self.outt.start()

        self.errt = outputThread(
                      self.Proc,
                      self.Proc.stderr,
                      self.errors)
        self.errt.start()

    def output(self,v):
        self.outdata += v
        self.outlen += 1
        if not self.no_stdout:
            wx.CallAfter(self.logger.write,v)

    def errors(self,v):
        self.errdata += v
        self.errlen += 1
        if not self.no_stderr:
            wx.CallAfter(self.logger.write_warning,v)

    def log_the_end(self,ecode,pid):
        self.logger.write(self.Command_str + "\n")
        self.logger.write_warning(_("exited with status %s (pid %s)\n")%(str(ecode),str(pid)))

    def finish(self, pid,ecode):
        self.finished = True
        self.exitcode = ecode
        if self.exitcode != 0:
            wx.CallAfter(self.log_the_end,ecode,pid)
        if self.finish_callback is not None:
            self.finish_callback(self,ecode,pid)

    def kill(self,gently=True):
        self.outt.killed = True
        self.errt.killed = True
        if wx.Platform == '__WXMSW__':
            PROCESS_TERMINATE = 1
            handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.Proc.pid)
            ctypes.windll.kernel32.TerminateProcess(handle, -1)
            ctypes.windll.kernel32.CloseHandle(handle)
        else:
            if gently:
                sig=SIGTERM
            else:
                sig=SIGKILL
            try:
                os.kill(self.Proc.pid, sig)
            except:
                pass
        self.outt.join()
        self.errt.join()

    def spin(self, timeout=None, out_limit=None, err_limit=None, keyword = None, kill_it = True):
        count = 0
        while not self.finished:
            if err_limit and self.errlen > err_limit:
                break
            if out_limit and self.outlen > out_limit:
                break
            if timeout:
                if count > timeout:
                    break
                count += 1
            if keyword and self.outdata.find(keyword)!=-1:
                    break
            wx.Yield()
            time.sleep(0.01)

        if not self.outt.finished and kill_it:
            self.kill()

        return [self.exitcode, self.outdata, self.errdata]