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/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
- def wire2strip(mod_par, irow, iwire):
88
- """Convert from wirebond index to strip_number."""
89
- for sensor in mod_par:
90
- for hyb in sensor:
91
- rng = hyb[irow]
92
- if iwire>= rng[0] and iwire<=rng[1]:
93
- if irow % 2:
94
- ichan = 2*(iwire-rng[0]) + 1
95
- else:
96
- ichan = 2*(iwire-rng[0])
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
- return ichan
155
+ return out
99
156
 
100
- return None
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 bomds.
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: rown number
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 ich >= first_chan and ich <= last_chan:
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
- def get_n_unconnected(self):
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 get_max_consecutive(self):
212
- """Returns the largest 'hole'."""
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
- for row in self.holes:
216
- mxW = -1
217
- for h in row:
218
- if h[1] > mxW:
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
- Return a list of [sensor, hybrid, segment, ichan, width]
232
- """
233
- holes = []
234
- channels = [[], []]
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, 0, 0, 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 range(4):
293
- if mxW[j] > mx_width[j]:
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
- if mxW[j] > mx_width[j]:
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 get_sensor_holes(self) -> list:
357
- """Return. holesin sensor strips.
358
-
359
- Return a list of [sensor, hybrid, segment, ichan, width]
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
- for _, ihyb, isegment, ichan, width in S.get_sensor_holes():
364
- holes.append([S.id, ihyb, isegment, ichan, width])
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 = "IFIC"
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
- grid.attach(Gtk.Label(label="Institute"), 0, 0, 1, 1)
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 None
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
- self.alternativeID = obj["alternativeIdentifier"]
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
- dbGtkUtils.complain("Wrong channel number", "Invalid channel number: {}".format(text))
624
- return
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 compute_unconnected(self):
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
- for test in test_parameters.values():
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
- model = self.models[test]
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.get_sensor_holes()
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(str(H)))
751
- if H[-1] > mxW:
752
- mxW = H[-1]
1022
+ self.write_message("{}\n".format(H))
753
1023
 
754
- if mxW > 0:
755
- self.write_message("Max width: {}". format(mxW))
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
- out["TOTAL_PERC_UNCON_SENSOR_CHAN"] = nstrips/M.nchan
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.get_unconnected(skeleton)
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()