Example #1
0
 def _pop_next_cmd(self):
     """In active mode, return manual command if one is available
     otherwise pop next command from tasks.
     In safe mode, only consider commands from task.
     If no command at all or in another mode, return None.
     """
     if self.is_active():
         try:
             manual_cmd = self.manual_cmd_queue.get_nowait()
         except queue.Empty:
             pass
         else:
             if self.cur_task is None:
                 self.cur_task = Task([manual_cmd])
             else:
                 return manual_cmd
     if self.is_active() or self.in_safe_mode():
         try:
             return self.cur_task.pop()
         except IndexError:
             self.cur_task.close()
             try:
                 self.cur_task = self.task_list.pop(0)
             except IndexError:
                 self.cur_task = None
                 self.state = self.ST_INAC
             else:
                 return self.cur_task.pop()
     return None
Example #2
0
 def main_producer():
     job = Job(config.JOB_ID)
     for i in range(config.TASK_NUM):
         job.tasks.append(Task(i))
     
     p = ProducerClient()
     t1 = threading.Thread(target=p.submit_job, args=(job,))
     t1.start()
     p.monitor_job(job)
Example #3
0
 def run_file(self, filename):
     """Run a raw GCode from file.
     """
     if self.is_inactive():
         with open(filename) as f:
             cmd_list = f.read().splitlines()
         if cmd_list:
             self.mutex.lock()
             self._kickstart(Task(cmd_list))
             self.mutex.unlock()
Example #4
0
 def send_manual_cmd(self, cmd):
     """Push a manual command to waiting queue and eventually kickstart it.
     """
     self.mutex.lock()
     # remove leading/trailing spaces
     cmd = re.sub(r'(^\s*)|(\s*$)', '', cmd)
     if self.is_active():
         self.manual_cmd_queue.put(cmd)
     elif self.is_inactive():
         self._kickstart(Task([cmd]))
     self.mutex.unlock()
Example #5
0
class ControllerBase(QtCore.QObject):
    link_state_update = QtCore.pyqtSignal()
    ST_UNCO = 0
    ST_INAC = 1
    ST_ACTI = 2
    ST_SAFE = 3

    def __init__(self, project, post_processor):
        super().__init__()
        self.project = project
        self.post_processor = post_processor

        self.keep_workers = False
        self.input_thread = GenericThread(self._input_worker)
        self.output_thread = GenericThread(self._output_worker)
        self.send_cond = QtCore.QWaitCondition()
        self.mutex = QtCore.QMutex()

        # NOTE better use SimpleQueue in 3.7
        self.manual_cmd_queue = queue.Queue()
        self.com_logger = CommunicationLogger()
        self.state = self.ST_UNCO

    def __del__(self):
        self.disconnect()

    def run_file(self, filename):
        """Run a raw GCode from file.
        """
        if self.is_inactive():
            with open(filename) as f:
                cmd_list = f.read().splitlines()
            if cmd_list:
                self.mutex.lock()
                self._kickstart(Task(cmd_list))
                self.mutex.unlock()

    def run(self, dry_run):
        """Make post-processor generate project's GCode as a list of tasks and
        start running it.
        """
        self.mutex.lock()
        if self.is_inactive():
            tasks = self.project.generate_tasks(self.post_processor, dry_run)
            self.task_list = tasks
            try:
                task = self.task_list.pop(0)
            except:
                print('No job to run.')
            else:
                self._kickstart(task)
                self.send_cond.wakeOne()
        self.mutex.unlock()

    def stop(self):
        """Discard any tasks and manual commands except the one running.
        """
        self.mutex.lock()
        if self.is_active():
            self.manual_cmd_queue.queue.clear()
            self.task_list = []
        self.mutex.unlock()

    def abort(self):
        """Discard all tasks and commands, setup emergency task instead and
        switch to safe mode to prevent any interruption of the emergency task.
        """
        self.mutex.lock()
        if self.is_active():
            self._enter_safe_mode()
        self.mutex.unlock()

    def _abort_internal(self, message):
        """Internal version of abort with mutex acquired on caller side and
        error logging.
        """
        if self.is_active():
            self._enter_safe_mode()
            self.com_logger.incident.emit(message)

    def _enter_safe_mode(self):
        """Factorization of abort and _abort_internal behaviour.
        """
        self.manual_cmd_queue.queue.clear()
        self.cur_task.fail()
        self.task_list = [self.post_processor.emergency_task()]
        self.state = self.ST_SAFE

    def send_manual_cmd(self, cmd):
        """Push a manual command to waiting queue and eventually kickstart it.
        """
        self.mutex.lock()
        # remove leading/trailing spaces
        cmd = re.sub(r'(^\s*)|(\s*$)', '', cmd)
        if self.is_active():
            self.manual_cmd_queue.put(cmd)
        elif self.is_inactive():
            self._kickstart(Task([cmd]))
        self.mutex.unlock()

    @abstractmethod
    def _link_open(self, *args, **kwargs):
        """Open communication link."""
        pass

    @abstractmethod
    def _link_close(self):
        """Close communication link."""
        pass

    @abstractmethod
    def _link_read(self):
        """Blocking read with timeout. Return complete line or empty string."""
        pass

    @abstractmethod
    def _link_send(self, cmd):
        """Sends a command and eventually adds end characters to it."""
        pass

    @abstractmethod
    def _link_busy(self):
        """Return True when machine controller is ready for next command to be
        sent. Implementation can use self.next_cmd if length information is
        needed.
        """
        pass

    @abstractmethod
    def _process_input(self, input):
        """Parse a line from the machine controller."""
        pass

    def is_unconnected(self):
        return self.state == self.ST_UNCO

    def is_inactive(self):
        return self.state == self.ST_INAC

    def is_active(self):
        return self.state == self.ST_ACTI

    def in_safe_mode(self):
        return self.state == self.ST_SAFE

    def connect(self, *args, **kwargs):
        """Connect to machine controller."""
        if self.is_unconnected():
            try:
                self._link_open(args, kwargs)
            except Exception as e:
                print('Connection failed: ' + str(e), file=sys.stderr)
            else:
                self.task_list = []
                self.cur_task = None
                self.next_cmd = None
                self.keep_workers = True
                self.input_thread.start()
                self.output_thread.start()
                self.state = self.ST_INAC
                self.link_state_update.emit()

    def disconnect(self):
        """Close link with machine controller. It can be called either from
        external thread or its self worker threads.
        """
        if self.is_inactive():
            self.mutex.lock()
            self.keep_workers = False
            self.send_cond.wakeOne()
            self.mutex.unlock()
            if QtCore.QThread.currentThread() != self.input_thread:
                self.input_thread.wait()
            if QtCore.QThread.currentThread() != self.output_thread:
                self.output_thread.wait()
            self._link_close()
            self.state = self.ST_UNCO
            self.link_state_update.emit()

    def _input_worker(self):
        """Input thread working loop."""
        while self.keep_workers:
            line = ''
            while (not line or line[-1] != '\n') and self.keep_workers:
                try:
                    line += self._link_read()
                except Exception as e:
                    print('Link read failed: ' + str(e), file=sys.stderr)
                    self.disconnect()
                    return
            if self.keep_workers:
                line = line.rstrip()
                self.mutex.lock()
                self._process_input(line)
                self.com_logger.log_received_data(line)
                self.mutex.unlock()

    def _output_worker(self):
        """Output thread working loop."""
        while self.keep_workers:
            self.mutex.lock()
            while ((self.next_cmd is None or self._link_busy())
                   and self.keep_workers):
                self.send_cond.wait(self.mutex)
            if self.keep_workers:
                try:
                    self._link_send(self.next_cmd)
                except Exception as e:
                    self.mutex.unlock()
                    print('Link send failed: ' + str(e), file=sys.stderr)
                    self.disconnect()
                    return
                self.com_logger.log_sent_data(self.next_cmd)
                self.next_cmd = None
            self.mutex.unlock()

    def _kickstart(self, task):
        """Wake up worker thread to run task."""
        self.cur_task = task
        self.next_cmd = self.cur_task.pop()
        self.state = self.ST_ACTI
        self.send_cond.wakeOne()

    def _pop_next_cmd(self):
        """In active mode, return manual command if one is available
        otherwise pop next command from tasks.
        In safe mode, only consider commands from task.
        If no command at all or in another mode, return None.
        """
        if self.is_active():
            try:
                manual_cmd = self.manual_cmd_queue.get_nowait()
            except queue.Empty:
                pass
            else:
                if self.cur_task is None:
                    self.cur_task = Task([manual_cmd])
                else:
                    return manual_cmd
        if self.is_active() or self.in_safe_mode():
            try:
                return self.cur_task.pop()
            except IndexError:
                self.cur_task.close()
                try:
                    self.cur_task = self.task_list.pop(0)
                except IndexError:
                    self.cur_task = None
                    self.state = self.ST_INAC
                else:
                    return self.cur_task.pop()
        return None

    def _complete_cmd(self):
        """Function to be called by input parser when a command is completed.
        """
        self.next_cmd = self._pop_next_cmd()
        self.send_cond.wakeOne()
Example #6
0
 def emergency_task(self):
     return Task(self._abort_seq)
Example #7
0
 def init_task(self):
     return Task(self._init_seq)