petal-qc 0.0.0__py3-none-any.whl
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.
- petal_qc/BTreport/CheckBTtests.py +321 -0
- petal_qc/BTreport/__init__.py +0 -0
- petal_qc/BTreport/bustapeReport.py +144 -0
- petal_qc/__init__.py +14 -0
- petal_qc/metrology/Cluster.py +90 -0
- petal_qc/metrology/DataFile.py +47 -0
- petal_qc/metrology/PetalMetrology.py +327 -0
- petal_qc/metrology/__init__.py +0 -0
- petal_qc/metrology/all2csv.py +57 -0
- petal_qc/metrology/analyze_locking_points.py +597 -0
- petal_qc/metrology/cold_noise.py +106 -0
- petal_qc/metrology/comparisonTable.py +59 -0
- petal_qc/metrology/convert_mitutoyo.py +175 -0
- petal_qc/metrology/convert_smartscope.py +145 -0
- petal_qc/metrology/coreMetrology.py +402 -0
- petal_qc/metrology/data2csv.py +63 -0
- petal_qc/metrology/do_Metrology.py +117 -0
- petal_qc/metrology/flatness4nigel.py +157 -0
- petal_qc/metrology/gtkutils.py +120 -0
- petal_qc/metrology/petal_flatness.py +353 -0
- petal_qc/metrology/show_data_file.py +118 -0
- petal_qc/metrology/testSummary.py +37 -0
- petal_qc/metrology/test_paralelism.py +71 -0
- petal_qc/thermal/CSVImage.py +69 -0
- petal_qc/thermal/DebugPlot.py +76 -0
- petal_qc/thermal/IRBFile.py +768 -0
- petal_qc/thermal/IRCore.py +110 -0
- petal_qc/thermal/IRDataGetter.py +359 -0
- petal_qc/thermal/IRPetal.py +1338 -0
- petal_qc/thermal/IRPetalParam.py +71 -0
- petal_qc/thermal/PetalColorMaps.py +62 -0
- petal_qc/thermal/Petal_IR_Analysis.py +142 -0
- petal_qc/thermal/PipeFit.py +598 -0
- petal_qc/thermal/__init__.py +0 -0
- petal_qc/thermal/analyze_IRCore.py +417 -0
- petal_qc/thermal/contours.py +378 -0
- petal_qc/thermal/create_IRCore.py +185 -0
- petal_qc/thermal/pipe_read.py +182 -0
- petal_qc/thermal/show_IR_petal.py +420 -0
- petal_qc/utils/Geometry.py +756 -0
- petal_qc/utils/Progress.py +182 -0
- petal_qc/utils/__init__.py +0 -0
- petal_qc/utils/all_files.py +35 -0
- petal_qc/utils/docx_utils.py +186 -0
- petal_qc/utils/fit_utils.py +188 -0
- petal_qc/utils/utils.py +180 -0
- petal_qc-0.0.0.dist-info/METADATA +29 -0
- petal_qc-0.0.0.dist-info/RECORD +51 -0
- petal_qc-0.0.0.dist-info/WHEEL +5 -0
- petal_qc-0.0.0.dist-info/entry_points.txt +3 -0
- petal_qc-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Produce metrology report."""
|
|
3
|
+
import io
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
import traceback
|
|
7
|
+
from argparse import Action
|
|
8
|
+
from argparse import ArgumentParser
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas as pd
|
|
15
|
+
import petal_qc.utils.docx_utils as docx_utils
|
|
16
|
+
from petal_qc.utils.Geometry import project_to_plane
|
|
17
|
+
from petal_qc.utils.utils import output_folder
|
|
18
|
+
from petal_qc.metrology import DataFile
|
|
19
|
+
from petal_qc.metrology.analyze_locking_points import analyze_locking_point_data
|
|
20
|
+
from petal_qc.metrology.analyze_locking_points import locking_point_positions
|
|
21
|
+
from petal_qc.metrology.convert_mitutoyo import mitutoyo2cvs
|
|
22
|
+
from petal_qc.metrology.petal_flatness import petal_flatness
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def check_spec(val, nom, fraction=0.0):
|
|
26
|
+
"""Checks if value is consistent with specs.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
----
|
|
30
|
+
val (): Actual value
|
|
31
|
+
nom (): Nominal value
|
|
32
|
+
fraction (float, optional): Fraction of nominal allowable. Defaults to 0.05.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
bool: true if within specs.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
if val <= nom:
|
|
40
|
+
return True
|
|
41
|
+
else:
|
|
42
|
+
rc = val <= (1+fraction)*nom
|
|
43
|
+
return rc
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CommaSeparatedListAction(Action):
|
|
47
|
+
"""Create a list from the comma sepparated numbers at imput."""
|
|
48
|
+
|
|
49
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
50
|
+
"""The actual action."""
|
|
51
|
+
value = np.array(list(map(float, values.split(','))), dtype='float64')
|
|
52
|
+
if value.shape[0] < 3:
|
|
53
|
+
raise Exception("{} needs a 3D vector".format(self.dest))
|
|
54
|
+
|
|
55
|
+
setattr(namespace, self.dest, value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def petal_metrology(ifile, options):
|
|
59
|
+
"""Do the analysis of the petal metrology data.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
----
|
|
63
|
+
ifile: Input file from Mitutoyo.
|
|
64
|
+
options: Command line options.
|
|
65
|
+
|
|
66
|
+
Return
|
|
67
|
+
------
|
|
68
|
+
Dictionary with DB values
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
ifile = Path(ifile).expanduser().resolve()
|
|
72
|
+
if not ifile.exists():
|
|
73
|
+
print("input file {} does not exist.".format(ifile))
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# We infer the date of the test from the creation date of data file.
|
|
77
|
+
test_date = datetime.fromtimestamp(ifile.stat().st_ctime).isoformat(timespec='milliseconds')
|
|
78
|
+
if test_date[-1] not in ['zZ']:
|
|
79
|
+
test_date += 'Z'
|
|
80
|
+
|
|
81
|
+
# The JSon output
|
|
82
|
+
sNames = {0: "petal", 1: "R0", 2: "R1", 3: "R2", 4: "R3S0", 5: "R3S1", 6: "R4S0", 7: "R4S1", 8: "R5S0", 9: "R5S1"}
|
|
83
|
+
|
|
84
|
+
dbOut = {
|
|
85
|
+
"component": options.SN,
|
|
86
|
+
"testType": "PETAL_METROLOGY_FRONT" if options.is_front else "PETAL_METROLOGY_BACK",
|
|
87
|
+
"institution": "DESYHH" if options.desy else "IFIC",
|
|
88
|
+
"runNumber": "1",
|
|
89
|
+
"date": test_date,
|
|
90
|
+
"passed": True,
|
|
91
|
+
"problems": False,
|
|
92
|
+
"properties": {
|
|
93
|
+
"OPERATOR": "Ohian Elesgaray",
|
|
94
|
+
"MACHINETYPE": "Mitutoyo"
|
|
95
|
+
},
|
|
96
|
+
"comments": [],
|
|
97
|
+
"defects": [],
|
|
98
|
+
"results": {
|
|
99
|
+
"METROLOGY": {},
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
results = {
|
|
104
|
+
"LOCATION_DELTA": {},
|
|
105
|
+
"REL_POS_DELTA": {},
|
|
106
|
+
"FD01_DIAM": 0,
|
|
107
|
+
"FD02_DIAM": 0,
|
|
108
|
+
"FLATNESS_GLOBAL": 0,
|
|
109
|
+
"FLATNESS_LOCAL": [], # a 12 element array
|
|
110
|
+
"COPLANARITY_LOCATORS": 0,
|
|
111
|
+
"PARALLELISM": 0,
|
|
112
|
+
"CHECK_BOT_LOC_DIAM": True,
|
|
113
|
+
"CHECK_SLOT_LOC_DIAM": True,
|
|
114
|
+
"CHECK_OVERSIZE_LOC_DIAM": True
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Open the output doc.
|
|
118
|
+
# Open the output document if a title is given in the options.
|
|
119
|
+
document = docx_utils.Document()
|
|
120
|
+
document.add_page_numbers()
|
|
121
|
+
document.styles['Normal'].font.name = "Calibri"
|
|
122
|
+
if options.title:
|
|
123
|
+
document.add_heading(options.title, 0)
|
|
124
|
+
|
|
125
|
+
P = document.add_paragraph(ifile.name, "Subtitle")
|
|
126
|
+
P.alignment = docx_utils.paragraph_align_center()
|
|
127
|
+
|
|
128
|
+
# Do petal flatness analysis
|
|
129
|
+
Fmean = 0.0
|
|
130
|
+
if options.desy:
|
|
131
|
+
flatness_data = DataFile.read(ifile, "PetalPlane")
|
|
132
|
+
else:
|
|
133
|
+
flatness_data = DataFile.read(ifile, r"Punto[-|Vision-]\w", "Punto")
|
|
134
|
+
Fmean = np.mean(flatness_data[:, 2])
|
|
135
|
+
flatness_data[:, 2] -= Fmean
|
|
136
|
+
|
|
137
|
+
TM = None
|
|
138
|
+
if not options.locking_points:
|
|
139
|
+
TM, avg, Zmean, Flatness = petal_flatness(flatness_data, options, document=document)
|
|
140
|
+
results["FLATNESS_GLOBAL"] = Flatness[0]
|
|
141
|
+
if not check_spec(Flatness[0], 0.250):
|
|
142
|
+
dbOut["comments"].append("Global flatness in sensor area {:.3f}".format(Flatness[0]))
|
|
143
|
+
|
|
144
|
+
results["FLATNESS_LOCAL"] = Flatness[1:]
|
|
145
|
+
for i, v in enumerate(Flatness[1:]):
|
|
146
|
+
if not check_spec(v, 0.050):
|
|
147
|
+
dbOut["defects"].append({
|
|
148
|
+
"name": "FLATNESS",
|
|
149
|
+
"description": "Flatness of {} is {:.3f} mm > 0.050 mm".format(sNames[i+1], v)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
# Do locator flatness analysis
|
|
153
|
+
TM = None # TODO: fix this
|
|
154
|
+
if options.desy:
|
|
155
|
+
locator_data = DataFile.read(ifile, ".*_FineFlatness")
|
|
156
|
+
else:
|
|
157
|
+
locator_data = DataFile.read(ifile, "PuntoLocator", "Punto")
|
|
158
|
+
locator_data[:, 2] -= Fmean
|
|
159
|
+
|
|
160
|
+
if TM:
|
|
161
|
+
M = project_to_plane(locator_data, TM, [0., 0., avg[2]])
|
|
162
|
+
M[:, 2] -= Zmean
|
|
163
|
+
out = analyze_locking_point_data(M, document=document, nbins=options.nbins, plane_fit=False)
|
|
164
|
+
else:
|
|
165
|
+
data = np.concatenate((flatness_data, locator_data))
|
|
166
|
+
out = analyze_locking_point_data(data, document=document, nbins=options.nbins, plane_fit=True)
|
|
167
|
+
|
|
168
|
+
for key, val in out.items():
|
|
169
|
+
results[key] = val
|
|
170
|
+
if key == "COPLANARITY_LOCATORS" and not check_spec(val, 0.1):
|
|
171
|
+
dbOut["comments"].append(
|
|
172
|
+
"Coplanarity of locators: {:.3f} mm".format(val))
|
|
173
|
+
elif key == "PARALLELISM" and not check_spec(abs(val), 0.1):
|
|
174
|
+
dbOut["defects"].append({
|
|
175
|
+
"name": key,
|
|
176
|
+
"description": "Paralelism of locators is {:.3f} mm > 0.100 mm".format(val)
|
|
177
|
+
})
|
|
178
|
+
elif key == "OFFSET" and not check_spec(abs(val), 0.100):
|
|
179
|
+
dbOut["comments"].append("Offset of locator plane w.r.t BT is {:.3f} mm".format(val))
|
|
180
|
+
|
|
181
|
+
# Analyze locking point positions
|
|
182
|
+
if not options.desy:
|
|
183
|
+
ofile = io.StringIO()
|
|
184
|
+
mitutoyo2cvs([ifile], ofile,
|
|
185
|
+
label=r"agujero_inf|Locator\w*|Slot\w+|Fiducial\w+",
|
|
186
|
+
data_type="Círculo|Punto",
|
|
187
|
+
keep=True, fill=6)
|
|
188
|
+
ofile.seek(0)
|
|
189
|
+
|
|
190
|
+
tbl = pd.read_csv(ofile, names=["X", "Y", "Z", "D", "R", "C", "Name"], sep=',', skiprows=1, index_col="Name")
|
|
191
|
+
print(tbl)
|
|
192
|
+
|
|
193
|
+
keywords = [n for n in tbl.index]
|
|
194
|
+
# if "FiducialBot" in keywords:
|
|
195
|
+
# indx = ["agujero_inf", "LocatorTop", "FiducialBot", "FiducialTop"]
|
|
196
|
+
# offset = 0
|
|
197
|
+
# else:
|
|
198
|
+
# indx = ["LocatorBot", "LocatorTop", "agujero_inf", "FiducialTop"]
|
|
199
|
+
# offset = 3
|
|
200
|
+
|
|
201
|
+
indx = ["LocatorBot", "LocatorTop", "agujero_inf", "FiducialTop"]
|
|
202
|
+
offset = 3
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
slot_center = np.mean(tbl.loc[["SlotSup", "SlotInf"]][["X", "Y", "D"]].values, axis=0)
|
|
206
|
+
|
|
207
|
+
print("")
|
|
208
|
+
print()
|
|
209
|
+
print(tbl.loc[indx][["X", "Y", "D"]])
|
|
210
|
+
input = tbl.loc[indx][["X", "Y", "D"]].values
|
|
211
|
+
|
|
212
|
+
ninput = np.insert(input, 1, slot_center, axis=0)
|
|
213
|
+
ninput[:, 1] += offset
|
|
214
|
+
out = locking_point_positions(ninput, document)
|
|
215
|
+
for key, val in out.items():
|
|
216
|
+
results[key] = val
|
|
217
|
+
if key == "LOCATION_DELTA":
|
|
218
|
+
for k, v in val.items():
|
|
219
|
+
delta = np.linalg.norm(v)
|
|
220
|
+
if not check_spec(delta, 0.050):
|
|
221
|
+
dbOut["defects"].append({
|
|
222
|
+
"name": key,
|
|
223
|
+
"description": "Delta {} is {:.3f} mm > 0.050 mm.".format(k, delta)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
elif key == "REL_POS_DELTA":
|
|
227
|
+
for k, v in val.items():
|
|
228
|
+
delta = np.linalg.norm(v)
|
|
229
|
+
if not check_spec(delta, 0.025):
|
|
230
|
+
dbOut["defects"].append({
|
|
231
|
+
"name": key,
|
|
232
|
+
"description": "Delta {} is {:.3f} mm > 0.025 mm.".format(k, delta)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
elif "CHECK_" in key:
|
|
236
|
+
if "OVERSIZE" in key:
|
|
237
|
+
if not check_spec(abs(val), 0.050):
|
|
238
|
+
dbOut["defects"].append({
|
|
239
|
+
"name": key,
|
|
240
|
+
"description": "LOC DIAM delta is {:.3f} mm > 0.025 mm.".format(abs(val))
|
|
241
|
+
})
|
|
242
|
+
else:
|
|
243
|
+
if val < 0 or val > 0.012:
|
|
244
|
+
dbOut["defects"].append({
|
|
245
|
+
"name": key,
|
|
246
|
+
"description": "LOC DIAM is not H7 0 <= {:.3f} <= 0.012 mm.".format(val)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
ofile.close()
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# Add a section for defects.
|
|
253
|
+
document.add_heading("Comments and Defects.", level=1)
|
|
254
|
+
added_defects = False
|
|
255
|
+
if len(dbOut["comments"])>0:
|
|
256
|
+
added_defects = True
|
|
257
|
+
document.add_heading("Comments.", level=2)
|
|
258
|
+
for C in dbOut["comments"]:
|
|
259
|
+
document.add_paragraph(C)
|
|
260
|
+
|
|
261
|
+
if len(dbOut["defects"])>0:
|
|
262
|
+
added_defects = True
|
|
263
|
+
document.add_heading("Defects.", level=2)
|
|
264
|
+
for D in dbOut["defects"]:
|
|
265
|
+
document.add_paragraph("{}: {}".format(D["name"], D["description"]))
|
|
266
|
+
|
|
267
|
+
if not added_defects:
|
|
268
|
+
document.add_paragraph("Petal is GOOD. No comments nor defects found.")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# close the doc file
|
|
272
|
+
ofile = output_folder(options.folder, options.out)
|
|
273
|
+
document.save(ofile)
|
|
274
|
+
|
|
275
|
+
dbOut["results"]["METROLOGY"] = results
|
|
276
|
+
|
|
277
|
+
if len(dbOut["defects"])>0:
|
|
278
|
+
dbOut["passed"] = False
|
|
279
|
+
|
|
280
|
+
if len(dbOut["comments"])>0:
|
|
281
|
+
dbOut["problems"] = True
|
|
282
|
+
|
|
283
|
+
return dbOut
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
parser = ArgumentParser()
|
|
288
|
+
parser.add_argument('files', nargs='*', help="Input files")
|
|
289
|
+
parser.add_argument("--SN", default="SNxxxx", help="Petal core serial nunber")
|
|
290
|
+
parser.add_argument("--front", dest='is_front', action="store_true", default=True)
|
|
291
|
+
parser.add_argument("--back", dest='is_front', action="store_false", default=True)
|
|
292
|
+
parser.add_argument("--prefix", dest='prefix', default="petal_metrology")
|
|
293
|
+
parser.add_argument("--desy", dest='desy', action="store_true", default=False)
|
|
294
|
+
parser.add_argument("--save", dest='save', action="store_true", default=False)
|
|
295
|
+
parser.add_argument("--out", dest="out", default="petal_flatness.docx",
|
|
296
|
+
type=str, help="The output fiel name")
|
|
297
|
+
parser.add_argument("--bottom-lp", dest='bLP', action=CommaSeparatedListAction, default=None,
|
|
298
|
+
help="Bottom locking point fiducial coordinates")
|
|
299
|
+
parser.add_argument("--upper-lp", dest='uLP', action=CommaSeparatedListAction, default=None,
|
|
300
|
+
help="upper locking point fiducials coordinates")
|
|
301
|
+
|
|
302
|
+
parser.add_argument("--title", dest="title", default=None,
|
|
303
|
+
type=str, help="Document title")
|
|
304
|
+
parser.add_argument("--nbins", dest="nbins", default=25,
|
|
305
|
+
type=int, help="Number of bins")
|
|
306
|
+
parser.add_argument("--folder", default=None, help="Folder to store output files. Superseeds folder in --out")
|
|
307
|
+
parser.add_argument("--locking_points", action="store_true", default=False)
|
|
308
|
+
# This is to convert a CMM file
|
|
309
|
+
parser.add_argument("--label", default="\\w+", help="The label to select")
|
|
310
|
+
parser.add_argument("--type", default="Punto", help="The class to select")
|
|
311
|
+
|
|
312
|
+
options = parser.parse_args()
|
|
313
|
+
if len(options.files) == 0:
|
|
314
|
+
print(sys.argv[0])
|
|
315
|
+
print("I need an input file")
|
|
316
|
+
sys.exit()
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
outDB = petal_metrology(options.files[0], options)
|
|
320
|
+
ofile = output_folder(options.folder, options.prefix + '.json')
|
|
321
|
+
with open(ofile, 'w') as of:
|
|
322
|
+
json.dump(outDB, of)
|
|
323
|
+
|
|
324
|
+
except Exception:
|
|
325
|
+
print(traceback.format_exc())
|
|
326
|
+
|
|
327
|
+
plt.show()
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Convert all files in input file to CSV (petal, locators)"""
|
|
3
|
+
import sys
|
|
4
|
+
from metrology.data2csv import data2cvs
|
|
5
|
+
from metrology.convert_smartscope import read_smartscope
|
|
6
|
+
from collections import namedtuple
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OptDict(dict):
|
|
10
|
+
"""https://stackoverflow.com/a/1639632/6494418"""
|
|
11
|
+
|
|
12
|
+
def __getattr__(self, name):
|
|
13
|
+
return self[name] if not isinstance(self[name], dict) \
|
|
14
|
+
else OptDict(self[name])
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def all2cvs(ifile):
|
|
18
|
+
"""Convert files listed in input to CSV."""
|
|
19
|
+
with open(ifile, 'r') as inp:
|
|
20
|
+
|
|
21
|
+
for line in inp:
|
|
22
|
+
line = line.strip()
|
|
23
|
+
if len(line) == 0:
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
if line[0] == '#':
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
SN = ""
|
|
30
|
+
try:
|
|
31
|
+
fnam, prefix = line.split()
|
|
32
|
+
except Exception:
|
|
33
|
+
fnam, prefix, SN, *_ = line.split()
|
|
34
|
+
|
|
35
|
+
opts = OptDict({"prefix": prefix})
|
|
36
|
+
print(fnam, prefix)
|
|
37
|
+
if options.desy:
|
|
38
|
+
read_smartscope(fnam, prefix + "-petal.csv", "PetalPlane")
|
|
39
|
+
|
|
40
|
+
else:
|
|
41
|
+
data2cvs([fnam, ], opts)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
from argparse import ArgumentParser
|
|
46
|
+
parser = ArgumentParser()
|
|
47
|
+
parser.add_argument('files', nargs='*', help="Input files")
|
|
48
|
+
parser.add_argument("--desy", dest='desy', action="store_true", default=False)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
options = parser.parse_args()
|
|
52
|
+
if len(options.files) == 0:
|
|
53
|
+
print(sys.argv[0])
|
|
54
|
+
print("I need an input file")
|
|
55
|
+
sys.exit()
|
|
56
|
+
|
|
57
|
+
all2cvs(options.files[0])
|