from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import QRunnable, QObject, QThreadPool import multiprocessing as mp import logging, sys, time, traceback, os, signal from datetime import datetime from Pythonic.record_function import Record from Pythonic.elementeditor import ElementEditor from Pythonic.record_function import alphabet from Pythonic.exceptwindow import ExceptWindow from Pythonic.debugwindow import DebugWindow from Pythonic.elements.basic_stack import ExecStack from Pythonic.elements.basic_sched import ExecSched from Pythonic.elements.basicelements import ExecRB, ExecR class WorkerSignals(QObject): finished = pyqtSignal(object, name='element_finished' ) pid_sig = pyqtSignal(object) class GridOperator(QObject): update_logger = pyqtSignal(name='update_logger') exec_pending = pyqtSignal(name='exec_pending') switch_grid = pyqtSignal('PyQt_PyObject', name='switch_grid') def __init__(self, grid, number): super().__init__() logging.debug('__init__() called on GridOperator') self.grid = grid self.number = number # number of workingarea [0-4] self.stop_flag = False self.fastpath = False # fastpath is active when debug is diasbled self.retry_counter = 0 self.delay = 0 self.threadpool = QThreadPool() self.b_debug_window = False self.pending_return = [] self.pid_register = [] self.exec_pending.connect(self.checkPending) logging.debug('__init__() GridOperator, threadCount: {}'.format( self.threadpool.maxThreadCount())) def startExec(self, start_pos, record=None): logging.debug('startExec() called, start_pos = {}'.format(start_pos)) try: element = self.grid.itemAtPosition(*start_pos).widget() except AttributeError as e: return if self.stop_flag: return self.update_logger.emit() executor = Executor(element, record, self.delay) executor.signals.finished.connect(self.execDone) executor.signals.pid_sig.connect(self.register_pid) element.highlightStart() self.threadpool.start(executor) def register_pid(self, pid): # register PID of spawned child process self.pid_register.append(pid) logging.debug('PID register: {}'.format(self.pid_register)) def execDone(self, prg_return): logging.debug('execDone() called GridOperator from {}'.format(prg_return.source)) element = self.grid.itemAtPosition(*prg_return.source).widget() logging.debug('PID returned: {}'.format(prg_return.pid)) # remove returned pid from register try: # does not work in case of an exception self.pid_register.remove(prg_return.pid) except Exception as e: logging.error('De-registration of PID failed: {}'.format(e)) # if an execption occured if(issubclass(prg_return.record_0.__class__, BaseException)): logging.error('Grid {} Target {}|{} Exception found: {}'.format( self.number + 1, prg_return.source[0], alphabet[prg_return.source[1]], prg_return.record_0)) element.highlightException() self.exceptwindow = ExceptWindow(str(prg_return.record_0), prg_return.source) self.exceptwindow.window_closed.connect(self.highlightStop) return ### proceed with regular execution ### # when the log checkbox is activated if prg_return.log: if prg_return.log_txt: logging.info('Grid: {} Message {}|{} : {}'.format( self.number + 1, prg_return.source[0], alphabet[prg_return.source[1]], prg_return.log_txt)) else: logging.info('Grid: {} Message {}|{} : {}'.format( self.number + 1, prg_return.source[0], alphabet[prg_return.source[1]], prg_return.record_0)) # when the debug button on the element is active if element.b_debug: logging.debug('GridOperator::execDone() b_debug_window = {}'.format(self.b_debug_window)) if isinstance(element, ExecStack): # don't open the regular debug window logging.debug('GridOperator::execDone()Special window for Exec stack element') element.highlightStop() self.goNext(prg_return) # check if there is already an open debug window elif not self.b_debug_window: self.debugWindow = DebugWindow(str(prg_return.record_0), prg_return.source) self.debugWindow.proceed_execution.connect(lambda: self.proceedExec(prg_return)) self.debugWindow.raiseWindow() #if not element.self_sync: self.b_debug_window = True else: self.pending_return.append(prg_return) else: # highlight stop =! element.highlightStop() self.goNext(prg_return) def checkPending(self): logging.debug('GridOperator::checkPending() called') if self.pending_return: prg_return = self.pending_return.pop(0) self.execDone(prg_return) def proceedExec(self, prg_return): element = self.grid.itemAtPosition(*prg_return.source).widget() element.highlightStop() self.b_debug_window = False self.exec_pending.emit() self.goNext(prg_return) def goNext(self, prg_return): # check is target_0 includes a diffrent grid # ExecReturn elemenot if prg_return.target_0: logging.debug('GridOperator::goNext() called with next target_0: {}'.format(prg_return.target_0)) logging.debug('GridOperator::goNext() called with record_0: {}'.format(prg_return.record_0)) if self.fastpath: if len(prg_return.target_0) == 3: # switch grid, go over main # fastpath = True self.switch_grid.emit((prg_return, True)) return new_rec = self.fastPath(prg_return.target_0, prg_return.record_0) if new_rec: # check for ExecR or ExecRB self.goNext(new_rec) else: # if nothing found: proceed as usual self.startExec(prg_return.target_0, prg_return.record_0) else: if len(prg_return.target_0) == 3: # switch grid, go over main # fastpath = False self.switch_grid.emit((prg_return, False)) return self.startExec(prg_return.target_0, prg_return.record_0) if prg_return.target_1: logging.debug('GridOperator::goNext() called with additional target_1: {}'.format( prg_return.target_1)) logging.debug('GridOperator::goNext() called with record_1: {}'.format(prg_return.record_1)) # self_sync is true on basic_sched and binancesched self_sync = self.grid.itemAtPosition(*prg_return.target_1).widget().self_sync if self.fastpath and not self_sync: new_rec = self.fastPath(prg_return.target_1, prg_return.record_1) logging.debug('GridOperator::goNext() execption here') logging.debug('GridOperator::goNext() new_rec: {}'.format(new_rec)) self.goNext(new_rec) else: self.startExec(prg_return.target_1, prg_return.record_1) def fastPath(self, target, record): logging.debug('GridOperator::fastPath() check row: {} col: {}'.format(*target)) element = self.grid.itemAtPosition(*target).widget() if isinstance(element, ExecRB): # jump to the next target # record_1 -> record_0 when goNext() is called recursively # returning only target_0 and record_0 new_rec = Record(element.getPos(), (element.row+1, element.column), record) return new_rec elif isinstance(element, ExecR): # jump to the next target #hier testen ob target fings # record_1 -> record_0 when goNext() is called recursively # returning only target_0 and record_0 new_rec = Record(element.getPos(), (element.row, element.column+1), record) return new_rec else: return None def highlightStop(self, position): logging.debug('highlightStop() called for position {}'.format(position)) element = self.grid.itemAtPosition(*position).widget() element.highlightStop() def stop_execution(self): logging.debug('stop_execution() called') self.stop_flag = True def kill_proc(self): logging.debug('kill_proc() called') for proc in self.pid_register: os.kill(proc, signal.SIGTERM) logging.info('Process killed, PID {}'.format(proc)) self.pid_register.clear() class Executor(QRunnable): def __init__(self, element, record, delay): super().__init__() logging.debug('Executor::__init__() called') self.element = element self.record = record self.stop_flag = False self.retry_counter = 0 self.delay = delay self.signals = WorkerSignals() def run(self): logging.debug('Executor::run() called with target {} pid {} at {}'.format( self.element.getPos(), os.getpid(), datetime.now())) self.start_proc(self.element.function, self.record, self.delay, 1) logging.debug('Executor::run() returned from {}, pid: {} returned at {}'.format( self.element.getPos(), os.getpid(), datetime.now())) def start_proc(self, function, record, delay, retries): # Bug: Sometimes the Exception windows isnt triggered logging.debug('Executor::start_proc() called with programm: {}'.format(function)) return_pipe_0, feed_pipe_0 = mp.Pipe(duplex=False) p_0 = mp.Process(target=target_0, args=(function, record, feed_pipe_0, )) p_0.start() self.signals.pid_sig.emit(p_0.pid) time.sleep(delay) result = return_pipe_0.recv() p_0.join() self.signals.finished.emit(result) def target_0(function, record, feed_pipe): ret = function.execute_ex(record) feed_pipe.send(ret)