def set_ready(self): """Initializes the gantries to their current positions.""" def set_ready_callback(): for dvc in self.devices: dvc.is_homed = True if self._is_machine_busy: print_error_msg(self.console, 'Cannot set or go to ready. The machine is busy.') return init_code = ActionType.G1 if self.is_machine_homed else ActionType.G92 cmds = self._get_initialization_commands(init_code) if cmds: self._mainqueue = [] self._mainqueue.extend(cmds) self._mainqueue.extend(self._disengage_motors_commands) self._work_type = WorkType.SET_READY self._keep_working = True self._clear_to_send = True self._working_thread = threading.Thread( target=self._worker, name='working thread', kwargs={"extra_callback": set_ready_callback}) self._working_thread.start()
def connect(self, baud: int = serial_controller.BAUDS[-1]) -> bool: """Connects to the active serial port.""" if not self._is_serial_enabled: print_error_msg(self.console, 'Serial is not enabled') else: connected = self._serial.open_port(baud) if connected: self._connected_on = datetime.now() port_name = next( filter(lambda p: p.is_connected and p.is_active, self.serial_port_list)).name print_info_msg(self.console, f'Connected to device {port_name}.') read_thread = threading.Thread(target=self._listener, name=f'read thread {port_name}') self._read_threads.append( ReadThread(thread=read_thread, port=port_name)) read_thread.start() else: print_error_msg(self.console, 'Unable to connect to device.') self._is_new_connection = connected return connected
def _unlock_machine(self): print_debug_msg(self.console, '**** Unlocking machine ****', self._is_dev_env) cmds = [] if self._is_machine_busy: print_error_msg(self.console, 'Cannot unlock. The machine is busy.') return False for dvc in self.devices: if dvc.serial_response: flags = dvc.serial_response.system_status_flags if flags and len(flags) == 8 and int(flags[0]): cmd = Action(ActionType.M511, dvc.device_id) cmds.append(cmd) if cmds: self._keep_working = True self._clear_to_send = True self._mainqueue = [] self._mainqueue.append(cmds) self._send_next() self._keep_working = False return True return False
def _worker(self, resuming=False, extra_callback=None) -> None: """Implements a worker thread.""" t_name = self.work_type_name state = "resumed" if resuming else "started" print_debug_msg(self.console, f'{t_name} thread {state}', self._is_dev_env) def callback(): if extra_callback: extra_callback() print_info_msg(self.console, f'{t_name} ended.') dispatcher.connect(callback, signal='notify_machine_idle') print_info_msg(self.console, f'{t_name} started.') had_error = False try: while self._keep_working and self.is_serial_port_connected: self._send_next() except AttributeError as err: print_error_msg(self.console, f'{t_name} thread stopped unexpectedly: {err.args[0]}') had_error = True finally: self._working_thread = None self._keep_working = False if not self._is_machine_paused: self._work_type = None if not had_error: print_debug_msg(self.console, f'{t_name} thread stopped.', self._is_dev_env)
def select_serial_port(self, name: str) -> bool: """Sets the active serial port to the provided one.""" selected = self._serial.select_port(name) if not selected: print_error_msg(self.console, 'Unable to select serial port.') return selected
def select_device(self, index: int) -> None: """Select device given index in devices list.""" if index < 0: self._selected_device = -1 dispatcher.send('core_d_deselected') elif index < len(self._devices): self._selected_device = index self.select_point(-1) dispatcher.send('core_o_deselected') dispatcher.send('core_d_selected', device=self._devices[index]) else: print_error_msg(self.console, f'invalid device index {index}.')
def resume_work(self) -> bool: """Resume the current run.""" if not self._is_machine_paused: print_error_msg(self.console, 'Cannot resume; machine is not paused.') return False self._is_machine_paused = False self._keep_working = True self._clear_to_send = True self._working_thread = threading.Thread(target=self._worker, name='working thread', kwargs={"resume": True}) self._working_thread.start() return True
def select_object(self, id_: int) -> None: """Select proxy object given id.""" if id_ < 0: self._deselect_object() dispatcher.send('core_o_deselected') elif id_ in (x.object_id for x in self._objectvis.objects): self.core.select_point(-1) self.core.select_device(-1) for obj in self._objectvis.objects: obj.selected = obj.object_id == id_ dispatcher.send('core_o_selected', object=self.core.objects[id_]) self._dirty = True else: print_error_msg(self.core.console, f'invalid proxy object id {id_}.')
def _query_machine(self): print_debug_msg(self.console, '**** Querying machine ****', self._is_dev_env) cmds = [] if self._is_machine_busy: print_error_msg(self.console, 'Cannot query. The machine is busy.') return for dvc in self.devices: cmds.append(Action(ActionType.G0, dvc.device_id)) if cmds: self._keep_working = True self._clear_to_send = True self._mainqueue = [] self._mainqueue.append(cmds) self._send_next() self._keep_working = False
def jog(self, action: Action): """Jogs the machine according to the provided action.""" if self._is_machine_busy: print_error_msg(self.console, 'Cannot jog. The machine is busy.') return header = self._get_move_commands(False, action.device) body = [action] footer = self._get_move_commands(True, action.device) self._mainqueue = [] self._mainqueue.extend(header) self._mainqueue.append(body) self._mainqueue.extend(footer) self._work_type = WorkType.JOGGING self._keep_working = True self._clear_to_send = True self._working_thread = threading.Thread(target=self._worker, name='working thread') self._working_thread.start()
def pause_work(self) -> bool: """Pause work in progress, saving the current position.""" if not self._working_thread: print_error_msg(self.console, 'Cannot pause. The machine is not busy.') return False self._is_machine_paused = True self._keep_working = False # try joining the print thread: enclose it in try/except because we # might be calling it from the thread itself try: self._working_thread.join() self._working_thread = None print_info_msg(self.console, f'{self.work_type_name} paused.') return True except RuntimeError as err: print_error_msg(self.console, f'Cannot join working thread: {err.args[0]}') return False
def start_homing(self) -> bool: """Start the homing sequence, following the steps in the configuration.""" def homing_callback(): for dvc in self.devices: dvc.is_homed = True if not self.is_serial_port_connected: print_error_msg( self.console, 'The machine needs to be connected before homing can start.') return False if self._is_machine_busy: print_error_msg(self.console, 'The machine is busy.') return False homing_actions = self.config.machine_settings.machine.homing_actions.copy( ) if not homing_actions or len(homing_actions) == 0: print_error_msg(self.console, 'No homing sequence to provided.') return False # Only send homing commands for connected devices. all_device_ids = [d.device_id for d in self.devices] homing_actions = list( filter(lambda c: c.device in all_device_ids, homing_actions)) device_ids = list(set(a.device for a in homing_actions)) batch_size = len(device_ids) header = self._get_move_commands(True, *device_ids) body = self._chunk_actions(batch_size, homing_actions) footer = self._get_initialization_commands(ActionType.G1) footer.extend(self._disengage_motors_commands) self._mainqueue = [] self._mainqueue.extend(header) self._mainqueue.extend(body) self._mainqueue.extend(footer) self._work_type = WorkType.HOMING self._keep_working = True self._clear_to_send = True self._working_thread = threading.Thread( target=self._worker, name='working thread', kwargs={"extra_callback": homing_callback}) self._working_thread.start() print_info_msg(self.console, 'Homing started.') return True
def start_imaging(self) -> bool: """Starts the imaging sequence, following the define action path.""" if not self.is_serial_port_connected: print_error_msg( self.console, 'The machine needs to be connected before imaging can start.') return False if self._is_machine_busy: print_error_msg(self.console, 'The machine is busy.') return False if not self.is_machine_idle: print_error_msg( self.console, 'The machine needs to be homed before imaging can start.') return False device_ids = list(set(a.device for a in self._actions)) # 1 move command * 1 shutter command. batch_size = 2 if self.config.machine_settings.machine.is_parallel_execution: batch_size = batch_size * len(device_ids) header = self._get_move_commands( True, *[dvc.device_id for dvc in self.devices]) body = self._chunk_actions(batch_size) footer = self._get_initialization_commands(ActionType.G1) footer.extend(self._disengage_motors_commands) self._mainqueue = [] self._mainqueue.extend(header) self._mainqueue.extend(body) self._mainqueue.extend(footer) self._work_type = WorkType.IMAGING self._keep_working = True self._clear_to_send = True self._working_thread = threading.Thread(target=self._worker, name='working thread') self._working_thread.start() return True