Browse Source

- 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

Marius Stanciu 6 years ago
parent
commit
e8faeb2c94
4 changed files with 202 additions and 42 deletions
  1. 2 0
      FlatCAM.py
  2. 5 5
      FlatCAMApp.py
  3. 2 0
      README.md
  4. 193 37
      flatcamTools/ToolRulesCheck.py

+ 2 - 0
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

+ 5 - 5
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):
         """

+ 2 - 0
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
 

+ 193 - 37
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")
-
-    def __getstate__(self):
-        self_dict = self.__dict__.copy()
-        del self_dict['pool']
-        return self_dict
+
+        log.debug("RuleCheck() executing")
+
+        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()))