diff --git a/CHANGELOG.md b/CHANGELOG.md index d9c61d94..05b5a9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ [unreleased] - Enhancement: Add right-click menu option to clear resume-at-line setting +- Enhancement: Display collet information in the manual toolchange popup, when using S1-S6 parameter for M6 toolchanges +- Enhancement: Display of the parsed line during file playback to visualize the lookahead buffer - Fixed: The 4th axis probing sequence for the z offset calibration (M469.5) was not passing pin diameter input through to the machine - Fixed: Viewing Gcode would cause an app crash when not connected to a machine diff --git a/carveracontroller/CNC.py b/carveracontroller/CNC.py index b1b0b4a3..d40e02b4 100644 --- a/carveracontroller/CNC.py +++ b/carveracontroller/CNC.py @@ -84,6 +84,7 @@ class CNC: "cutter" : "", "tlo" : 0.0, "target_tool": -1, + "target_collet_type": 0, "program" : "M0", "spindle" : "M5", "coolant" : "M9", @@ -91,6 +92,7 @@ class CNC: "playedlines": 0, "playedpercent": 0, "playedseconds": 0, + "parsedlines": -1, "atc_state": 0, diff --git a/carveracontroller/Controller.py b/carveracontroller/Controller.py index 03de44a6..40f34ed7 100644 --- a/carveracontroller/Controller.py +++ b/carveracontroller/Controller.py @@ -1239,10 +1239,15 @@ def parseBracketAngle(self, line,): CNC.vars["target_tool"] = int(d['T'][2]) else: CNC.vars["target_tool"] = -1 + if len(d['T']) > 3: + CNC.vars["target_collet_type"] = int(d['T'][3]) + else: + CNC.vars["target_collet_type"] = 0 else: CNC.vars["tool"] = -1 CNC.vars["tlo"] = 0.0 CNC.vars["target_tool"] = -1 + CNC.vars["target_collet_type"] = 0 if 'W' in d: CNC.vars["wpvoltage"] = float(d['W'][0]) if 'L' in d: @@ -1257,11 +1262,13 @@ def parseBracketAngle(self, line,): CNC.vars["playedseconds"] = int(d['P'][2]) if len(d['P']) >= 4: CNC.vars["is_playing"] = int(d['P'][3]) + if len(d['P']) >= 5: + CNC.vars["parsedlines"] = int(d['P'][4]) else: # not playing file CNC.vars["playedlines"] = -1 CNC.vars["is_playing"] = 0 - + CNC.vars["parsedlines"] = -1 if 'A' in d: CNC.vars["atc_state"] = int(d['A'][0]) else: diff --git a/carveracontroller/main.py b/carveracontroller/main.py index ac8eab96..88da896f 100644 --- a/carveracontroller/main.py +++ b/carveracontroller/main.py @@ -370,7 +370,7 @@ def _do_update_line_highlighting(self, dt): if current_pos and len(current_pos) > 1: line_number = current_pos[1] if line_number > 0 and hasattr(app.root, 'gcode_rv'): - app.root.gcode_rv.set_selected_line(line_number) + app.root.gcode_rv.set_selected_line_and_parsed_line(line_number, -1) def on_value(self, instance, value): """Called when the slider value changes (both programmatically and manually)""" @@ -2034,6 +2034,7 @@ class GCodeRow(RecycleDataViewBehavior, BoxLayout): """Single row in GCodeRV: line number, optional resume-flag icon, gcode text.""" index = None selected = BooleanProperty(False) + parsed = BooleanProperty(False) selectable = BooleanProperty(True) line_no = NumericProperty(0) text = StringProperty('') @@ -2473,6 +2474,7 @@ class GCodeRV(RecycleView): scroll_time = 0 old_selected_line = 0 new_selected_line = 0 + new_parsed_line = 0 def __init__(self, **kwargs): super(GCodeRV, self).__init__(**kwargs) @@ -2483,15 +2485,26 @@ def on_scroll_stop(self, touch): - def set_selected_line(self, line): + def set_selected_line_and_parsed_line(self, line, parsed_line): app = App.get_running_app() aiming_page = int(line / MAX_LOAD_LINES) + (0 if line % MAX_LOAD_LINES == 0 else 1) if aiming_page != app.curr_page: app.root.load_page(aiming_page) line = line % MAX_LOAD_LINES + + aiming_page_parsed = int(parsed_line / MAX_LOAD_LINES) + (0 if parsed_line % MAX_LOAD_LINES == 0 else 1) + if aiming_page_parsed == aiming_page: + parsed_line = parsed_line % MAX_LOAD_LINES + else: + parsed_line = 0 + if self.data_length > 0 and line < self.data_length: page_lines = len(self.view_adapter.views) self.new_selected_line = line - 1 + if parsed_line > 0: + self.new_parsed_line = parsed_line - 1 + else: + self.new_parsed_line = -1 # Schedule all UI attribute updates for the next frame to avoid re-entry loops def update_selection_ui(dt): @@ -2499,6 +2512,12 @@ def update_selection_ui(dt): view = self.view_adapter.views[key] if view and hasattr(view, 'selected') and view.selected is not None: view.selected = False + if view and hasattr(view, 'parsed') and view.parsed is not None: + view.parsed = False + if self.new_parsed_line >= 0: + new_parsed_line = self.view_adapter.get_visible_view(self.new_parsed_line) + if new_parsed_line: + new_parsed_line.parsed = True new_line = self.view_adapter.get_visible_view(self.new_selected_line) if new_line: new_line.selected = True @@ -3784,6 +3803,8 @@ def open_tool_confirm_popup(self): if self.confirm_popup.showing: return target_tool = str(CNC.vars['target_tool']) + target_collet_type = CNC.vars['target_collet_type'] + target_collet_type_text = ["Undefined","3mm", "1/8\"", "4mm", "6mm", "1/4\"","8mm"] if CNC.vars['target_tool'] == 0: target_tool = 'Probe' elif CNC.vars['target_tool'] == 8888: @@ -3796,8 +3817,12 @@ def open_tool_confirm_popup(self): #target is valid tool if CNC.vars['target_tool'] != -1: if CNC.vars['tool'] == -1: - self.confirm_popup.lb_title.text = tr._('Manual toolchange') - self.confirm_popup.lb_content.text = tr._('Insert tool: ') + '%s\n' % (target_tool) + tr._(' Then press \' Confirm\' or main button to clamp.\n') + if target_collet_type == 0: + self.confirm_popup.lb_title.text = tr._('Manual toolchange') + self.confirm_popup.lb_content.text = tr._('Insert tool: ') + '%s\n' % (target_tool) + tr._('Then press \' Confirm\' or main button to clamp.\n') + else: + self.confirm_popup.lb_title.text = tr._('Manual toolchange') + self.confirm_popup.lb_content.text = tr._('Change to collet: ') + '%s\n' % (target_collet_type_text[target_collet_type]) + tr._('Insert tool: ') + '%s\n' % (target_tool) + tr._('Then press \' Confirm\' or main button to clamp.\n') else: self.confirm_popup.lb_title.text = tr._('Manual toolchange') self.confirm_popup.lb_content.text = tr._('When the tool is clamped press \' Confirm\' or main button to proceed.\nKeep your hands off the spindle unless you are willing to lose a finger!') @@ -3806,8 +3831,12 @@ def open_tool_confirm_popup(self): self.confirm_popup.lb_title.text = tr._('Hold tool') self.confirm_popup.lb_content.text = tr._('Please hold the current tool and press \' Confirm\' or main button to proceed') else: - self.confirm_popup.lb_title.text = tr._('Open clamp') - self.confirm_popup.lb_content.text = tr._('When the collet is empty press \' Confirm\' or main button to proceed') + if target_collet_type == 0: + self.confirm_popup.lb_title.text = tr._('Confirm empty') + self.confirm_popup.lb_content.text = tr._('When the collet is empty press \' Confirm\' or main button to proceed') + else: + self.confirm_popup.lb_title.text = tr._('Confirm empty') + self.confirm_popup.lb_content.text = tr._('Change to collet: ') + '%s\n' % (target_collet_type_text[target_collet_type]) + tr._('When the collet is changed and empty press \' Confirm\' or main button to proceed') else: self.confirm_popup.lb_title.text = tr._('Changing Tool') self.confirm_popup.lb_content.text = tr._('Please change to tool: ') + '%s\n' % (target_tool) + tr._('Then press \' Confirm\' or main button to proceed') @@ -5061,6 +5090,7 @@ def updateStatus(self, *args): self.wpb_leveling.value = 0 self.wpb_play.value = 0 self.progress_info = "" + self.parsed_lines = 0 # Stop smooth progress updates if self._progress_smooth_clock is not None: self._progress_smooth_clock.cancel() @@ -5078,11 +5108,12 @@ def updateStatus(self, *args): self.progress_info = tr._(' No Remote File Selected') + last_job_elapsed else: app.playing = True - if self.played_lines != CNC.vars["playedlines"]: + if self.played_lines != CNC.vars["playedlines"] or self.parsed_lines != CNC.vars["parsedlines"]: self.played_lines = CNC.vars["playedlines"] + self.parsed_lines = CNC.vars["parsedlines"] self.wpb_play.value = CNC.vars["playedpercent"] if (app.selected_remote_filename != '' or app.selected_local_filename != '') and self.selected_file_line_count > 0: - self.gcode_rv.set_selected_line(self.played_lines) + self.gcode_rv.set_selected_line_and_parsed_line(self.played_lines, self.parsed_lines) self.gcode_viewer.set_distance_by_lineidx(self.played_lines, 0.5) remaining_sec = self.gcode_viewer.get_remaining_time_by_lineidx(self.played_lines, 0.5) if remaining_sec is not None and remaining_sec >= 0: @@ -5282,7 +5313,7 @@ def moveLineIndex(self, up = True): self.test_line = self.test_line + 1 if self.test_line == 0: self.test_line = 1 - self.gcode_rv.set_selected_line(self.test_line - 1) + self.gcode_rv.set_selected_line_and_parsed_line(self.test_line - 1, -1) def execCallback(self, line): logger.info(f"MDI Sent: {line}") @@ -5878,7 +5909,11 @@ def gcode_play_call_back(self, distance, line_number): if getattr(self, '_skip_next_set_selected_line_from_callback', False): self._skip_next_set_selected_line_from_callback = False elif line_number > 0 and hasattr(self, 'gcode_rv'): - self.gcode_rv.set_selected_line(line_number) + if CNC.vars.get('is_playing', 0) == 1: + parsed = CNC.vars.get('parsedlines', 0) + self.gcode_rv.set_selected_line_and_parsed_line(line_number, parsed) + else: + self.gcode_rv.set_selected_line_and_parsed_line(line_number, -1) # ----------------------------------------------------------------------- def gcode_play_over_call_back(self): diff --git a/carveracontroller/makera.kv b/carveracontroller/makera.kv index 9766ef42..11c7da20 100644 --- a/carveracontroller/makera.kv +++ b/carveracontroller/makera.kv @@ -188,6 +188,12 @@ Rectangle: pos: self.pos size: self.size + # Parsed-line marker: cyan left border when this row is the parsed line + Color: + rgba: (0, 0.6, 0.85, 1) if self.parsed else (0, 0, 0, 0) + Rectangle: + pos: self.pos + size: (dp(3), self.height) if self.parsed else (0, 0) Label: size_hint_x: None width: dp(72)