diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d8e91a5..fbdd6155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ CHANGELOG for FlatCAM beta - in CNCJob UI Autolevelling - sending GCode/GRBL commands is now threaded - in CNCJob UI Autolevelling - Grbl Connect tab colors will change with the connection status - in CNCJob UI Autolevelling - GRBL Control and Sender tabs are disabled when the serial port is disconnected +- in CNCJob UI Autolevelling - GRBL Sender - now only a single command can be sent +- in CNCJob UI Autolevelling - GRBL controller - changed the UI +- in CNCJob UI Autolevelling - added some VOronoi poly calculations 18.08.2020 diff --git a/appGUI/ObjectUI.py b/appGUI/ObjectUI.py index 3702f907..02fb34be 100644 --- a/appGUI/ObjectUI.py +++ b/appGUI/ObjectUI.py @@ -2072,7 +2072,7 @@ class CNCObjectUI(ObjectUI): self.al_toolbar = FCDetachableTab(protect=True, parent=self) self.al_toolbar.setTabsClosable(False) self.al_toolbar.useOldIndex(True) - + self.al_toolbar.set_detachable(val=False) self.grbl_box.addWidget(self.al_toolbar) # GRBL Connect TAB @@ -2176,8 +2176,9 @@ class CNCObjectUI(ObjectUI): ctrl_hlay = QtWidgets.QHBoxLayout() self.controller_reset_button = FCButton(_("Reset")) self.controller_reset_button.setToolTip( - _("SW reset the controller. CTRL+X command.") + _("Software reset of the controller.") ) + self.controller_reset_button.setDisabled(True) ctrl_hlay.addWidget(self.controller_reset_button) self.com_connect_button = FCButton() @@ -2206,11 +2207,13 @@ class CNCObjectUI(ObjectUI): grbl_jog_grid.setColumnStretch(2, 0) grbl_jog_grid.setColumnStretch(3, 0) grbl_jog_grid.setColumnStretch(4, 1) + grbl_jog_grid.setColumnStretch(5, 0) + grbl_jog_grid.setRowStretch(0, 0) grbl_jog_grid.setRowStretch(1, 0) grbl_jog_grid.setRowStretch(2, 0) grbl_jog_grid.setRowStretch(3, 0) - grbl_jog_grid.setColumnStretch(4, 1) + grbl_ctrl_grid.addLayout(grbl_jog_grid, 8, 0, 1, 3) # JOG Y Up @@ -2270,45 +2273,52 @@ class CNCObjectUI(ObjectUI): grbl_zero_grid.setColumnStretch(0, 0) grbl_zero_grid.setColumnStretch(1, 0) grbl_zero_grid.setColumnStretch(2, 0) - grbl_ctrl_grid.addLayout(grbl_zero_grid, 10, 0, 1, 3) + grbl_jog_grid.addLayout(grbl_zero_grid, 0, 5, 3, 1) # Zero X axis - self.grbl_zerox_button = FCButton(_("ZERO X axes")) + self.grbl_zerox_button = QtWidgets.QToolButton() + self.grbl_zerox_button.setText(_("X")) self.grbl_zerox_button.setToolTip( _("Zero the CNC X axes at current position.") ) grbl_zero_grid.addWidget(self.grbl_zerox_button, 0, 0) # Zero Y axis - self.grbl_zeroy_button = FCButton(_("ZERO Y axes")) + self.grbl_zeroy_button = QtWidgets.QToolButton() + self.grbl_zeroy_button.setText(_("Y")) + self.grbl_zeroy_button.setToolTip( _("Zero the CNC Y axes at current position.") ) - grbl_zero_grid.addWidget(self.grbl_zeroy_button, 0, 1) + grbl_zero_grid.addWidget(self.grbl_zeroy_button, 1, 0) # Zero Z axis - self.grbl_zeroz_button = FCButton(_("ZERO Z axes")) + self.grbl_zeroz_button = QtWidgets.QToolButton() + self.grbl_zeroz_button.setText(_("Z")) + self.grbl_zeroz_button.setToolTip( _("Zero the CNC Z axes at current position.") ) - grbl_zero_grid.addWidget(self.grbl_zeroz_button, 0, 2) + grbl_zero_grid.addWidget(self.grbl_zeroz_button, 2, 0) # Zeroo all axes - self.grbl_zero_all_button = FCButton(_("ZERO all axes")) + self.grbl_zero_all_button = QtWidgets.QToolButton() + self.grbl_zero_all_button.setText(_("All")) + self.grbl_zero_all_button.setToolTip( _("Zero all CNC axes at current position.") ) - grbl_zero_grid.addWidget(self.grbl_zero_all_button, 2, 0, 1, 3) + grbl_zero_grid.addWidget(self.grbl_zero_all_button, 0, 1, 3, 1) # GRBL SENDER grbl_send_grid = QtWidgets.QGridLayout() - grbl_send_grid.setColumnStretch(0, 0) - grbl_send_grid.setColumnStretch(1, 1) - grbl_send_grid.setColumnStretch(2, 0) + grbl_send_grid.setColumnStretch(0, 1) + grbl_send_grid.setColumnStretch(1, 0) self.gr_send_tab_layout.addLayout(grbl_send_grid) - # CUSTOM COMMAND - self.grbl_command_label = FCLabel('%s:' % _("CMD")) + # Send CUSTOM COMMAND + self.grbl_command_label = FCLabel('%s:' % _("Send Command")) self.grbl_command_label.setToolTip( _("Send a custom command to GRBL.") ) + grbl_send_grid.addWidget(self.grbl_command_label, 2, 0, 1, 2) self.grbl_command_entry = FCEntry() @@ -2317,37 +2327,15 @@ class CNCObjectUI(ObjectUI): self.grbl_send_button.setToolTip( _("Send a custom command to GRBL.") ) - grbl_send_grid.addWidget(self.grbl_command_label, 2, 0) - grbl_send_grid.addWidget(self.grbl_command_entry, 2, 1) - grbl_send_grid.addWidget(self.grbl_send_button, 2, 2) - - # GET HEIGHT MAP - self.grbl_get_heightmap_button = FCButton(_("Get Height Map")) - self.grbl_get_heightmap_button.setToolTip( - _("Will send the probing GCode to the GRBL controller\n" - "and wait for the Z probing data.") - ) - grbl_send_grid.addWidget(self.grbl_get_heightmap_button, 4, 0, 1, 3) - - # GET Report - self.grbl_report_button = FCButton(_("Get Report")) - self.grbl_report_button.setToolTip( - _("Print in shell the GRBL report.") - ) - grbl_send_grid.addWidget(self.grbl_report_button, 5, 0, 1, 3) - - grbl_send2_grid = QtWidgets.QGridLayout() - grbl_send2_grid.setColumnStretch(0, 0) - grbl_send2_grid.setColumnStretch(1, 1) - grbl_send2_grid.setColumnStretch(2, 0) - self.gr_send_tab_layout.addLayout(grbl_send2_grid) - self.gr_send_tab_layout.addStretch(1) + grbl_send_grid.addWidget(self.grbl_command_entry, 4, 0) + grbl_send_grid.addWidget(self.grbl_send_button, 4, 1) # Get Parameter - self.grbl_get_param_label = FCLabel('%s:' % _("Parameter")) + self.grbl_get_param_label = FCLabel('%s:' % _("Get Config parameter")) self.grbl_get_param_label.setToolTip( _("A GRBL parameter.") ) + grbl_send_grid.addWidget(self.grbl_get_param_label, 6, 0, 1, 2) self.grbl_parameter_entry = FCEntry() @@ -2356,9 +2344,23 @@ class CNCObjectUI(ObjectUI): self.grbl_get_param_button.setToolTip( _("Get the value of a specified GRBL parameter.") ) - grbl_send2_grid.addWidget(self.grbl_get_param_label, 2, 0) - grbl_send2_grid.addWidget(self.grbl_parameter_entry, 2, 1) - grbl_send2_grid.addWidget(self.grbl_get_param_button, 2, 2) + grbl_send_grid.addWidget(self.grbl_parameter_entry, 8, 0) + grbl_send_grid.addWidget(self.grbl_get_param_button, 8, 1) + + # GET Report + self.grbl_report_button = FCButton(_("Get Report")) + self.grbl_report_button.setToolTip( + _("Print in shell the GRBL report.") + ) + grbl_send_grid.addWidget(self.grbl_report_button, 10, 0, 1, 2) + + # GET HEIGHT MAP + self.grbl_get_heightmap_button = FCButton(_("Get Height Map")) + self.grbl_get_heightmap_button.setToolTip( + _("Will send the probing GCode to the GRBL controller\n" + "and wait for the Z probing data.") + ) + grbl_send_grid.addWidget(self.grbl_get_heightmap_button, 12, 0, 1, 2) self.grbl_frame.hide() # ############################################################################################################# diff --git a/appObjects/FlatCAMCNCJob.py b/appObjects/FlatCAMCNCJob.py index 5a6e3948..d39c2745 100644 --- a/appObjects/FlatCAMCNCJob.py +++ b/appObjects/FlatCAMCNCJob.py @@ -198,6 +198,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): } ''' self.al_geometry_dict = {} + self.solid_geo = None self.grbl_ser_port = None # Attributes to be included in serialization @@ -554,6 +555,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.ui.sal_cb.stateChanged.connect(self.on_autolevelling) self.ui.al_mode_radio.activated_custom.connect(self.on_mode_radio) self.ui.al_controller_combo.currentIndexChanged.connect(self.on_controller_change) + self.ui.voronoi_cb.stateChanged.connect(self.show_voronoi_diagram) # GRBL self.ui.com_search_button.clicked.connect(self.on_search_ports) self.ui.add_bd_button.clicked.connect(self.on_add_baudrate_grbl) @@ -654,8 +656,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): def on_add_al_probepoints(self): # create the solid_geo - solid_geo = [geo['geom'] for geo in self.gcode_parsed if geo['kind'][0] == 'C'] - solid_geo = unary_union(solid_geo) + self.solid_geo = unary_union([geo['geom'] for geo in self.gcode_parsed if geo['kind'][0] == 'C']) # reset al table self.ui.al_probe_points_table.setRowCount(0) @@ -663,7 +664,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): # reset the al dict self.al_geometry_dict.clear() - xmin, ymin, xmax, ymax = solid_geo.bounds + xmin, ymin, xmax, ymax = self.solid_geo.bounds if self.ui.al_mode_radio.get_value() == 'grid': width = abs(xmax - xmin) @@ -684,14 +685,19 @@ class CNCJobObject(FlatCAMObj, CNCjob): points.append((new_x, new_y)) pt_id = 0 + pts_list = [] for point in points: pt_id += 1 + pt = Point(point) + pts_list.append(pt) new_dict = { - 'point': Point(point), + 'point': pt, 'geo': None, 'height': 0.0 } self.al_geometry_dict[pt_id] = deepcopy(new_dict) + self.calculate_voronoi_diagram(pts=pts_list) + else: self.app.inform.emit(_("Click on canvas to add a Test Point...")) @@ -709,13 +715,28 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.mouse_events_connected = True - self.calculate_voronoi_diagram() - self.build_al_table_sig.emit() - def calculate_voronoi_diagram(self): - pass - # return voronoi_diagram() + def show_voronoi_diagram(self, state): + if state: + pass + else: + pass + + def calculate_voronoi_diagram(self, pts): + pts_union = unary_union(pts) + env = self.solid_geo.envelope + try: + voronoi_union = voronoi_diagram(geom=pts_union, envelope=env) + print(voronoi_union) + except Exception as e: + log.debug("CNCJobObject.calculate_voronoi_diagram() --> %s" % str(e)) + return + + for pt_key in list(self.al_geometry_dict.keys()): + for poly in voronoi_union: + if self.al_geometry_dict[pt_key]['point'].within(poly): + self.al_geometry_dict[pt_key]['geo'] = poly # To be called after clicking on the plot. def on_mouse_click_release(self, event): @@ -786,6 +807,11 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.app.inform.emit(_("Finished manual adding of Test Point...")) + pts_list = [] + for k in self.al_geometry_dict: + pts_list.append(self.al_geometry_dict[k]['point']) + self.calculate_voronoi_diagram(pts=pts_list) + # rebuild the al table self.build_al_table_sig.emit() @@ -952,17 +978,6 @@ class CNCJobObject(FlatCAMObj, CNCjob): xonxoff=False, rtscts=False) - self.app.inform.emit("%s: %s" % (_("Port connected"), port_name)) - self.ui.com_connect_button.setStyleSheet("QPushButton {background-color: seagreen;}") - self.ui.com_connect_button.setText(_("Connected")) - - for idx in range(self.ui.al_toolbar.count()): - if self.ui.al_toolbar.tabText(idx) == _("Connect"): - self.ui.al_toolbar.tabBar.setTabTextColor(idx, QtGui.QColor('seagreen')) - if self.ui.al_toolbar.tabText(idx) == _("Control"): - self.ui.al_toolbar.tabBar.setTabEnabled(idx, True) - if self.ui.al_toolbar.tabText(idx) == _("Sender"): - self.ui.al_toolbar.tabBar.setTabEnabled(idx, True) # Toggle DTR to reset the controller loaded with GRBL (Arduino, ESP32, etc) try: self.grbl_ser_port.dtr = False @@ -975,12 +990,36 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.grbl_ser_port.dtr = True except IOError: pass + + answer = self.wake_grbl() + answer = ['ok'] # hack for development without a GRBL controller connected + for line in answer: + if 'ok' in line.lower(): + self.ui.com_connect_button.setStyleSheet("QPushButton {background-color: seagreen;}") + self.ui.com_connect_button.setText(_("Connected")) + self.ui.controller_reset_button.setDisabled(False) + + for idx in range(self.ui.al_toolbar.count()): + if self.ui.al_toolbar.tabText(idx) == _("Connect"): + self.ui.al_toolbar.tabBar.setTabTextColor(idx, QtGui.QColor('seagreen')) + if self.ui.al_toolbar.tabText(idx) == _("Control"): + self.ui.al_toolbar.tabBar.setTabEnabled(idx, True) + if self.ui.al_toolbar.tabText(idx) == _("Sender"): + self.ui.al_toolbar.tabBar.setTabEnabled(idx, True) + + self.app.inform.emit("%s: %s" % (_("Port connected"), port_name)) + return + + self.grbl_ser_port.close() + self.app.inform.emit("[ERROR_NOTCL] %s: %s" % (_("Could not connect to GRBL on port"), port_name)) + except serial.SerialException: self.grbl_ser_port = serial.Serial() self.grbl_ser_port.port = port_name self.grbl_ser_port.close() self.ui.com_connect_button.setStyleSheet("QPushButton {background-color: red;}") self.ui.com_connect_button.setText(_("Disconnected")) + self.ui.controller_reset_button.setDisabled(True) for idx in range(self.ui.al_toolbar.count()): if self.ui.al_toolbar.tabText(idx) == _("Connect"): @@ -1006,12 +1045,16 @@ class CNCJobObject(FlatCAMObj, CNCjob): def wake_grbl(self): # Wake up grbl self.grbl_ser_port.write("\r\n\r\n".encode('utf-8')) - time.sleep(1) # Wait for grbl to initialize - self.grbl_ser_port.flushInput() # Flush startup text in serial input + # Wait for GRBL controller to initialize + time.sleep(1) + + grbl_out = deepcopy(self.grbl_ser_port.readlines()) + self.grbl_ser_port.reset_input_buffer() + + return grbl_out def on_send_grbl_command(self): cmd = self.ui.grbl_command_entry.get_value() - self.wake_grbl() # show the Shell Dock self.app.ui.shell_dock.show() @@ -1023,22 +1066,47 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.app.worker_task.emit({'fcn': worker_task, 'params': []}) def send_grbl_command(self, command, echo=True): - stripped_cmd = command.strip() # Strip all EOL characters for consistency + stripped_cmd = command.strip() + + cmd = stripped_cmd.rpartition('\n')[0] + if echo: + self.app.inform_shell[str, bool].emit(cmd, False) + + # Send Gcode command to GRBL + snd = cmd + '\n' + self.grbl_ser_port.write(snd.encode('utf-8')) + grbl_out = self.grbl_ser_port.readlines() + + result = False + for line in grbl_out: + if echo: + try: + self.app.inform_shell.emit(' : ' + line.decode('utf-8').strip().upper()) + except Exception as e: + log.debug("CNCJobObject.send_grbl_command() --> %s" % str(e)) + if 'ok' in line: + result = True + + return result + + def send_grbl_block(self, command, echo=True): + stripped_cmd = command.strip() for l in stripped_cmd.split('\n'): if echo: self.app.inform_shell[str, bool].emit(l, False) + # Send Gcode block to GRBL snd = l + '\n' - self.grbl_ser_port.write(snd.encode('utf-8')) # Send g-code block to grbl - grbl_out = self.grbl_ser_port.readlines() # Wait for grbl response with carriage return + self.grbl_ser_port.write(snd.encode('utf-8')) + grbl_out = self.grbl_ser_port.readlines() for line in grbl_out: if echo: try: self.app.inform_shell.emit(' : ' + line.decode('utf-8').strip().upper()) except Exception as e: - log.debug("CNCJobObject.send_grbl_command() --> %s" % str(e)) + log.debug("CNCJobObject.send_grbl_block() --> %s" % str(e)) def get_grbl_parameter(self, param): if '$' in param: @@ -1080,6 +1148,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): cmd = '\x18' self.wake_grbl() self.send_grbl_command(command=cmd) + self.app.inform.emit("%s" % _("GRBL software reset was sent.")) def probing_gcode(self, coords, pr_travel, probe_fr, pr_depth, controller): """