petal-qc 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of petal-qc might be problematic. Click here for more details.

Files changed (56) hide show
  1. petal_qc-0.0.0/PKG-INFO +29 -0
  2. petal_qc-0.0.0/README.md +6 -0
  3. petal_qc-0.0.0/petal_qc/BTreport/CheckBTtests.py +321 -0
  4. petal_qc-0.0.0/petal_qc/BTreport/__init__.py +0 -0
  5. petal_qc-0.0.0/petal_qc/BTreport/bustapeReport.py +144 -0
  6. petal_qc-0.0.0/petal_qc/__init__.py +14 -0
  7. petal_qc-0.0.0/petal_qc/metrology/Cluster.py +90 -0
  8. petal_qc-0.0.0/petal_qc/metrology/DataFile.py +47 -0
  9. petal_qc-0.0.0/petal_qc/metrology/PetalMetrology.py +327 -0
  10. petal_qc-0.0.0/petal_qc/metrology/__init__.py +0 -0
  11. petal_qc-0.0.0/petal_qc/metrology/all2csv.py +57 -0
  12. petal_qc-0.0.0/petal_qc/metrology/analyze_locking_points.py +597 -0
  13. petal_qc-0.0.0/petal_qc/metrology/cold_noise.py +106 -0
  14. petal_qc-0.0.0/petal_qc/metrology/comparisonTable.py +59 -0
  15. petal_qc-0.0.0/petal_qc/metrology/convert_mitutoyo.py +175 -0
  16. petal_qc-0.0.0/petal_qc/metrology/convert_smartscope.py +145 -0
  17. petal_qc-0.0.0/petal_qc/metrology/coreMetrology.py +402 -0
  18. petal_qc-0.0.0/petal_qc/metrology/data2csv.py +63 -0
  19. petal_qc-0.0.0/petal_qc/metrology/do_Metrology.py +117 -0
  20. petal_qc-0.0.0/petal_qc/metrology/flatness4nigel.py +157 -0
  21. petal_qc-0.0.0/petal_qc/metrology/gtkutils.py +120 -0
  22. petal_qc-0.0.0/petal_qc/metrology/petal_flatness.py +353 -0
  23. petal_qc-0.0.0/petal_qc/metrology/show_data_file.py +118 -0
  24. petal_qc-0.0.0/petal_qc/metrology/testSummary.py +37 -0
  25. petal_qc-0.0.0/petal_qc/metrology/test_paralelism.py +71 -0
  26. petal_qc-0.0.0/petal_qc/thermal/CSVImage.py +69 -0
  27. petal_qc-0.0.0/petal_qc/thermal/DebugPlot.py +76 -0
  28. petal_qc-0.0.0/petal_qc/thermal/IRBFile.py +768 -0
  29. petal_qc-0.0.0/petal_qc/thermal/IRCore.py +110 -0
  30. petal_qc-0.0.0/petal_qc/thermal/IRDataGetter.py +359 -0
  31. petal_qc-0.0.0/petal_qc/thermal/IRPetal.py +1338 -0
  32. petal_qc-0.0.0/petal_qc/thermal/IRPetalParam.py +71 -0
  33. petal_qc-0.0.0/petal_qc/thermal/PetalColorMaps.py +62 -0
  34. petal_qc-0.0.0/petal_qc/thermal/Petal_IR_Analysis.py +142 -0
  35. petal_qc-0.0.0/petal_qc/thermal/PipeFit.py +598 -0
  36. petal_qc-0.0.0/petal_qc/thermal/__init__.py +0 -0
  37. petal_qc-0.0.0/petal_qc/thermal/analyze_IRCore.py +417 -0
  38. petal_qc-0.0.0/petal_qc/thermal/contours.py +378 -0
  39. petal_qc-0.0.0/petal_qc/thermal/create_IRCore.py +185 -0
  40. petal_qc-0.0.0/petal_qc/thermal/pipe_read.py +182 -0
  41. petal_qc-0.0.0/petal_qc/thermal/show_IR_petal.py +420 -0
  42. petal_qc-0.0.0/petal_qc/utils/Geometry.py +756 -0
  43. petal_qc-0.0.0/petal_qc/utils/Progress.py +182 -0
  44. petal_qc-0.0.0/petal_qc/utils/__init__.py +0 -0
  45. petal_qc-0.0.0/petal_qc/utils/all_files.py +35 -0
  46. petal_qc-0.0.0/petal_qc/utils/docx_utils.py +186 -0
  47. petal_qc-0.0.0/petal_qc/utils/fit_utils.py +188 -0
  48. petal_qc-0.0.0/petal_qc/utils/utils.py +180 -0
  49. petal_qc-0.0.0/petal_qc.egg-info/PKG-INFO +29 -0
  50. petal_qc-0.0.0/petal_qc.egg-info/SOURCES.txt +54 -0
  51. petal_qc-0.0.0/petal_qc.egg-info/dependency_links.txt +1 -0
  52. petal_qc-0.0.0/petal_qc.egg-info/entry_points.txt +3 -0
  53. petal_qc-0.0.0/petal_qc.egg-info/requires.txt +11 -0
  54. petal_qc-0.0.0/petal_qc.egg-info/top_level.txt +1 -0
  55. petal_qc-0.0.0/pyproject.toml +44 -0
  56. petal_qc-0.0.0/setup.cfg +4 -0
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.1
2
+ Name: petal_qc
3
+ Version: 0.0.0
4
+ Summary: A collection of scripts for Petal CORE QC.
5
+ Author-email: Carlos Lacasta <carlos.lacasta@cern.ch>
6
+ Project-URL: Homepage, https://gitlab.cern.ch/atlas-itk/sw/db/itk-pdb-gtk-gui-utils
7
+ Project-URL: Bug Tracker, https://gitlab.cern.ch/atlas-itk/sw/db/itk-pdb-gtk-gui-utils/-/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: itkdb
13
+ Requires-Dist: itkdb_gtk
14
+ Requires-Dist: numpy
15
+ Requires-Dist: matplotlib
16
+ Requires-Dist: lmfit
17
+ Requires-Dist: openpyxl
18
+ Requires-Dist: pandas
19
+ Requires-Dist: python_dateutil
20
+ Requires-Dist: python_docx
21
+ Requires-Dist: scipy
22
+ Requires-Dist: scikit-image
23
+
24
+ # Petal QC
25
+
26
+ The folder contains a collections of scripts to make the analysis of the Petal
27
+ QC metrology and thermal tests.
28
+
29
+ They will also upload the tests to the DB if required.
@@ -0,0 +1,6 @@
1
+ # Petal QC
2
+
3
+ The folder contains a collections of scripts to make the analysis of the Petal
4
+ QC metrology and thermal tests.
5
+
6
+ They will also upload the tests to the DB if required.
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env python3
2
+ """Check that bus tapes in petal have been tested.
3
+
4
+ Upload teh Bus Tape Summary TEst to the PDB.
5
+
6
+ To run it
7
+
8
+ python3 CheckBTtests.py petal_SN
9
+
10
+ The main routine is BTreport and can be called from anywhere else.
11
+
12
+ """
13
+
14
+ import sys
15
+ import traceback
16
+ import json
17
+ import getpass
18
+ import datetime
19
+ import dateutil.parser
20
+ import itkdb
21
+ from itkdb_gtk import ITkDBlogin
22
+
23
+ def complain(main_msg, secondary_msg=None):
24
+ """Prints an error message
25
+
26
+ Args:
27
+ -----
28
+ main (): Main message
29
+ secondary (): Seconday message
30
+ """
31
+ print("**Error\n{}".format(main_msg))
32
+ if secondary_msg:
33
+ msg = secondary_msg.replace("\n", "\n\t")
34
+ print("\t{}".format(msg))
35
+
36
+
37
+ def find_petal(session, SerialN, silent=False, complain_func=complain):
38
+ """Find petal with given SerialN.
39
+
40
+ Args:
41
+ -----
42
+ session (itkdb.Client): The DB session
43
+ SerialN (str): The petal SerialNumber
44
+ silent (bool, optional): No compllaint writen. Defaults to False.
45
+
46
+ Returns
47
+ -------
48
+ dict: A petal (JSon)
49
+
50
+ Raises
51
+ ------
52
+ LookupError if petal not found
53
+ TypeError if SerialN is not a CORE_AVS
54
+ """
55
+ try:
56
+ petal_core = session.get('getComponent', json={'component': SerialN})
57
+
58
+ except Exception as ex:
59
+ if not silent:
60
+ complain_func("Could not find Petal Core in DB", str(ex))
61
+
62
+ raise LookupError("Could not find Petal core with SN {}".format(SerialN)) from ex
63
+
64
+ if petal_core["type"]["code"] != "CORE_AVS":
65
+ if not silent:
66
+ complain_func("Wrong component type", "This is not an AVS petal core")
67
+
68
+ raise TypeError("This is not an AVS petal core")
69
+
70
+ return petal_core
71
+
72
+
73
+ def get_type(child):
74
+ """Return object type
75
+
76
+ Args:
77
+ -----
78
+ child: object
79
+
80
+ Returns
81
+ -------
82
+ str: object type
83
+
84
+ """
85
+ if child["type"] is not None:
86
+ comp_type = child["type"]["code"]
87
+
88
+ else:
89
+ comp_type = child["componentType"]["code"]
90
+
91
+ return comp_type
92
+
93
+
94
+ def find_bus_tapes(session, petal, complain_func=complain):
95
+ """Find Bus tapes in petal.
96
+
97
+ Args:
98
+ ----
99
+ session (itkdb.Client): The DB session
100
+ petal (Json): The petal object
101
+
102
+ Returns
103
+ -------
104
+ dict: a dict with the bustapes. Key is the BT type.
105
+ """
106
+ bt_list = {}
107
+ for child in petal["children"]:
108
+ cstage = "Missing"
109
+ if child["component"] is None:
110
+ continue
111
+
112
+ else:
113
+ child_sn = child["component"]["serialNumber"]
114
+ comp_type = get_type(child)
115
+ if "BT_PETAL" not in comp_type:
116
+ continue
117
+
118
+ # We are left with the bus tapes
119
+ cobj = session.get('getComponent', json={'component': child["component"]["id"]})
120
+ cstage = cobj["currentStage"]['code']
121
+ if cstage != "COMPLETED":
122
+ complain_func("Bus tape not in final stages", cstage)
123
+ continue
124
+
125
+ bt_list[comp_type] = cobj, child['id']
126
+
127
+ return bt_list
128
+
129
+
130
+ def find_but_tape_tests(session, petal_date, bt_sn, complain_func=complain):
131
+ """Find tests in bus tape.
132
+
133
+ Args:
134
+ session (itkdb.Client): The DB session
135
+ petal_date (DateTime): The petal date
136
+ bt_sn (str): The bus tape SN
137
+
138
+ Returns:
139
+ dict: dict with all tests. key is the test type code.
140
+
141
+ """
142
+ test_list = session.get("listTestRunsByComponent", json={"component": bt_sn, "stage": "COMPLETED"})
143
+ bt_tests = {}
144
+ for tst in test_list:
145
+ test_type = tst["testType"]["name"]
146
+ test_code = tst["testType"]["code"]
147
+
148
+ # Create the storage for the tests. Used to sort by date in case of multiple tests
149
+ if test_code not in bt_tests:
150
+ bt_tests[test_code] = []
151
+
152
+ test_date = tst["date"]
153
+ bt_tests[test_code].append(tst)
154
+
155
+ for key, val in bt_tests.items():
156
+ val.sort(key=lambda a: dateutil.parser.parse(a["date"]), reverse=True)
157
+ the_test = val[0]
158
+ if petal_date > dateutil.parser.parse(val[0]["date"]):
159
+ complain_func("Test on tape happened before petal", "")
160
+ bt_tests[key] = None
161
+ else:
162
+ bt_tests[key] = val[0]
163
+
164
+ return bt_tests
165
+
166
+
167
+ def date2string(the_date=None):
168
+ """REturns date string in TTkDB format."""
169
+ if the_date is None:
170
+ the_date = datetime.datetime.now()
171
+ out = the_date.isoformat(timespec='milliseconds')
172
+ if out[-1] not in ['zZ']:
173
+ out += 'Z'
174
+
175
+ return out
176
+
177
+
178
+ def BTreport(session, SerialN, complain_func=complain):
179
+ """Makes the BTreport for a petal core.
180
+
181
+ Args:
182
+ session (itkdb.Client): The DB session
183
+ SerialN (str): The Petal core SB
184
+ """
185
+ # get petal frm DB.
186
+ try:
187
+ petal = find_petal(session, SerialN, complain_func=complain_func)
188
+
189
+ except Exception as ex:
190
+ print(str(ex))
191
+ return None
192
+
193
+ print("+++ Petal core {} [{}]".format(SerialN, petal["alternativeIdentifier"]))
194
+ petal_date = dateutil.parser.parse(petal["stateTs"])
195
+ comp_type = get_type(petal)
196
+ if comp_type != "CORE_AVS":
197
+ complain_func("This is not a petal cores", comp_type)
198
+ return None
199
+
200
+ # Check that the petal core is in the proper stage.
201
+ stage = petal["currentStage"]['code']
202
+ if stage != "AT_QC_SITE":
203
+ complain_func("Petal core is not at QC_SITE", stage)
204
+ return None
205
+
206
+ # Check children
207
+ if "children" not in petal:
208
+ complain_func("{}[{}]".format(SerialN, id), "Not assembled")
209
+ return None
210
+
211
+ # Loop on children an find bustapes
212
+ bt_list = find_bus_tapes(session, petal, complain_func=complain_func)
213
+ if len(bt_list) == 0:
214
+ complain_func("no valid bustape found", "Either not assembled or in incorrect stage.")
215
+ return None
216
+
217
+ out = {
218
+ "component": SerialN,
219
+ "testType": "BTTESTING",
220
+ "institution": petal["currentLocation"]["code"],
221
+ "runNumber": "1",
222
+ "date": date2string(),
223
+ "passed": True,
224
+ "problems": False,
225
+ "results": {
226
+ "PASS": [],
227
+ "RUNS_ELECTRICALTEST": []
228
+ # "RUNS_STRETCHTEST": []
229
+ }
230
+ }
231
+
232
+ # Check te tests in the bustapes
233
+ ngood = 0
234
+ ntrouble = 0
235
+ for bt, cp_id in bt_list.values():
236
+ bt_sn = bt["serialNumber"]
237
+ print("bus tape {}".format(bt_sn))
238
+
239
+ bt_ngood = 0
240
+ bt_nprob = 0
241
+ # get list of tests and select the latest.
242
+ bt_tests = find_but_tape_tests(session, petal_date, bt_sn, complain_func=complain_func)
243
+
244
+ results = {}
245
+ childId = {}
246
+ for key, the_test in bt_tests.items():
247
+ print("\t->The Test {} [{}] {}".format(the_test["testType"]["name"],
248
+ the_test["date"][0:16],
249
+ "PASSED" if the_test["passed"] else "FAILED"))
250
+
251
+ results[the_test["testType"]["code"]] = the_test["passed"]
252
+ childId[the_test["testType"]["code"]] = the_test['id']
253
+ if the_test["passed"]:
254
+ ngood += 1
255
+ bt_ngood += 1
256
+
257
+ if the_test["problems"]:
258
+ ntrouble += 1
259
+ bt_nprob += 1
260
+
261
+ out["results"]["PASS"].append(
262
+ {"value": (bt_ngood == 2),
263
+ "childParentRelation": cp_id})
264
+
265
+ out["results"]["RUNS_ELECTRICALTEST"].append(
266
+ {"value": childId["BTELECTRICAL"],
267
+ "childParentRelation": cp_id})
268
+
269
+ # out["results"]["RUNS_STRETCHTEST"].append(
270
+ # {"value": childId["BTSTRETCHP"],
271
+ # "childParentRelation": cp_id})
272
+
273
+ out["passed"] = (ngood == 4)
274
+ out["problems"] = (ntrouble > 0)
275
+ print("BusTape Report\n\tN. good {}\n\tN. prob {}".format(ngood, ntrouble))
276
+
277
+ return out
278
+
279
+ def main():
280
+ """Main entry."""
281
+ try:
282
+ SN = sys.argv[1]
283
+ except IndexError:
284
+ print("I need a petal core SN")
285
+ sys.exit()
286
+
287
+ # ITk PDB authentication
288
+ dlg = None
289
+ try:
290
+ # We use here the Gtk GUI
291
+ dlg = ITkDBlogin.ITkDBlogin()
292
+ client = dlg.get_client()
293
+
294
+ except Exception:
295
+ # Login with "standard" if the above fails.
296
+ client = itkdb.Client()
297
+ client.user._access_code1 = getpass.getpass("Access 1: ")
298
+ client.user._access_code2 = getpass.getpass("Access 2: ")
299
+ client.user.authenticate()
300
+ print("Hello {} !".format(client.user.name))
301
+
302
+ # Check the Bustape tests
303
+ try:
304
+ out = BTreport(client, SN)
305
+ # Upload test
306
+ if out:
307
+ db_response = client.post("uploadTestRunResults", json=out)
308
+ print(json.dumps(db_response, indent=3))
309
+
310
+
311
+ except Exception:
312
+ print(traceback.format_exc())
313
+
314
+ try:
315
+ dlg.die()
316
+
317
+ except Exception:
318
+ print("Bye !")
319
+
320
+ if __name__ == "__main__":
321
+ main()
File without changes
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env python3
2
+ from contextlib import redirect_stdout
3
+
4
+ from itkdb_gtk import ITkDButils
5
+ from itkdb_gtk import dbGtkUtils
6
+ from itkdb_gtk import ITkDBlogin
7
+ import itkdb
8
+
9
+ from petal_qc.BTreport import CheckBTtests
10
+
11
+ import gi
12
+ gi.require_version("Gtk", "3.0")
13
+ from gi.repository import Gtk, Gio
14
+
15
+
16
+ class BusTapeReport(dbGtkUtils.ITkDBWindow):
17
+ """Makes a report of bustapes."""
18
+
19
+ def __init__(self, session=None, title="", panel_size=100):
20
+ """Initialization.
21
+
22
+ Args:
23
+ title: The title of the window.
24
+ pannel_size: size of message panel.
25
+
26
+ """
27
+ super().__init__(session=session, title=title, show_search="SEarch for Petal CORE")
28
+ self.petal_SN = None
29
+ self.alternativeID = None
30
+ self.outDB = None
31
+
32
+
33
+ # Active button in header
34
+ button = Gtk.Button()
35
+ icon = Gio.ThemedIcon(name="document-send-symbolic")
36
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
37
+ button.add(image)
38
+ button.set_tooltip_text("Click to upload test")
39
+ button.connect("clicked", self.upload_test_gui)
40
+ self.hb.pack_end(button)
41
+
42
+ # JScon edit
43
+ button = Gtk.Button()
44
+ icon = Gio.ThemedIcon(name="accessories-text-editor-symbolic")
45
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
46
+ button.add(image)
47
+ button.set_tooltip_text("Click to see the test data")
48
+ button.connect("clicked", self.show_test_gui)
49
+ self.hb.pack_end(button)
50
+
51
+ # The Serial number
52
+ self.SN = dbGtkUtils.TextEntry()
53
+ self.SN.connect("text-changed", self.on_SN_changed)
54
+
55
+ # Put the 3 objects in a Grid
56
+ grid = Gtk.Grid(column_spacing=5, row_spacing=5)
57
+ self.mainBox.pack_start(grid, False, True, 0)
58
+
59
+ grid.attach(Gtk.Label(label="Serial No."), 0, 0, 1, 1)
60
+ grid.attach(self.SN.entry, 1, 0, 1, 1)
61
+
62
+
63
+ self.mainBox.pack_start(self.message_panel.frame, True, True, 0)
64
+
65
+
66
+ def quit(self, *args):
67
+ """Quits the application."""
68
+ self.hide()
69
+ self.destroy()
70
+
71
+ def on_SN_changed(self, entry, value):
72
+ """New SN given. Ask in PDB,"""
73
+ if len(value) <= 0:
74
+ return None
75
+
76
+
77
+ obj = ITkDButils.get_DB_component(self.session, value)
78
+ if obj is not None:
79
+ entry.set_text(obj["serialNumber"])
80
+ self.alternativeID = obj["alternativeIdentifier"]
81
+
82
+ else:
83
+ dbGtkUtils.complain("Invalid SN", value)
84
+
85
+ def query_db(self, *args):
86
+ """Search petal and bustapes."""
87
+ SN = self.SN.get_text()
88
+ if SN is None or len(SN)==0:
89
+ dbGtkUtils.complain("Invalid Serial Number", "Wrong value: {}".format(SN))
90
+ return
91
+
92
+ self.outDB = CheckBTtests.BTreport(self.session, SN, complain_func=dbGtkUtils.complain)
93
+
94
+ if self.outDB is None:
95
+ dbGtkUtils.complain("Could not get he report.")
96
+
97
+ def show_test_gui(self, *args):
98
+ """Show test data."""
99
+ if self.outDB is None:
100
+ return
101
+
102
+ values, rc = dbGtkUtils.DictDialog.create_json_data_editor(self.outDB)
103
+ if rc == Gtk.ResponseType.OK:
104
+ self.outDB = values
105
+
106
+
107
+ def upload_test_gui(self, *args):
108
+ """Uploads test and attachments."""
109
+ pass
110
+
111
+ def main():
112
+ # ITk PDB authentication
113
+ dlg = None
114
+ try:
115
+ # We use here the Gtk GUI
116
+ dlg = ITkDBlogin.ITkDBlogin()
117
+ client = dlg.get_client()
118
+
119
+ except Exception:
120
+ # Login with "standard" if the above fails.
121
+ client = itkdb.Client()
122
+ client.user._access_code1 = getpass.getpass("Access 1: ")
123
+ client.user._access_code2 = getpass.getpass("Access 2: ")
124
+ client.user.authenticate()
125
+ print("Hello {} !".format(client.user.name))
126
+
127
+ BT = BusTapeReport(client, title="Bustape Report")
128
+ BT.write_message("Welcome !!\n")
129
+ BT.connect("destroy", Gtk.main_quit)
130
+ BT.show_all()
131
+
132
+ try:
133
+ Gtk.main()
134
+ except KeyboardInterrupt:
135
+ print("Arrrgggg!!!")
136
+
137
+ try:
138
+ dlg.die()
139
+
140
+ except Exception:
141
+ print("Bye !")
142
+
143
+ if __name__ == "__main__":
144
+ main()
@@ -0,0 +1,14 @@
1
+ """petal_qc python module."""
2
+ __version__ = "0.0.0"
3
+
4
+
5
+ def coreMetrology():
6
+ """Launches the Core metrology analysis ahd PDB script."""
7
+ from .metrology.coreMetrology import main
8
+ main()
9
+
10
+ def bustapeReport():
11
+ """Launches the Core metrology analysis ahd PDB script."""
12
+ from .BTreport.bustapeReport import main
13
+ main()
14
+
@@ -0,0 +1,90 @@
1
+ """Find point clisters."""
2
+ import math
3
+
4
+ import numpy as np
5
+
6
+
7
+ class Cluster(object):
8
+ """A cluster."""
9
+
10
+ def __init__(self, x=0, y=0, z=0):
11
+ """Initialize the cluster."""
12
+ self.x = x
13
+ self.y = y
14
+ self.z = z
15
+ self.N = 0.0
16
+ self.points = []
17
+ self.xtra = []
18
+
19
+ def add(self, P, xtra=None):
20
+ """Add a new point."""
21
+ N = self.N + 1.0
22
+ self.x = (self.x * self.N + P[0])/N
23
+ self.y = (self.y * self.N + P[1])/N
24
+ try:
25
+ self.z = (self.z * self.N + P[2])/N
26
+
27
+ except IndexError:
28
+ pass
29
+
30
+ self.N += 1.0
31
+ self.points.append(P)
32
+ self.xtra.append(xtra)
33
+
34
+ def get_points(self):
35
+ """Return the array of points."""
36
+ return np.array(self.points)
37
+
38
+ def distance(self, P):
39
+ """Compute distance to Point."""
40
+ dist = math.sqrt((self.x-P[0])**2 + (self.y-P[1])**2)
41
+ return dist
42
+
43
+ def __lt__(self, other):
44
+ """Sort two clusters.
45
+
46
+ A cluster is smaller if has smaller Y.
47
+ """
48
+ return self.y < other.y
49
+
50
+
51
+ def cluster_points(points, distance):
52
+ """Cluster Points.
53
+
54
+ Group the points in clusters formed by points whose
55
+ distance is smaller than distance
56
+
57
+ Args:
58
+ ----
59
+ points (list): list of points in the for [x, y, z] or equivalent
60
+ distance (float): distance.
61
+
62
+ Returns
63
+ -------
64
+ list: List of Clusters
65
+
66
+ """
67
+ # Cluster points which are closer than distance.
68
+ clusters = []
69
+ for ipoint, P in enumerate(points):
70
+ if np.isnan(P[0]) or np.isnan(P[1]):
71
+ continue
72
+
73
+ dist_min = 1e9
74
+ clst_min = -1
75
+ for iclst, C in enumerate(clusters):
76
+ dist = C.distance(P)
77
+ if dist < dist_min:
78
+ clst_min = iclst
79
+ dist_min = dist
80
+
81
+ if clst_min < 0 or dist_min > distance:
82
+ C = Cluster()
83
+ C.add(P, ipoint)
84
+ clusters.append(C)
85
+
86
+ else:
87
+ clusters[clst_min].add(P, ipoint)
88
+
89
+ clusters.sort(key=lambda x: x.N, reverse=True)
90
+ return clusters
@@ -0,0 +1,47 @@
1
+ """Gets the Metrology data from a file."""
2
+ from tempfile import NamedTemporaryFile
3
+
4
+ import numpy as np
5
+
6
+ from .convert_mitutoyo import mitutoyo2cvs
7
+ from .convert_smartscope import read_smartscope
8
+
9
+
10
+ def read(fname, label='\\w+', type="Punto", keep=False):
11
+ """Read a file.
12
+
13
+ Args:
14
+ ----
15
+ fname: The file name
16
+ label: the label for an mitutyyo file
17
+ type: the entry type in a mitutoyo file
18
+ keep: if true keeps the label
19
+ """
20
+ try:
21
+ data = np.loadtxt(fname, unpack=False, skiprows=1, delimiter=',')
22
+
23
+ except FileNotFoundError:
24
+ print("Input file not found.")
25
+ return None
26
+
27
+ except Exception:
28
+ # This might be CMM file
29
+ is_mitutoyo = False
30
+ with open(fname, 'r', encoding='cp1252') as fin:
31
+ for line in fin:
32
+ if 'Elemento' in line:
33
+ is_mitutoyo = True
34
+ break
35
+ elif 'Quality Vision' in line:
36
+ is_mitutoyo = False
37
+ break
38
+
39
+ ofile = NamedTemporaryFile()
40
+ if is_mitutoyo:
41
+ mitutoyo2cvs([fname], ofile.name, label=label, data_type=type, keep=keep)
42
+ else:
43
+ read_smartscope(fname, ofile.name, label, keep=keep)
44
+
45
+ data = np.loadtxt(ofile.name, unpack=False, skiprows=1, delimiter=',')
46
+
47
+ return data