diff --git a/FlatCAM.py b/FlatCAM.py index b44643a2..191d5fbe 100644 --- a/FlatCAM.py +++ b/FlatCAM.py @@ -7,6 +7,8 @@ from FlatCAMApp import App from flatcamGUI import VisPyPatches from multiprocessing import freeze_support +# import copyreg +# import types if sys.platform == "win32": # cx_freeze 'module win32' workaround diff --git a/FlatCAMApp.py b/FlatCAMApp.py index a446cedd..440f3909 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2778,11 +2778,11 @@ class App(QtCore.QObject): # recognize therefore we must quit but take into consideration the app reboot from within, in that case # the args_to_process will contain the path to the FlatCAM.exe (cx_freezed executable) - for arg in args_to_process: - if 'FlatCAM.exe' in arg: - continue - else: - sys.exit(2) + # for arg in args_to_process: + # if 'FlatCAM.exe' in arg: + # continue + # else: + # sys.exit(2) def set_ui_title(self, name): """ diff --git a/README.md b/README.md index 1a90d4d3..8b231413 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ CAD program, and create G-Code for Isolation routing. - in Optimal Tool added display of how frequent that minimum distance is found - in Tool Distance and Tool Minimal Distance made the entry fields read-only - in Optimal Tool added the display of the locations where the minimum distance was detected +- added support to use Multi Processing (multi core usage, not simple threading) in Rules Check Tool +- in Rules Check Tool added the functionality for the following rules: Hole Size, Trace Size, Hole to Hole Clerance 29.09.2019 diff --git a/flatcamTools/ToolRulesCheck.py b/flatcamTools/ToolRulesCheck.py index ec5470da..2314b878 100644 --- a/flatcamTools/ToolRulesCheck.py +++ b/flatcamTools/ToolRulesCheck.py @@ -12,7 +12,8 @@ from ObjectCollection import * import time from FlatCAMPool import * from os import getpid -import copyreg, types, sys +from shapely.ops import nearest_points +from shapely.geometry.base import BaseGeometry import gettext import FlatCAMTranslation as fcTranslate @@ -23,15 +24,6 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -def _pickle_method(m): - class_self = m.im_class if m.im_self is None else m.im_self - print(getattr, (class_self, m.im_func.func_name)) - return getattr, (class_self, m.im_func.func_name) - - -copyreg.pickle(types.MethodType, _pickle_method) - - class RulesCheck(FlatCAMTool): toolName = _("Check Rules") @@ -443,7 +435,7 @@ class RulesCheck(FlatCAMTool): self.form_layout_1.addRow(QtWidgets.QLabel("")) - # Drill2Drill clearance + # Hole2Hole clearance self.clearance_d2d_cb = FCCheckBox('%s:' % _("Hole to Hole Clearance")) self.clearance_d2d_cb.setToolTip( _("This checks if the minimum clearance between a drill hole\n" @@ -451,7 +443,7 @@ class RulesCheck(FlatCAMTool): ) self.form_layout_1.addRow(self.clearance_d2d_cb) - # Drill2Drill clearance value + # Hole2Hole clearance value self.clearance_d2d_entry = FCEntry() self.clearance_d2d_lbl = QtWidgets.QLabel('%s:' % _("Min value")) self.clearance_d2d_lbl.setToolTip( @@ -527,6 +519,10 @@ class RulesCheck(FlatCAMTool): # flag to signal the constrain was activated self.constrain_flag = False + # Multiprocessing Process Pool + self.pool = Pool(processes=cpu_count()) + self.results = None + # def on_object_loaded(self, index, row): # print(index.internalPointer().child_items[row].obj.options['name'], index.data()) @@ -598,34 +594,194 @@ class RulesCheck(FlatCAMTool): self.e2_object.setDisabled(True) self.reset_fields() - def foo(self, bar, baz): - print("start", getpid()) - bar = bar ** 2 - print(bar, getpid()) - print("end", getpid()) - return bar, baz + @staticmethod + def check_holes_size(elements, size): + rule = _("Hole Size") + + violations = list() + obj_violations = dict() + obj_violations.update({ + 'name': '', + 'dia': list() + }) + + for elem in elements: + dia_list = [] + + name = elem['name'] + for tool in elem['tools']: + tool_dia = float(elem['tools'][tool]['C']) + if tool_dia < float(size): + dia_list.append(tool_dia) + obj_violations['name'] = name + obj_violations['dia'] = dia_list + violations.append(deepcopy(obj_violations)) + + return rule, violations + + @staticmethod + def check_holes_clearance(elements, size): + rule = _("Hole to Hole Clearance") + + violations = list() + obj_violations = dict() + obj_violations.update({ + 'name': '', + 'points': list() + }) + + total_geo = list() + for elem in elements: + for tool in elem['tools']: + if 'solid_geometry' in elem['tools'][tool]: + geometry = elem['tools'][tool]['solid_geometry'] + for geo in geometry: + total_geo.append(geo) + + min_dict = dict() + idx = 1 + for geo in total_geo: + for s_geo in total_geo[idx:]: + + # minimize the number of distances by not taking into considerations those that are too small + dist = geo.distance(s_geo) + loc_1, loc_2 = nearest_points(geo, s_geo) + + dx = loc_1.x - loc_2.x + dy = loc_1.y - loc_2.y + loc = min(loc_1.x, loc_2.x) + (abs(dx) / 2), min(loc_1.y, loc_2.y) + (abs(dy) / 2) + + if dist in min_dict: + min_dict[dist].append(loc) + else: + min_dict[dist] = [loc] + idx += 1 + + points_list = list() + for dist in min_dict.keys(): + if float(dist) < size: + for location in min_dict[dist]: + points_list.append(location) + + name_list = list() + for elem in elements: + name_list.append(elem['name']) + + obj_violations['name'] = name_list + obj_violations['points'] = points_list + violations.append(deepcopy(obj_violations)) + + return rule, violations + + @staticmethod + def check_traces_size(elements, size): + rule = _("Trace Size") + + violations = list() + obj_violations = dict() + obj_violations.update({ + 'name': '', + 'size': list(), + 'points': list() + }) + + for elem in elements: + dia_list = [] + points_list = [] + name = elem['name'] + for apid in elem['apertures']: + tool_dia = float(elem['apertures'][apid]['size']) + if tool_dia < float(size): + dia_list.append(tool_dia) + for geo_el in elem['apertures'][apid]['geometry']: + if 'solid' in geo_el.keys(): + geo = geo_el['solid'] + pt = geo.representative_point() + points_list.append((pt.x, pt.y)) + + obj_violations['name'] = name + obj_violations['size'] = dia_list + obj_violations['points'] = points_list + violations.append(deepcopy(obj_violations)) + + return rule, violations def execute(self): - log.debug("started") - self.pool = Pool() - log.debug("executing") self.results = list() - i = 50 - while i < 100: - j = i + 1 - while j < 150: - self.results.append(self.pool.apply_async(self.foo, args=(i, j))) - j = j + 1 - i = i + 1 - output = [p.get() for p in self.results] - print(output) - log.debug("finished") + log.debug("RuleCheck() executing") - def __getstate__(self): - self_dict = self.__dict__.copy() - del self_dict['pool'] - return self_dict + def worker_job(app_obj): + proc = self.app.proc_container.new(_("Working...")) + + # RULE: Check Trace Size + if self.trace_size_cb.get_value(): + copper_list = list() + copper_name_1 = self.copper_t_object.currentText() + if copper_name_1 is not '' and self.copper_t_cb.get_value(): + elem_dict = dict() + elem_dict['name'] = deepcopy(copper_name_1) + elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_1).apertures) + copper_list.append(elem_dict) + + copper_name_2 = self.copper_b_object.currentText() + if copper_name_2 is not '' and self.copper_b_cb.get_value(): + elem_dict = dict() + elem_dict['name'] = deepcopy(copper_name_2) + elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_2).apertures) + copper_list.append(elem_dict) + + trace_size = float(self.trace_size_entry.get_value()) + self.results.append(self.pool.apply_async(self.check_traces_size, args=(copper_list, trace_size))) + + # RULE: Check Hole to Hole Clearance + if self.clearance_d2d_cb.get_value(): + exc_list = list() + exc_name_1 = self.e1_object.currentText() + if exc_name_1 is not '' and self.e1_cb.get_value(): + elem_dict = dict() + elem_dict['name'] = deepcopy(exc_name_1) + elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools) + exc_list.append(elem_dict) + + exc_name_2 = self.e2_object.currentText() + if exc_name_2 is not '' and self.e2_cb.get_value(): + elem_dict = dict() + elem_dict['name'] = deepcopy(exc_name_2) + elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools) + exc_list.append(elem_dict) + + hole_clearance = float(self.clearance_d2d_entry.get_value()) + self.results.append(self.pool.apply_async(self.check_holes_clearance, args=(exc_list, hole_clearance))) + + # RULE: Check Holes Size + if self.drill_size_cb.get_value(): + exc_list = list() + exc_name_1 = self.e1_object.currentText() + if exc_name_1 is not '' and self.e1_cb.get_value(): + elem_dict = dict() + elem_dict['name'] = deepcopy(exc_name_1) + elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools) + exc_list.append(elem_dict) + + exc_name_2 = self.e2_object.currentText() + if exc_name_2 is not '' and self.e2_cb.get_value(): + elem_dict = dict() + elem_dict['name'] = deepcopy(exc_name_2) + elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools) + exc_list.append(elem_dict) + + drill_size = float(self.drill_size_entry.get_value()) + self.results.append(self.pool.apply_async(self.check_holes_size, args=(exc_list, drill_size))) + + output = list() + for p in self.results: + output.append(p.get()) + + print(output) + log.debug("RuleCheck() finished") + + self.app.worker_task.emit({'fcn': worker_job, 'params': [self.app]}) def reset_fields(self): # self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))