itkdb-gtk 0.10.9.dev4__py3-none-any.whl → 0.10.10__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 itkdb-gtk might be problematic. Click here for more details.
- itkdb_gtk/CreateShipments.py +9 -4
- itkdb_gtk/GetShipments.py +65 -62
- itkdb_gtk/GlueWeight.py +34 -50
- itkdb_gtk/ITkDBlogin.py +6 -2
- itkdb_gtk/ITkDButils.py +44 -22
- itkdb_gtk/PanelVisualInspection.py +16 -11
- itkdb_gtk/PetalReceptionTests.py +16 -7
- itkdb_gtk/UploadMultipleTests.py +125 -63
- itkdb_gtk/UploadTest.py +10 -7
- itkdb_gtk/VisualInspection.py +299 -0
- itkdb_gtk/WireBondGui.py +482 -138
- itkdb_gtk/__init__.py +6 -6
- itkdb_gtk/dashBoard.py +81 -15
- itkdb_gtk/dbGtkUtils.py +60 -17
- {itkdb_gtk-0.10.9.dev4.dist-info → itkdb_gtk-0.10.10.dist-info}/METADATA +1 -1
- itkdb_gtk-0.10.10.dist-info/RECORD +28 -0
- {itkdb_gtk-0.10.9.dev4.dist-info → itkdb_gtk-0.10.10.dist-info}/WHEEL +1 -1
- {itkdb_gtk-0.10.9.dev4.dist-info → itkdb_gtk-0.10.10.dist-info}/entry_points.txt +1 -1
- itkdb_gtk/UploadPetalInformation.py +0 -710
- itkdb_gtk/readAVSdata.py +0 -693
- itkdb_gtk-0.10.9.dev4.dist-info/RECORD +0 -29
- {itkdb_gtk-0.10.9.dev4.dist-info → itkdb_gtk-0.10.10.dist-info}/top_level.txt +0 -0
itkdb_gtk/WireBondGui.py
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
"""Wirebonding GUI for PSB."""
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
|
+
import re
|
|
5
6
|
import json
|
|
7
|
+
import copy
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from collections import namedtuple
|
|
6
10
|
import gi
|
|
7
11
|
|
|
8
12
|
gi.require_version("Gtk", "3.0")
|
|
@@ -12,7 +16,6 @@ try:
|
|
|
12
16
|
import itkdb_gtk
|
|
13
17
|
|
|
14
18
|
except ImportError:
|
|
15
|
-
from pathlib import Path
|
|
16
19
|
cwd = Path(__file__).parent.parent
|
|
17
20
|
sys.path.append(cwd.as_posix())
|
|
18
21
|
import itkdb_gtk
|
|
@@ -22,6 +25,11 @@ __HELP_LINK__="https://itkdb-gtk.docs.cern.ch/wirebondTest.html"
|
|
|
22
25
|
from itkdb_gtk import dbGtkUtils
|
|
23
26
|
from itkdb_gtk import ITkDBlogin, ITkDButils, UploadTest
|
|
24
27
|
|
|
28
|
+
|
|
29
|
+
#valid_channel = re.compile("(^[0-9]+)-([0-9]+)")
|
|
30
|
+
valid_channel = re.compile("^[0-9]+[\\s*\\,\\s,-[0-9]+]*")
|
|
31
|
+
|
|
32
|
+
|
|
25
33
|
test_parameters = {
|
|
26
34
|
"Repaired Row 1": "REPAIRED_FRONTEND_ROW1",
|
|
27
35
|
"Failed Row 1": "FAILED_FRONTEND_ROW1",
|
|
@@ -84,21 +92,75 @@ module_param = {
|
|
|
84
92
|
],
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
module_to_tf = {
|
|
96
|
+
0: [],
|
|
97
|
+
1: [],
|
|
98
|
+
2: [],
|
|
99
|
+
3: [],
|
|
100
|
+
4: [],
|
|
101
|
+
5: []
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
hyb_to_pb = {
|
|
105
|
+
0: [],
|
|
106
|
+
1: [],
|
|
107
|
+
2: [],
|
|
108
|
+
3: [],
|
|
109
|
+
4: [],
|
|
110
|
+
5: []
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def range_to_list(V):
|
|
115
|
+
"""Convert a range to a list.
|
|
116
|
+
|
|
117
|
+
ch1-ch2 or ch1:ch2 -> [ch1, ch1+1, ch1+2, ..., ch2] or
|
|
118
|
+
ch1:step:ch2 -> [ch1, ch1+sep, ch1+2*step, ..., ch2]
|
|
119
|
+
"""
|
|
120
|
+
nfound = 0
|
|
121
|
+
for c in "-:,":
|
|
122
|
+
if c in V:
|
|
123
|
+
nfound += 1
|
|
124
|
+
break
|
|
125
|
+
|
|
126
|
+
if nfound == 0:
|
|
127
|
+
return [V]
|
|
128
|
+
|
|
129
|
+
out = []
|
|
130
|
+
values = V.split(',')
|
|
131
|
+
for V in values:
|
|
132
|
+
if '-' in V:
|
|
133
|
+
endpoints = list(map(int, V.split('-')))
|
|
134
|
+
endpoints.sort()
|
|
135
|
+
for i in range(endpoints[0], endpoints[1]+1):
|
|
136
|
+
out.append(str(i))
|
|
137
|
+
elif ':' in V:
|
|
138
|
+
endpoints = list(map(int, V.split(':')))
|
|
139
|
+
if len(endpoints) == 2:
|
|
140
|
+
endpoints.sort()
|
|
141
|
+
for i in range(endpoints[0], endpoints[1]+1):
|
|
142
|
+
out.append(str(i))
|
|
143
|
+
|
|
144
|
+
elif len(endpoints) == 3:
|
|
145
|
+
for i in range(endpoints[0], endpoints[2]+1, endpoints[1]):
|
|
146
|
+
out.append(str(i))
|
|
147
|
+
|
|
148
|
+
else:
|
|
149
|
+
print("Wring range specification. {}".format(V))
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
else:
|
|
153
|
+
out.append(V)
|
|
97
154
|
|
|
98
|
-
|
|
155
|
+
return out
|
|
99
156
|
|
|
100
|
-
|
|
157
|
+
def count_items(items):
|
|
158
|
+
"""Count number of channels from results."""
|
|
159
|
+
nitems = 0
|
|
160
|
+
for key in items.keys():
|
|
161
|
+
nitems += len(range_to_list(key))
|
|
101
162
|
|
|
163
|
+
return nitems
|
|
102
164
|
|
|
103
165
|
def find_holes(chan_list, min_chan=0, max_chan=999999):
|
|
104
166
|
"""Find groups of consecutive channels."""
|
|
@@ -142,19 +204,41 @@ def find_holes(chan_list, min_chan=0, max_chan=999999):
|
|
|
142
204
|
return holes
|
|
143
205
|
|
|
144
206
|
|
|
207
|
+
def wire2strip(iwire, irow, first_chan):
|
|
208
|
+
"""From bond to strip number."""
|
|
209
|
+
if irow % 2:
|
|
210
|
+
istrip = 2*(iwire-first_chan) + 1
|
|
211
|
+
else:
|
|
212
|
+
istrip = 2*(iwire-first_chan)
|
|
213
|
+
|
|
214
|
+
return istrip
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class Hole:
|
|
218
|
+
"""A range of consecutive unconnected channels."""
|
|
219
|
+
def __init__(self, *args):
|
|
220
|
+
for i, name in enumerate(['sensor', 'hybrid', 'row', 'chan', 'width']):
|
|
221
|
+
setattr(self, name, args[i])
|
|
222
|
+
|
|
223
|
+
def __repr__(self):
|
|
224
|
+
return "sensor: {} hyb: {} row: {} chan: {} width: {}".format(
|
|
225
|
+
self.sensor, self.hybrid, self.row, self.chan, self.width
|
|
226
|
+
)
|
|
227
|
+
|
|
145
228
|
class HybridHoles:
|
|
146
|
-
"""Holes in hybrid
|
|
229
|
+
"""Holes in hybrid bonds.
|
|
147
230
|
|
|
148
231
|
Holes are defined by a list [first_chan, n_chan].
|
|
149
232
|
"""
|
|
150
233
|
|
|
151
|
-
def __init__(self, param, hid=0):
|
|
234
|
+
def __init__(self, param, win=None, hid=0):
|
|
152
235
|
"""Initialization.
|
|
153
236
|
|
|
154
237
|
Args:
|
|
155
238
|
param: Hybrid wirebon parameters.
|
|
156
239
|
|
|
157
240
|
"""
|
|
241
|
+
self.win = win
|
|
158
242
|
self.id = hid
|
|
159
243
|
self.param = param
|
|
160
244
|
self.nchan = 0
|
|
@@ -163,12 +247,25 @@ class HybridHoles:
|
|
|
163
247
|
|
|
164
248
|
self.holes = [[] for irow in range(4)]
|
|
165
249
|
self.channels = [[] for irow in range(4)]
|
|
250
|
+
# Sensor strips for each of the strip rows "served" by a hybrid.
|
|
251
|
+
self.sensor_channels = [[], []]
|
|
252
|
+
self.sensor_holes = [[], []]
|
|
253
|
+
|
|
254
|
+
def ready(self):
|
|
255
|
+
"""Call when all channels are in."""
|
|
256
|
+
for irow, C in enumerate(self.channels):
|
|
257
|
+
C.sort()
|
|
258
|
+
self.holes[irow] = find_holes(C)
|
|
259
|
+
|
|
260
|
+
for irow, S in enumerate(self.sensor_channels):
|
|
261
|
+
S.sort()
|
|
262
|
+
self.sensor_holes[irow] = find_holes(S)
|
|
166
263
|
|
|
167
264
|
def add_channel(self, irow, ichan)->bool:
|
|
168
265
|
"""Add a new channel in row.
|
|
169
266
|
|
|
170
267
|
Args:
|
|
171
|
-
irow:
|
|
268
|
+
irow: row number
|
|
172
269
|
ichan: channel number
|
|
173
270
|
|
|
174
271
|
Returns:
|
|
@@ -177,102 +274,93 @@ class HybridHoles:
|
|
|
177
274
|
"""
|
|
178
275
|
first_chan = self.param[irow][0]
|
|
179
276
|
last_chan = self.param[irow][1]
|
|
277
|
+
strip_row = int(irow/2)
|
|
278
|
+
|
|
279
|
+
|
|
180
280
|
if isinstance(ichan, list) or isinstance(ichan, tuple):
|
|
181
281
|
nadded = 0
|
|
182
282
|
for ich in ichan:
|
|
183
|
-
if
|
|
283
|
+
if first_chan <= ich <= last_chan:
|
|
184
284
|
self.channels[irow].append(ich)
|
|
185
285
|
nadded += 1
|
|
186
286
|
|
|
187
287
|
self.channels[irow] = sorted(self.channels[irow])
|
|
288
|
+
for iwire in self.channels[irow]:
|
|
289
|
+
istrip = wire2strip(iwire, irow, first_chan)
|
|
290
|
+
self.sensor_channels[strip_row].append(istrip)
|
|
291
|
+
|
|
188
292
|
return nadded>0
|
|
189
|
-
else:
|
|
190
|
-
if ichan >= first_chan and ichan <= last_chan:
|
|
191
|
-
self.channels[irow].append(ichan)
|
|
192
|
-
return True
|
|
193
|
-
else:
|
|
194
|
-
return False
|
|
195
293
|
|
|
196
|
-
|
|
294
|
+
if first_chan <= ichan <= last_chan:
|
|
295
|
+
self.channels[irow].append(ichan)
|
|
296
|
+
istrip = wire2strip(ichan, irow, first_chan)
|
|
297
|
+
self.sensor_channels[strip_row].append(istrip)
|
|
298
|
+
return True
|
|
299
|
+
|
|
300
|
+
return False
|
|
301
|
+
|
|
302
|
+
def get_n_unconnected(self) -> list:
|
|
197
303
|
"""Count number of unconnected channels.
|
|
198
304
|
|
|
199
305
|
Return a list, one item per row.
|
|
200
306
|
"""
|
|
201
|
-
nchan = []
|
|
202
|
-
for row in self.holes:
|
|
203
|
-
nch = 0
|
|
204
|
-
for h in row:
|
|
205
|
-
nch += h[1]
|
|
206
|
-
|
|
207
|
-
nchan.append(nch)
|
|
208
|
-
|
|
307
|
+
nchan = [len(C) for C in self.channels]
|
|
209
308
|
return nchan
|
|
210
309
|
|
|
211
|
-
def
|
|
212
|
-
"""
|
|
310
|
+
def get_max_consecutive_from_list(self, holes):
|
|
311
|
+
"""Return max widht of holes."""
|
|
213
312
|
mx_width = []
|
|
313
|
+
lst_holes = []
|
|
314
|
+
for irow, row in enumerate(holes):
|
|
315
|
+
if len(row) == 0:
|
|
316
|
+
mxW = 0
|
|
214
317
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
mxW = h[1]
|
|
318
|
+
else:
|
|
319
|
+
mxW = -1
|
|
320
|
+
for h in row:
|
|
321
|
+
lst_holes.append(Hole(0, self.id, irow, h[0], h[1]))
|
|
322
|
+
mxW = max(mxW, h[1])
|
|
220
323
|
|
|
221
324
|
mx_width.append(mxW)
|
|
222
325
|
|
|
223
|
-
return mx_width
|
|
224
|
-
|
|
225
|
-
def get_sensor_holes(self):
|
|
226
|
-
"""Compute holes in 'sensor' strips.
|
|
326
|
+
return mx_width, lst_holes
|
|
227
327
|
|
|
228
|
-
Each hybrid has 2 sensor segments corresponding to
|
|
229
|
-
rows (1,2) and (3, 4).
|
|
230
328
|
|
|
231
|
-
|
|
232
|
-
"""
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
for irow, row in enumerate(self.channels):
|
|
236
|
-
isegment = int(irow/2)
|
|
237
|
-
for ich in row:
|
|
238
|
-
rng = self.param[irow]
|
|
239
|
-
if irow % 2:
|
|
240
|
-
chan = 2*(ich-rng[0]) + 1
|
|
241
|
-
else:
|
|
242
|
-
chan = 2*(ich-rng[0])
|
|
243
|
-
|
|
244
|
-
channels[isegment].append(chan)
|
|
245
|
-
|
|
246
|
-
channels[isegment] = sorted(channels[isegment])
|
|
247
|
-
|
|
248
|
-
for isegment, S in enumerate(channels):
|
|
249
|
-
H = find_holes(S)
|
|
250
|
-
if len(H)>0:
|
|
251
|
-
out = [ [0, self.id, isegment, chan, width] for chan, width in H ]
|
|
252
|
-
holes.extend(out)
|
|
253
|
-
|
|
254
|
-
return holes
|
|
329
|
+
def get_max_consecutive(self):
|
|
330
|
+
"""Returns the largest 'hole'."""
|
|
331
|
+
mx_width, _ = self.get_max_consecutive_from_list(self.holes)
|
|
332
|
+
return mx_width
|
|
255
333
|
|
|
334
|
+
def get_max_sensor_consecutive(self):
|
|
335
|
+
"""Return largest hole in sensor."""
|
|
336
|
+
mx_width, holes = self.get_max_consecutive_from_list(self.sensor_holes)
|
|
337
|
+
return mx_width, holes
|
|
256
338
|
|
|
257
339
|
class SensorHoles:
|
|
258
340
|
"""Holes in sensor."""
|
|
259
341
|
|
|
260
|
-
def __init__(self, param, sid=0):
|
|
342
|
+
def __init__(self, param, win=None, sid=0):
|
|
261
343
|
"""Initialization.
|
|
262
344
|
|
|
263
345
|
Args:
|
|
264
346
|
param: sensor wirebon params
|
|
265
347
|
"""
|
|
348
|
+
self.win = win
|
|
266
349
|
self.id = sid
|
|
267
350
|
self.param = param
|
|
268
351
|
self.nchan = 0
|
|
269
352
|
self.nhybrid = len(param)
|
|
270
353
|
self.hybrids = []
|
|
271
354
|
for i, P in enumerate(param):
|
|
272
|
-
H = HybridHoles(P, hid=i)
|
|
355
|
+
H = HybridHoles(P, hid=i, win=win)
|
|
273
356
|
self.hybrids.append(H)
|
|
274
357
|
self.nchan += H.nchan
|
|
275
358
|
|
|
359
|
+
def ready(self):
|
|
360
|
+
"""Call when all channels are in."""
|
|
361
|
+
for H in self.hybrids:
|
|
362
|
+
H.ready()
|
|
363
|
+
|
|
276
364
|
def get_n_hyb(self):
|
|
277
365
|
"""Return number of hybrids."""
|
|
278
366
|
return len(self.hybrids)
|
|
@@ -286,15 +374,29 @@ class SensorHoles:
|
|
|
286
374
|
|
|
287
375
|
This is ordered by row.
|
|
288
376
|
"""
|
|
289
|
-
mx_width = [0
|
|
377
|
+
mx_width = [0 for x in range(4)]
|
|
290
378
|
for hyb in self.hybrids:
|
|
291
379
|
mxW = hyb.get_max_consecutive()
|
|
292
|
-
for j in
|
|
293
|
-
|
|
294
|
-
mx_width[j] = mxW[j]
|
|
380
|
+
for j, val in enumerate(mxW):
|
|
381
|
+
mx_width[j] = max(mx_width[j] , val)
|
|
295
382
|
|
|
296
383
|
return mx_width
|
|
297
384
|
|
|
385
|
+
def get_max_sensor_consecutive(self):
|
|
386
|
+
"""MAx widht of holes in sensor."""
|
|
387
|
+
mx_width = -1
|
|
388
|
+
holes = []
|
|
389
|
+
for hyb in self.hybrids:
|
|
390
|
+
mxW, hyb_holes = hyb.get_max_sensor_consecutive()
|
|
391
|
+
for H in hyb_holes:
|
|
392
|
+
H.sensor = self.id
|
|
393
|
+
|
|
394
|
+
holes.extend(hyb_holes)
|
|
395
|
+
for v in mxW:
|
|
396
|
+
mx_width = max(mx_width, v)
|
|
397
|
+
|
|
398
|
+
return mx_width, holes
|
|
399
|
+
|
|
298
400
|
def get_n_unconnected(self):
|
|
299
401
|
"""Count number of unconnected channels.
|
|
300
402
|
|
|
@@ -308,37 +410,30 @@ class SensorHoles:
|
|
|
308
410
|
|
|
309
411
|
return nchan
|
|
310
412
|
|
|
311
|
-
def get_sensor_holes(self):
|
|
312
|
-
"""Return holes sensor.
|
|
313
|
-
|
|
314
|
-
Return a list of [sensor, hybrid, segment, ichan, width]
|
|
315
|
-
"""
|
|
316
|
-
holes = []
|
|
317
|
-
for hyb in self.hybrids:
|
|
318
|
-
H = hyb.get_sensor_holes()
|
|
319
|
-
for _, ih, isegment, ichan, width in H:
|
|
320
|
-
holes.append([self.id, ih, isegment, ichan, width])
|
|
321
|
-
|
|
322
|
-
return holes
|
|
323
|
-
|
|
324
413
|
|
|
325
414
|
class ModuleHoles:
|
|
326
415
|
"""Holes in Modules."""
|
|
327
416
|
|
|
328
|
-
def __init__(self, param):
|
|
417
|
+
def __init__(self, param, win=None):
|
|
329
418
|
"""Initialization.
|
|
330
419
|
|
|
331
420
|
Args:
|
|
332
421
|
param: module wirebond params
|
|
333
422
|
"""
|
|
423
|
+
self.win = win
|
|
334
424
|
self.nsensor = len(param)
|
|
335
425
|
self.nchan = 0
|
|
336
426
|
self.sensors = []
|
|
337
427
|
for i, P in enumerate(param):
|
|
338
|
-
S = SensorHoles(P, sid=i)
|
|
428
|
+
S = SensorHoles(P, sid=i, win=win)
|
|
339
429
|
self.sensors.append(S)
|
|
340
430
|
self.nchan += S.nchan
|
|
341
431
|
|
|
432
|
+
def ready(self):
|
|
433
|
+
"""Call when all channels are in."""
|
|
434
|
+
for S in self.sensors:
|
|
435
|
+
S.ready()
|
|
436
|
+
|
|
342
437
|
def get_max_consecutive(self):
|
|
343
438
|
"""Max number of consecutive unconnected channels.
|
|
344
439
|
|
|
@@ -348,22 +443,21 @@ class ModuleHoles:
|
|
|
348
443
|
for S in self.sensors:
|
|
349
444
|
mxW = S.get_max_consecutive()
|
|
350
445
|
for j in range(4):
|
|
351
|
-
|
|
352
|
-
mx_width[j] = mxW[j]
|
|
446
|
+
mx_width[j] = max(mx_width[j], mxW[j])
|
|
353
447
|
|
|
354
448
|
return mx_width
|
|
355
449
|
|
|
356
|
-
def
|
|
357
|
-
"""
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
"""
|
|
361
|
-
holes = []
|
|
450
|
+
def get_max_sensor_consecutive(self):
|
|
451
|
+
"""The maximum number of consecutive channels per strip row."""
|
|
452
|
+
mx_width = -1
|
|
453
|
+
module_holes = []
|
|
362
454
|
for S in self.sensors:
|
|
363
|
-
|
|
364
|
-
|
|
455
|
+
width, holes = S.get_max_sensor_consecutive()
|
|
456
|
+
module_holes.extend(holes)
|
|
457
|
+
mx_width = max(mx_width, width)
|
|
458
|
+
|
|
459
|
+
return mx_width, module_holes
|
|
365
460
|
|
|
366
|
-
return holes
|
|
367
461
|
|
|
368
462
|
def get_n_unconnected(self) -> list:
|
|
369
463
|
"""Count number of unconnected channels.
|
|
@@ -422,12 +516,14 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
422
516
|
self.pdb = None
|
|
423
517
|
self.models = {}
|
|
424
518
|
self.holes = {}
|
|
425
|
-
self.institute = "
|
|
519
|
+
self.institute = self.pdb_user["institutions"][0]["code"]
|
|
426
520
|
self.inst_combo = None
|
|
427
521
|
self.module_SN = None
|
|
428
522
|
self.alternativeID = None
|
|
429
523
|
self.combo = None
|
|
430
524
|
self.tree = None
|
|
525
|
+
self.lut = {}
|
|
526
|
+
self.module_type = None
|
|
431
527
|
self.init_window()
|
|
432
528
|
|
|
433
529
|
def init_window(self):
|
|
@@ -464,15 +560,31 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
464
560
|
grid = Gtk.Grid(column_spacing=5, row_spacing=1)
|
|
465
561
|
|
|
466
562
|
# The shipment receiver
|
|
467
|
-
institute = self.create_institute_combo()
|
|
563
|
+
institute = self.create_institute_combo(True)
|
|
468
564
|
institute.connect("changed", self.on_institute)
|
|
469
565
|
institute.set_tooltip_text("Select the Institute.")
|
|
470
566
|
dbGtkUtils.set_combo_iter(institute, self.institute)
|
|
471
|
-
|
|
567
|
+
|
|
568
|
+
lbl = Gtk.Label(label="Institute")
|
|
569
|
+
lbl.set_xalign(0)
|
|
570
|
+
grid.attach(lbl, 0, 0, 1, 1)
|
|
472
571
|
grid.attach(institute, 1, 0, 1, 1)
|
|
473
572
|
self.inst_combo = institute
|
|
474
573
|
|
|
475
574
|
|
|
575
|
+
|
|
576
|
+
# The lookup table
|
|
577
|
+
lbl = Gtk.Label(label="Lookup Table")
|
|
578
|
+
lbl.set_xalign(0)
|
|
579
|
+
grid.attach(lbl, 2, 0, 1, 1)
|
|
580
|
+
|
|
581
|
+
self.testF = Gtk.FileChooserButton()
|
|
582
|
+
self.testF.set_tooltip_text("Click to select Lookup table.")
|
|
583
|
+
|
|
584
|
+
grid.attach(self.testF, 3, 0, 1, 1)
|
|
585
|
+
self.testF.connect("file-set", self.on_lut)
|
|
586
|
+
|
|
587
|
+
|
|
476
588
|
for i, tit in enumerate(["Operator", "Bond Machine", "Wire Batch", "SN", "Date"]):
|
|
477
589
|
lbl = Gtk.Label(label=tit)
|
|
478
590
|
lbl.set_xalign(0)
|
|
@@ -514,7 +626,7 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
514
626
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
515
627
|
self.mainBox.pack_start(box, False, False, 0)
|
|
516
628
|
dbGtkUtils.add_button_to_container(box, "Add Bond",
|
|
517
|
-
"Click to add a new bond.",
|
|
629
|
+
"Click to add a new bond or bond range.",
|
|
518
630
|
self.add_item)
|
|
519
631
|
|
|
520
632
|
dbGtkUtils.add_button_to_container(box, "Remove Bond",
|
|
@@ -527,6 +639,98 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
527
639
|
self.mainBox.pack_start(self.message_panel.frame, True, True, 0)
|
|
528
640
|
self.write_message("wirebond GUI\n")
|
|
529
641
|
|
|
642
|
+
def on_lut(self, fdlg):
|
|
643
|
+
"""Get look-up table."""
|
|
644
|
+
fnam = Path(fdlg.get_filename())
|
|
645
|
+
if not fnam.exists():
|
|
646
|
+
dbGtkUtils.complain("Cannot open Luukup Table.",
|
|
647
|
+
"File {} does not exist.".format(fnam))
|
|
648
|
+
return
|
|
649
|
+
|
|
650
|
+
lut = {}
|
|
651
|
+
module_map = {}
|
|
652
|
+
section = None
|
|
653
|
+
i_local = 0
|
|
654
|
+
i_std = 1
|
|
655
|
+
indx = ["local", "standard"]
|
|
656
|
+
found_format = False
|
|
657
|
+
with open(fnam, 'r', encoding="UTF-8") as fin:
|
|
658
|
+
for line in fin:
|
|
659
|
+
line = line.strip()
|
|
660
|
+
|
|
661
|
+
# Remove comments.
|
|
662
|
+
ipos = line.find('#')
|
|
663
|
+
jpos = line.find("#!")
|
|
664
|
+
if jpos>=0 and ipos==jpos:
|
|
665
|
+
if found_format:
|
|
666
|
+
dbGtkUtils.complain("A second format line was found.",
|
|
667
|
+
"Onely one is allowed. stopr map parsing.")
|
|
668
|
+
return
|
|
669
|
+
|
|
670
|
+
indx = [x.lower() for x in line[ipos+2:].split()]
|
|
671
|
+
try:
|
|
672
|
+
i_local = indx.index("local")
|
|
673
|
+
i_std = indx.index("standard")
|
|
674
|
+
found_format = True
|
|
675
|
+
except ValueError:
|
|
676
|
+
dbGtkUtils.complain("Wrong format desciption string.",
|
|
677
|
+
"The words 'local' and 'standard' should be there.\n{}".format(line))
|
|
678
|
+
return
|
|
679
|
+
|
|
680
|
+
continue
|
|
681
|
+
|
|
682
|
+
if ipos >= 0:
|
|
683
|
+
line = line[:ipos].strip()
|
|
684
|
+
|
|
685
|
+
if len(line) == 0:
|
|
686
|
+
continue
|
|
687
|
+
|
|
688
|
+
# Check for new section
|
|
689
|
+
ipos = line.find(':')
|
|
690
|
+
if ipos >= 0:
|
|
691
|
+
section = line[:ipos].strip().upper()
|
|
692
|
+
if section in module_map:
|
|
693
|
+
dbGtkUtils.complain("Section {} already in map.".format(section), "Stop parsing bond Lookup table.")
|
|
694
|
+
return
|
|
695
|
+
|
|
696
|
+
lut = {}
|
|
697
|
+
module_map[section] = lut
|
|
698
|
+
continue
|
|
699
|
+
|
|
700
|
+
if section is None:
|
|
701
|
+
continue
|
|
702
|
+
|
|
703
|
+
values = line.split()
|
|
704
|
+
if len(values)!=len(indx):
|
|
705
|
+
dbGtkUtils.complain("Cannot read Lookup table.", "Wrong line format: {}".format(line))
|
|
706
|
+
return
|
|
707
|
+
|
|
708
|
+
v_local = range_to_list(values[i_local])
|
|
709
|
+
v_std = range_to_list(values[i_std])
|
|
710
|
+
|
|
711
|
+
if len(v_local) != len(v_std):
|
|
712
|
+
dbGtkUtils.complain("Wrong Lookup table.",
|
|
713
|
+
"Ranges have different length: {}".format(line))
|
|
714
|
+
return
|
|
715
|
+
|
|
716
|
+
for L, S in zip(v_local, v_std):
|
|
717
|
+
lut[L] = S
|
|
718
|
+
|
|
719
|
+
self.lut = module_map
|
|
720
|
+
|
|
721
|
+
def convert_channel(self, C):
|
|
722
|
+
"""Convert channel according to LUT
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
C (str): channel number
|
|
726
|
+
|
|
727
|
+
"""
|
|
728
|
+
try:
|
|
729
|
+
return self.lut[self.module_type][C]
|
|
730
|
+
|
|
731
|
+
except KeyError:
|
|
732
|
+
return C
|
|
733
|
+
|
|
530
734
|
def on_institute(self, combo):
|
|
531
735
|
"""Institute changed."""
|
|
532
736
|
name = self.get_institute_from_combo(combo)
|
|
@@ -536,13 +740,20 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
536
740
|
def on_SN_changed(self, entry, value):
|
|
537
741
|
"""New SN given. Ask in PDB,"""
|
|
538
742
|
if len(value) <= 0:
|
|
539
|
-
return
|
|
540
|
-
|
|
743
|
+
return
|
|
541
744
|
|
|
542
745
|
obj = itkdb_gtk.ITkDButils.get_DB_component(self.session, value)
|
|
543
|
-
if obj is not None:
|
|
746
|
+
if obj is not None and obj["serialNumber"] is not None:
|
|
544
747
|
entry.set_text(obj["serialNumber"])
|
|
545
|
-
|
|
748
|
+
alternativeID = obj["alternativeIdentifier"]
|
|
749
|
+
module_SN = obj["serialNumber"]
|
|
750
|
+
if len(module_SN) == 14 and module_SN.startswith("20USEM"):
|
|
751
|
+
self.module_SN = module_SN
|
|
752
|
+
self.alternativeID = alternativeID
|
|
753
|
+
self.module_type = module_SN[5:7]
|
|
754
|
+
|
|
755
|
+
else:
|
|
756
|
+
itkdb_gtk.dbGtkUtils.complain("Invalid SN: {}".format(module_SN), "Not a Ring module")
|
|
546
757
|
|
|
547
758
|
else:
|
|
548
759
|
itkdb_gtk.dbGtkUtils.complain("Invalid SN", value)
|
|
@@ -556,7 +767,7 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
556
767
|
view_model = self.models[param]
|
|
557
768
|
self.tree.set_model(view_model)
|
|
558
769
|
else:
|
|
559
|
-
self.write_message("Cannot find model for {}".format(param))
|
|
770
|
+
self.write_message("Cannot find model for {}\n".format(param))
|
|
560
771
|
|
|
561
772
|
def create_combo(self):
|
|
562
773
|
"""Create the combo."""
|
|
@@ -620,8 +831,9 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
620
831
|
def channel_edited(self, widget, path, text):
|
|
621
832
|
"""Handles edition in channel number cell."""
|
|
622
833
|
if not text.isnumeric():
|
|
623
|
-
|
|
624
|
-
|
|
834
|
+
if valid_channel.match(text) is None:
|
|
835
|
+
dbGtkUtils.complain("Wrong channel number", "Invalid channel number: {}".format(text))
|
|
836
|
+
return
|
|
625
837
|
|
|
626
838
|
self.text_edited(0, path, text)
|
|
627
839
|
|
|
@@ -699,16 +911,72 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
699
911
|
self.hide()
|
|
700
912
|
self.destroy()
|
|
701
913
|
|
|
702
|
-
def
|
|
914
|
+
def compute_repaired(self, skeleton):
|
|
915
|
+
"""Compute number of repaired."""
|
|
916
|
+
nrepaired = 0
|
|
917
|
+
for key, values in skeleton["results"].items():
|
|
918
|
+
if key.find("REPAIRED")<0 or key.find("ROW")<0:
|
|
919
|
+
continue
|
|
920
|
+
|
|
921
|
+
nrepaired += len(values)
|
|
922
|
+
|
|
923
|
+
if nrepaired>0:
|
|
924
|
+
skeleton["problems"] = True
|
|
925
|
+
skeleton["comments"].append("Number of repaired FE bonds: {}".format(nrepaired))
|
|
926
|
+
|
|
927
|
+
return nrepaired
|
|
928
|
+
|
|
929
|
+
def compute_hybrid_to_pb(self, skeleton):
|
|
930
|
+
"""Compute number of failures and repairs."""
|
|
931
|
+
n = count_items(skeleton["results"]["REPAIRED_HYBRID_TO_PB"])
|
|
932
|
+
n = count_items(skeleton["results"]["FAILED_HYBRID_TO_PB"])
|
|
933
|
+
if n:
|
|
934
|
+
msg = "Hybrid to PB: {} failing bonds.".format(n)
|
|
935
|
+
skeleton["comments"].append(msg)
|
|
936
|
+
skeleton["passed"] = False
|
|
937
|
+
self.write_message("{}\n".format(msg))
|
|
938
|
+
for key in skeleton["results"]["FAILED_HYBRID_TO_PB"]:
|
|
939
|
+
defect = {
|
|
940
|
+
"description": "Unbonded: Hybrid to PB",
|
|
941
|
+
"name": "UNBODED",
|
|
942
|
+
"properties": {
|
|
943
|
+
"wire_number": range_to_list(key)
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
skeleton["defects"].append(defect)
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def compute_module_to_frame(self, skeleton):
|
|
950
|
+
"""Compute number of failures and repairs."""
|
|
951
|
+
n = count_items(skeleton["results"]["REPAIRED_MODULE_TO_FRAME"])
|
|
952
|
+
n = count_items(skeleton["results"]["FAILED_MODULE_TO_FRAME"])
|
|
953
|
+
if n:
|
|
954
|
+
msg = "Module to test frame: {} failing bonds.".format(n)
|
|
955
|
+
skeleton["comments"].append(msg)
|
|
956
|
+
skeleton["passed"] = False
|
|
957
|
+
self.write_message("{}\n".format(msg))
|
|
958
|
+
for key in skeleton["results"]["FAILED_MODULE_TO_FRAME"]:
|
|
959
|
+
defect = {
|
|
960
|
+
"description": "Unbonded: Module to Frame",
|
|
961
|
+
"name": "UNBODED",
|
|
962
|
+
"properties": {
|
|
963
|
+
"wire_number": range_to_list(key)
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
skeleton["defects"].append(defect)
|
|
967
|
+
|
|
968
|
+
def compute_unconnected(self, results):
|
|
703
969
|
"""Compute number of unconnected."""
|
|
704
970
|
try:
|
|
705
971
|
param = get_module_param(self.SN.get_text())
|
|
706
972
|
except ValueError as E:
|
|
707
973
|
dbGtkUtils.complain("Wrong SN number", str(E))
|
|
974
|
+
return None
|
|
708
975
|
|
|
709
|
-
M = ModuleHoles(param=param)
|
|
976
|
+
M = ModuleHoles(param=param, win=self)
|
|
710
977
|
|
|
711
|
-
|
|
978
|
+
defects = []
|
|
979
|
+
for test, values in results.items():
|
|
712
980
|
if test.find("FAILED") < 0:
|
|
713
981
|
continue
|
|
714
982
|
if test.find("ROW") < 0:
|
|
@@ -717,56 +985,86 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
717
985
|
irow = int(test[-1]) - 1
|
|
718
986
|
|
|
719
987
|
# Get list of all channels with wirebond notation.
|
|
720
|
-
|
|
721
|
-
it = model.get_iter_first()
|
|
722
|
-
out = []
|
|
723
|
-
while it:
|
|
724
|
-
chan, _ = model[it]
|
|
725
|
-
if len(chan) > 0:
|
|
726
|
-
out.append(int(chan))
|
|
727
|
-
|
|
728
|
-
it = model.iter_next(it)
|
|
729
|
-
|
|
988
|
+
out = [int(x) for x in values.keys()]
|
|
730
989
|
# Translate to sensor, hybrids, etc.
|
|
731
990
|
for S in M.sensors:
|
|
732
991
|
for H in S.hybrids:
|
|
733
992
|
H.add_channel(irow, out)
|
|
734
993
|
H.holes[irow] = find_holes(H.channels[irow])
|
|
735
994
|
|
|
995
|
+
# add defects
|
|
996
|
+
defects.append(
|
|
997
|
+
{
|
|
998
|
+
"description": "Unbonded Channel",
|
|
999
|
+
"name": "UNBONDED",
|
|
1000
|
+
"properties": {
|
|
1001
|
+
"front_end_row": irow,
|
|
1002
|
+
"wire_number": out
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
736
1006
|
|
|
737
1007
|
# Now get sensor strips.
|
|
1008
|
+
M.ready()
|
|
738
1009
|
unconnected = M.get_n_unconnected()
|
|
739
1010
|
mx_consecutive = M.get_max_consecutive()
|
|
740
|
-
module_holes = M.
|
|
1011
|
+
mx_sensor_width, module_holes = M.get_max_sensor_consecutive()
|
|
741
1012
|
|
|
742
1013
|
out = {}
|
|
1014
|
+
out["comments"] = []
|
|
1015
|
+
out["defects"] = defects
|
|
743
1016
|
for irow in range(4):
|
|
744
1017
|
key = "MAX_CONT_UNCON_ROW{}".format(irow+1)
|
|
745
1018
|
out[key] = mx_consecutive[irow]
|
|
746
1019
|
|
|
747
|
-
mxW = 0
|
|
748
1020
|
self.write_message("Found {} clusters of unconnected strips in sensor.\n".format(len(module_holes)))
|
|
749
1021
|
for H in module_holes:
|
|
750
|
-
self.write_message("{}\n".format(
|
|
751
|
-
if H[-1] > mxW:
|
|
752
|
-
mxW = H[-1]
|
|
1022
|
+
self.write_message("{}\n".format(H))
|
|
753
1023
|
|
|
754
|
-
if
|
|
755
|
-
self.write_message("Max width: {}". format(
|
|
1024
|
+
if mx_sensor_width > 0:
|
|
1025
|
+
self.write_message("Max width of consecutive unconnected strips: {}\n". format(mx_sensor_width))
|
|
1026
|
+
|
|
1027
|
+
out["MAX_UNCON_SENSOR_CHAN"] = mx_sensor_width
|
|
1028
|
+
if mx_sensor_width > 8:
|
|
1029
|
+
out["passed"] = False
|
|
1030
|
+
out["comments"].append("Too many consecutive sensor strips unconnected: {}".format(mx_sensor_width))
|
|
756
1031
|
|
|
757
|
-
out["MAX_UNCON_SENSOR_CHAN"] = mxW
|
|
758
1032
|
nstrips = 0
|
|
759
1033
|
for v in unconnected:
|
|
760
1034
|
nstrips += v
|
|
761
1035
|
|
|
762
|
-
|
|
1036
|
+
percent = 100*nstrips/M.nchan
|
|
1037
|
+
out["TOTAL_PERC_UNCON_SENSOR_CHAN"] = percent
|
|
1038
|
+
if out["TOTAL_PERC_UNCON_SENSOR_CHAN"] > 1.0:
|
|
1039
|
+
out["passed"] = False
|
|
1040
|
+
out["comments"].append("More than 1%% of channels unconnected: {:.1f}%%".format(percent))
|
|
763
1041
|
|
|
764
1042
|
return out
|
|
765
1043
|
|
|
766
1044
|
def get_unconnected(self, skeleton):
|
|
767
1045
|
"""Fill the test DTO with unconnected information."""
|
|
768
|
-
out = self.compute_unconnected()
|
|
1046
|
+
out = self.compute_unconnected(skeleton["results"])
|
|
1047
|
+
if out is None:
|
|
1048
|
+
raise ValueError("Wrong SN")
|
|
1049
|
+
|
|
769
1050
|
for key, val in out.items():
|
|
1051
|
+
if key in ["passed", "problems"]:
|
|
1052
|
+
skeleton[key] = out[key]
|
|
1053
|
+
continue
|
|
1054
|
+
|
|
1055
|
+
if key == "comments":
|
|
1056
|
+
for C in out[key]:
|
|
1057
|
+
skeleton[key].append(C)
|
|
1058
|
+
|
|
1059
|
+
continue
|
|
1060
|
+
|
|
1061
|
+
if key == "defects":
|
|
1062
|
+
for D in out[key]:
|
|
1063
|
+
skeleton[key].append(D)
|
|
1064
|
+
|
|
1065
|
+
continue
|
|
1066
|
+
|
|
1067
|
+
|
|
770
1068
|
skeleton["results"][key] = val
|
|
771
1069
|
|
|
772
1070
|
def read_file(self, *args):
|
|
@@ -803,7 +1101,7 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
803
1101
|
try:
|
|
804
1102
|
dbGtkUtils.set_combo_iter(self.inst_combo, data["institution"])
|
|
805
1103
|
except KeyError:
|
|
806
|
-
self.write_message("institution value is not in the loaded file.")
|
|
1104
|
+
self.write_message("institution value is not in the loaded file\n.")
|
|
807
1105
|
|
|
808
1106
|
self.operator.set_text(data["properties"]["OPERATOR"])
|
|
809
1107
|
self.machine.set_text(data["properties"]["BOND_MACHINE"])
|
|
@@ -832,6 +1130,43 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
832
1130
|
|
|
833
1131
|
data["results"][key] = out
|
|
834
1132
|
|
|
1133
|
+
def fix_list_of_channels(self, data):
|
|
1134
|
+
"""Expand ranges and, eventually, apply LUT.
|
|
1135
|
+
|
|
1136
|
+
Args:
|
|
1137
|
+
data: The test payload.
|
|
1138
|
+
"""
|
|
1139
|
+
for tit, section in data["results"].items():
|
|
1140
|
+
if not isinstance(section, dict):
|
|
1141
|
+
continue
|
|
1142
|
+
|
|
1143
|
+
range_items = []
|
|
1144
|
+
added_items = []
|
|
1145
|
+
for key, comment in section.items():
|
|
1146
|
+
values = list(map(str.strip, key.split(',')))
|
|
1147
|
+
if ',' in key or '-' in key:
|
|
1148
|
+
range_items.append(key)
|
|
1149
|
+
|
|
1150
|
+
else:
|
|
1151
|
+
continue
|
|
1152
|
+
|
|
1153
|
+
for V in values:
|
|
1154
|
+
for i in range_to_list(V):
|
|
1155
|
+
added_items.append((str(i), comment))
|
|
1156
|
+
|
|
1157
|
+
for key in range_items:
|
|
1158
|
+
section.pop(key)
|
|
1159
|
+
|
|
1160
|
+
for key, comm in added_items:
|
|
1161
|
+
section[key] = comm
|
|
1162
|
+
|
|
1163
|
+
if len(self.lut)>0 and len(section)>0:
|
|
1164
|
+
tmp = copy.deepcopy(section)
|
|
1165
|
+
section.clear()
|
|
1166
|
+
for key, val in tmp.items():
|
|
1167
|
+
section[self.convert_channel(key)] = val
|
|
1168
|
+
|
|
1169
|
+
|
|
835
1170
|
def save_test(self, *args):
|
|
836
1171
|
"""Save Test file."""
|
|
837
1172
|
dialog = Gtk.FileChooserDialog(
|
|
@@ -863,6 +1198,7 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
863
1198
|
data = {
|
|
864
1199
|
"institution": self.institute,
|
|
865
1200
|
"date": ITkDButils.get_db_date(),
|
|
1201
|
+
"runNumber": "1",
|
|
866
1202
|
"properties": {},
|
|
867
1203
|
"results": {},
|
|
868
1204
|
"comments": [],
|
|
@@ -892,7 +1228,7 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
892
1228
|
if len(values) == 4:
|
|
893
1229
|
SN, operator, machine = values
|
|
894
1230
|
else:
|
|
895
|
-
self.write_message("Something went wrong while requesting missing information
|
|
1231
|
+
self.write_message("Something went wrong while requesting missing information.\n")
|
|
896
1232
|
|
|
897
1233
|
data["component"] = SN
|
|
898
1234
|
data["properties"]["OPERATOR"] = operator
|
|
@@ -927,7 +1263,15 @@ class WireBond(dbGtkUtils.ITkDBWindow):
|
|
|
927
1263
|
|
|
928
1264
|
self.get_test_header(skeleton)
|
|
929
1265
|
self.get_list_of_channels(skeleton)
|
|
930
|
-
self.
|
|
1266
|
+
self.fix_list_of_channels(skeleton)
|
|
1267
|
+
try:
|
|
1268
|
+
self.get_unconnected(skeleton)
|
|
1269
|
+
self.compute_repaired(skeleton)
|
|
1270
|
+
self.compute_hybrid_to_pb(skeleton)
|
|
1271
|
+
self.compute_module_to_frame(skeleton)
|
|
1272
|
+
|
|
1273
|
+
except ValueError:
|
|
1274
|
+
return
|
|
931
1275
|
|
|
932
1276
|
uploadW = UploadTest.UploadTest(self.session, payload=skeleton)
|
|
933
1277
|
# uploadW.run()
|