itkdb-gtk 0.0.12__py3-none-any.whl → 0.0.16__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.

@@ -0,0 +1,863 @@
1
+ #!/usr/bin/env python3
2
+ """Wirebonding GUI for PSB."""
3
+
4
+ import sys
5
+ import json
6
+ import gi
7
+
8
+ gi.require_version("Gtk", "3.0")
9
+ from gi.repository import Gtk, Gio, GLib
10
+
11
+ try:
12
+ import itkdb_gtk
13
+
14
+ except ImportError:
15
+ from pathlib import Path
16
+ cwd = Path(sys.argv[0]).parent.parent
17
+ sys.path.append(cwd.as_posix())
18
+
19
+ from itkdb_gtk import dbGtkUtils
20
+ from itkdb_gtk import ITkDBlogin, ITkDButils, UploadTest
21
+
22
+ test_parameters = {
23
+ "Repaired Row 1": "REPAIRED_FRONTEND_ROW1",
24
+ "Failed Row 1": "FAILED_FRONTEND_ROW1",
25
+ "Repaired Row 2": "REPAIRED_FRONTEND_ROW2",
26
+ "Failed Row 2": "FAILED_FRONTEND_ROW2",
27
+ "Repaired Row 3": "REPAIRED_FRONTEND_ROW3",
28
+ "Failed Row 3": "FAILED_FRONTEND_ROW3",
29
+ "Repaired Row 4": "REPAIRED_FRONTEND_ROW4",
30
+ "Failed Row 4": "FAILED_FRONTEND_ROW4",
31
+ "Repaired Hyb->PB": "REPAIRED_HYBRID_TO_PB",
32
+ "Failed HyB->PB": "FAILED_HYBRID_TO_PB",
33
+ "Repaired Module->Frame": "REPAIRED_MODULE_TO_FRAME",
34
+ "Failed Module->Frame": "FAILED_MODULE_TO_FRAME"
35
+ }
36
+
37
+ # module_param[iring][i_sensor][i_hybrid][i_row]
38
+ module_param = {
39
+ 0: [
40
+ [
41
+ [[255, 830], [1343, 1918], [2431, 3006], [3519, 4094]],
42
+ [[831, 1342], [1919, 2430], [3007, 3518], [4095, 4606]],
43
+ ]
44
+ ],
45
+ 1: [
46
+ [
47
+ [[271, 974], [1615, 2318], [2959, 3662], [4303, 5006]],
48
+ [[975, 1614], [2319, 2958], [3663, 4302], [5007, 5646]],
49
+ ]
50
+ ],
51
+ 2: [
52
+ [
53
+ [[201, 968], [969, 1736], [1737, 2504], [2505, 3272]]
54
+ ]
55
+ ],
56
+ 3: [
57
+ [
58
+ [[566, 1013], [2358, 2805], [4150, 4597], [5942, 6389]],
59
+ [[1462, 1909], [3254, 3701], [5046, 5493], [6838, 7285]]
60
+ ],
61
+ [
62
+ [[1014, 1461], [2806, 3253], [4598, 5045], [6390, 6837]],
63
+ [[1910, 2357], [3702, 4149], [5494, 5941], [7286, 7733]]
64
+ ]
65
+ ],
66
+ 4: [
67
+ [
68
+ [[318, 829], [1342, 1853], [2366, 2877], [3390, 3901]]
69
+ ],
70
+ [
71
+ [[830, 1341], [1854, 2365], [2878, 3389], [3902, 4413]]
72
+ ]
73
+ ],
74
+ 5: [
75
+ [
76
+ [[332, 907], [1484, 2059], [2636, 3211], [3788, 4363]]
77
+ ],
78
+ [
79
+ [[908, 1483], [2060, 2635], [3212, 3787], [4364, 4939]]
80
+ ]
81
+ ],
82
+ }
83
+
84
+ def wire2strip(mod_par, irow, iwire):
85
+ """Convert from wirebond index to strip_number."""
86
+ for sensor in mod_par:
87
+ for hyb in sensor:
88
+ rng = hyb[irow]
89
+ if iwire>= rng[0] and iwire<=rng[1]:
90
+ if irow % 2:
91
+ ichan = 2*(iwire-rng[0]) + 1
92
+ else:
93
+ ichan = 2*(iwire-rng[0])
94
+
95
+ return ichan
96
+
97
+ return None
98
+
99
+
100
+ def find_holes(chan_list, min_chan=0, max_chan=999999):
101
+ """Find groups of consecutive channels."""
102
+ out = sorted(chan_list)
103
+ nchan = 0
104
+ last_chan = -1
105
+ ichan = -1
106
+ holes = []
107
+ for chan in out:
108
+ if chan < min_chan or chan > max_chan:
109
+ continue
110
+
111
+ if last_chan < 0:
112
+ last_chan = chan
113
+ continue
114
+
115
+ if chan - last_chan > 1:
116
+ if nchan:
117
+ holes.append([ichan, nchan])
118
+ nchan = 0
119
+ ichan = -1
120
+ else:
121
+ if last_chan < max_chan and chan >= max_chan:
122
+ # WE are in another sensor
123
+ holes.append([ichan, nchan])
124
+ nchan = 0
125
+ ichan = -1
126
+ last_chan = chan
127
+ continue
128
+
129
+ nchan += 1
130
+ if ichan < 0:
131
+ ichan = last_chan
132
+ nchan += 1
133
+
134
+ last_chan = chan
135
+
136
+ if nchan:
137
+ holes.append([ichan, nchan])
138
+
139
+ return holes
140
+
141
+
142
+ class HybridHoles:
143
+ """Holes in hybrid bomds.
144
+
145
+ Holes are defined by a list [first_chan, n_chan].
146
+ """
147
+
148
+ def __init__(self, param, hid=0):
149
+ """Initialization.
150
+
151
+ Args:
152
+ param: Hybrid wirebon parameters.
153
+
154
+ """
155
+ self.id = hid
156
+ self.param = param
157
+ self.nchan = 0
158
+ for min_chan, max_chan in param:
159
+ self.nchan += (max_chan-min_chan+1)
160
+
161
+ self.holes = [[] for irow in range(4)]
162
+ self.channels = [[] for irow in range(4)]
163
+
164
+ def add_channel(self, irow, ichan)->bool:
165
+ """Add a new channel in row.
166
+
167
+ Args:
168
+ irow: rown number
169
+ ichan: channel number
170
+
171
+ Return:
172
+ True if added, False otherwise.
173
+ """
174
+ first_chan = self.param[irow][0]
175
+ last_chan = self.param[irow][1]
176
+ if isinstance(ichan, list) or isinstance(ichan, tuple):
177
+ nadded = 0
178
+ for ich in ichan:
179
+ if ich >= first_chan and ich <= last_chan:
180
+ self.channels[irow].append(ich)
181
+ nadded += 1
182
+
183
+ self.channels[irow] = sorted(self.channels[irow])
184
+ return nadded>0
185
+ else:
186
+ if ichan >= first_chan and ichan <= last_chan:
187
+ self.channels[irow].append(ichan)
188
+ return True
189
+ else:
190
+ return False
191
+
192
+ def get_n_unconnected(self):
193
+ """Count number of unconnected channels.
194
+
195
+ Return a list, one item per row.
196
+ """
197
+ nchan = []
198
+ for row in self.holes:
199
+ nch = 0
200
+ for h in row:
201
+ nch += h[1]
202
+
203
+ nchan.append(nch)
204
+
205
+ return nchan
206
+
207
+ def get_max_consecutive(self):
208
+ """Returns the largest 'hole'."""
209
+ mx_width = []
210
+
211
+ for row in self.holes:
212
+ mxW = -1
213
+ for h in row:
214
+ if h[1] > mxW:
215
+ mxW = h[1]
216
+
217
+ mx_width.append(mxW)
218
+
219
+ return mx_width
220
+
221
+ def get_sensor_holes(self):
222
+ """Compute holes in 'sensor' strips.
223
+
224
+ Each hybrid has 2 sensor segments corresponding to
225
+ rows (1,2) and (3, 4).
226
+
227
+ Return a list of [sensor, hybrid, segment, ichan, width]
228
+ """
229
+ holes = []
230
+ channels = [[], []]
231
+ for irow, row in enumerate(self.channels):
232
+ isegment = int(irow/2)
233
+ for ich in row:
234
+ rng = self.param[irow]
235
+ if irow % 2:
236
+ chan = 2*(ich-rng[0]) + 1
237
+ else:
238
+ chan = 2*(ich-rng[0])
239
+
240
+ channels[isegment].append(chan)
241
+
242
+ channels[isegment] = sorted(channels[isegment])
243
+
244
+ for isegment, S in enumerate(channels):
245
+ H = find_holes(S)
246
+ if len(H)>0:
247
+ out = [ [0, self.id, isegment, chan, width] for chan, width in H ]
248
+ holes.extend(out)
249
+
250
+ return holes
251
+
252
+
253
+ class SensorHoles:
254
+ """Holes in sensor."""
255
+
256
+ def __init__(self, param, sid=0):
257
+ """Initialization.
258
+
259
+ Args:
260
+ param: sensor wirebon params
261
+ """
262
+ self.id = sid
263
+ self.param = param
264
+ self.nchan = 0
265
+ self.nhybrid = len(param)
266
+ self.hybrids = []
267
+ for i, P in enumerate(param):
268
+ H = HybridHoles(P, hid=i)
269
+ self.hybrids.append(H)
270
+ self.nchan += H.nchan
271
+
272
+ def get_n_hyb(self):
273
+ """Return number of hybrids."""
274
+ return len(self.hybrids)
275
+
276
+ def get_hybrid(self, i):
277
+ """Return i-th hybrid."""
278
+ return self.hybrids[i]
279
+
280
+ def get_max_consecutive(self):
281
+ """Max number of consecutive unconnected channels.
282
+
283
+ This is ordered by row.
284
+ """
285
+ mx_width = [0, 0, 0, 0]
286
+ for hyb in self.hybrids:
287
+ mxW = hyb.get_max_consecutive()
288
+ for j in range(4):
289
+ if mxW[j] > mx_width[j]:
290
+ mx_width[j] = mxW[j]
291
+
292
+ return mx_width
293
+
294
+ def get_n_unconnected(self):
295
+ """Count number of unconnected channels.
296
+
297
+ Return a list, one item per row.
298
+ """
299
+ nchan = [0, 0, 0, 0]
300
+ for hyb in self.hybrids:
301
+ nc = hyb.get_n_unconnected()
302
+ for i, n in enumerate(nc):
303
+ nchan[i] += n
304
+
305
+ return nchan
306
+
307
+ def get_sensor_holes(self):
308
+ """Return holes sensor.
309
+
310
+ Return a list of [sensor, hybrid, segment, ichan, width]
311
+ """
312
+ holes = []
313
+ for hyb in self.hybrids:
314
+ H = hyb.get_sensor_holes()
315
+ for _, ih, isegment, ichan, width in H:
316
+ holes.append([self.id, ih, isegment, ichan, width])
317
+
318
+ return holes
319
+
320
+
321
+ class ModuleHoles:
322
+ """Holes in Modules."""
323
+
324
+ def __init__(self, param):
325
+ """Initialization.
326
+
327
+ Args:
328
+ param: module wirebond params
329
+ """
330
+ self.nsensor = len(param)
331
+ self.nchan = 0
332
+ self.sensors = []
333
+ for i, P in enumerate(param):
334
+ S = SensorHoles(P, sid=i)
335
+ self.sensors.append(S)
336
+ self.nchan += S.nchan
337
+
338
+ def get_max_consecutive(self):
339
+ """Max number of consecutive unconnected channels.
340
+
341
+ This is ordered by row.
342
+ """
343
+ mx_width = [-1, -1, -1, -1]
344
+ for S in self.sensors:
345
+ mxW = S.get_max_consecutive()
346
+ for j in range(4):
347
+ if mxW[j] > mx_width[j]:
348
+ mx_width[j] = mxW[j]
349
+
350
+ return mx_width
351
+
352
+ def get_sensor_holes(self) -> list:
353
+ """Return. holesin sensor strips.
354
+
355
+ Return a list of [sensor, hybrid, segment, ichan, width]
356
+ """
357
+ holes = []
358
+ for S in self.sensors:
359
+ for _, ihyb, isegment, ichan, width in S.get_sensor_holes():
360
+ holes.append([S.id, ihyb, isegment, ichan, width])
361
+
362
+ return holes
363
+
364
+ def get_n_unconnected(self) -> list:
365
+ """Count number of unconnected channels.
366
+
367
+ Return a list, one item per row.
368
+ """
369
+ nchan = [0, 0, 0, 0]
370
+ for S in self.sensors:
371
+ nc = S.get_n_unconnected()
372
+ for i, n in enumerate(nc):
373
+ nchan[i] += n
374
+
375
+ return nchan
376
+
377
+ def get_total_unconnected(self) -> int:
378
+ """Return total number of unconnected wires."""
379
+ unc = self.get_n_unconnected()
380
+ out = 0
381
+ for v in unc:
382
+ out += v
383
+
384
+ return int(out)
385
+
386
+
387
+ def get_module_param(SN):
388
+ """Get parameters of module type.
389
+
390
+ Args:
391
+ SN: Serial Number
392
+
393
+ Returns:
394
+ list: list with bond numbers.
395
+ """
396
+ if len(SN) != 14 or SN[:3]!="20U":
397
+ raise ValueError("Wrong serial number {}".format(SN))
398
+
399
+ if SN[3:5] != "SE":
400
+ raise ValueError("Cannot handle barrel modules yet.")
401
+
402
+ mod_type = SN[5:7]
403
+ if mod_type[0] != "M":
404
+ raise ValueError("Does not seem to be a RingModule: {}".format(SN))
405
+
406
+ ring = int(mod_type[-1])
407
+ param = module_param[ring]
408
+
409
+ return param
410
+
411
+
412
+ class WireBond(Gtk.Window):
413
+ """Main window."""
414
+
415
+ def __init__(self, session=None, title=""):
416
+ """Initialization."""
417
+ super().__init__(title=title)
418
+ self.pdb = None
419
+ self.session = session
420
+ self.models = {}
421
+ self.holes = {}
422
+ self.prepare_window()
423
+
424
+ def prepare_window(self):
425
+ """Creates the GUI."""
426
+ self.hb = Gtk.HeaderBar()
427
+ self.hb.set_show_close_button(True)
428
+ self.hb.props.title = "Wire Bond"
429
+ self.set_titlebar(self.hb)
430
+
431
+ # Button to upload
432
+ button = Gtk.Button()
433
+ icon = Gio.ThemedIcon(name="document-send-symbolic")
434
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
435
+ button.add(image)
436
+ button.set_tooltip_text("Click to upload test")
437
+ button.connect("clicked", self.upload_test)
438
+ self.hb.pack_end(button)
439
+
440
+ button = Gtk.Button()
441
+ icon = Gio.ThemedIcon(name="document-save-symbolic")
442
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
443
+ button.add(image)
444
+ button.set_tooltip_text("Click to save test file")
445
+ button.connect("clicked", self.save_test)
446
+ self.hb.pack_end(button)
447
+
448
+ # Button to upload
449
+ button = Gtk.Button()
450
+ icon = Gio.ThemedIcon(name="document-open-symbolic")
451
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
452
+ button.add(image)
453
+ button.set_tooltip_text("Click to read local data file.")
454
+ button.connect("clicked", self.read_file)
455
+ self.hb.pack_end(button)
456
+
457
+ # Create the main box and add it to the window
458
+ self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
459
+ self.mainBox.set_property("margin-left", 6)
460
+ self.mainBox.set_property("margin-right", 6)
461
+ self.add(self.mainBox)
462
+
463
+ # Data panel
464
+ grid = Gtk.Grid(column_spacing=5, row_spacing=1)
465
+ for i, tit in enumerate(["Operator", "Bond Machine", "Wire Batch", "SN", "Date"]):
466
+ lbl = Gtk.Label(label=tit)
467
+ lbl.set_xalign(0)
468
+ grid.attach(lbl, 0, i, 1, 1)
469
+
470
+ self.operator = dbGtkUtils.new_small_text_entry()
471
+ self.machine = dbGtkUtils.new_small_text_entry()
472
+ self.batch = dbGtkUtils.new_small_text_entry()
473
+ self.SN = dbGtkUtils.new_small_text_entry()
474
+ self.date = dbGtkUtils.TextEntry(small=True)
475
+ self.date.connect("text_changed", self.new_date)
476
+
477
+ grid.attach(self.operator, 1, 0, 1, 1)
478
+ grid.attach(self.machine, 1, 1, 1, 1)
479
+ grid.attach(self.batch, 1, 2, 1, 1)
480
+ grid.attach(self.SN, 1, 3, 1, 1)
481
+ grid.attach(self.date.widget, 1, 4, 1, 1)
482
+
483
+ self.mainBox.pack_start(grid, True, True, 0)
484
+
485
+ # Prepare combo and all the models
486
+ lbl = Gtk.Label(label="Choose section.")
487
+ lbl.set_xalign(0)
488
+ self.mainBox.pack_start(lbl, True, True, 0)
489
+
490
+ combo = self.create_combo()
491
+ self.mainBox.pack_start(combo, True, True, 0)
492
+
493
+ # The tree view
494
+ scrolled = self.create_tree_view()
495
+ self.mainBox.pack_start(scrolled, True, True, 5)
496
+
497
+ box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
498
+ self.mainBox.pack_start(box, False, False, 0)
499
+ dbGtkUtils.add_button_to_container(box, "Add Item",
500
+ "Click to add a new item.",
501
+ self.add_item)
502
+
503
+ #
504
+ # The text view and buffer
505
+ #
506
+ self.message_panel = dbGtkUtils.MessagePanel(size=100)
507
+ self.mainBox.pack_start(self.message_panel.frame, True, True, 0)
508
+ self.write_message("wirebond GUI\n")
509
+
510
+ # The button box
511
+ btnBox = Gtk.ButtonBox(orientation=Gtk.Orientation.HORIZONTAL)
512
+
513
+ btn = Gtk.Button(label="Quit")
514
+ btn.connect("clicked", self.quit)
515
+ btnBox.add(btn)
516
+
517
+ self.mainBox.pack_end(btnBox, False, True, 0)
518
+ self.show_all()
519
+
520
+ def on_name_combo_changed(self, combo):
521
+ """Change model in TreeView."""
522
+ tree_iter = combo.get_active_iter()
523
+ if tree_iter is not None:
524
+ model = combo.get_model()
525
+ param = model[tree_iter][1]
526
+ view_model = self.models[param]
527
+ self.tree.set_model(view_model)
528
+ else:
529
+ self.write_message("Cannot find model for {}".format(param))
530
+
531
+ def create_combo(self):
532
+ """Create the combo."""
533
+ model = Gtk.ListStore(str, str)
534
+ for txt, param in test_parameters.items():
535
+ model.append([txt, param])
536
+
537
+ M = Gtk.ListStore(str, str)
538
+ M.append(["", ""])
539
+ self.models[param] = M
540
+
541
+ self.combo = Gtk.ComboBox.new_with_model_and_entry(model)
542
+ self.combo.set_entry_text_column(0)
543
+ self.combo.set_active(0)
544
+ self.combo.connect("changed", self.on_name_combo_changed)
545
+ return self.combo
546
+
547
+ def create_tree_view(self, size=150):
548
+ """Creates the tree vew with the attachmens."""
549
+ tree_iter = self.combo.get_active_iter()
550
+ combo_model = self.combo.get_model()
551
+ param = combo_model[tree_iter][1]
552
+ model = self.models[param]
553
+
554
+ self.tree = Gtk.TreeView(model=model)
555
+ self.tree.connect("button-press-event", self.button_pressed)
556
+
557
+ scrolled = Gtk.ScrolledWindow()
558
+ scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
559
+ scrolled.add(self.tree)
560
+ scrolled.set_size_request(-1, size)
561
+
562
+ renderer = Gtk.CellRendererText()
563
+ renderer.set_property("editable", True)
564
+ renderer.connect("edited", self.channel_edited)
565
+ column = Gtk.TreeViewColumn("Channel", renderer, text=0)
566
+ self.tree.append_column(column)
567
+
568
+ renderer = Gtk.CellRendererText()
569
+ renderer.set_property("editable", True)
570
+ renderer.connect("edited", self.failure_edited)
571
+ column = Gtk.TreeViewColumn("Failure", renderer, text=1)
572
+ self.tree.append_column(column)
573
+
574
+ return scrolled
575
+
576
+ def text_edited(self, icol, path, text):
577
+ """Text has been edited in the TreeView"""
578
+ if len(text) == 0:
579
+ return
580
+
581
+ model = self.tree.get_model()
582
+
583
+ n_child = model.iter_n_children()
584
+ current_child = int(path)
585
+ if n_child-current_child == 1:
586
+ model.append(["", ""])
587
+
588
+ model[path][icol] = text
589
+
590
+ def channel_edited(self, widget, path, text):
591
+ """Handles edition in channel number cell."""
592
+ if not text.isnumeric():
593
+ dbGtkUtils.complain("Wrong channel number", "Invalid channel number: {}".format(text))
594
+ return
595
+
596
+ self.text_edited(0, path, text)
597
+
598
+ def failure_edited(self, widget, path, text):
599
+ """Handles edition in comment."""
600
+ self.text_edited(1, path, text)
601
+
602
+
603
+ def new_date(self, entry, value):
604
+ """new date given at input."""
605
+ d = dbGtkUtils.parse_date_as_string(value)
606
+ if d is not None:
607
+ self.date.set_text(d)
608
+
609
+ def button_pressed(self, *args):
610
+ """Button pressed."""
611
+ pass
612
+
613
+ def add_item(self, *args):
614
+ """Adds a new item in the current model."""
615
+ out = dbGtkUtils.get_a_list_of_values("Add Item", ["Channel", "Comment"])
616
+ if len(out) == 2:
617
+ model = self.tree.get_model()
618
+ model[-1] = out
619
+ model.append(["", ""])
620
+
621
+ def quit(self, *args):
622
+ """Quits the application."""
623
+ if self.pdb:
624
+ self.pdb.die()
625
+
626
+ self.hide()
627
+ self.destroy()
628
+
629
+ def write_message(self, text):
630
+ """Writes text to Text Viewer."""
631
+ self.message_panel.write_message(text)
632
+
633
+ def compute_unconnected(self):
634
+ """Compute number of unconnected."""
635
+ param = get_module_param(self.SN.get_text())
636
+ M = ModuleHoles(param=param)
637
+
638
+ for test in test_parameters.values():
639
+ if test.find("FAILED") < 0:
640
+ continue
641
+ if test.find("ROW") < 0:
642
+ continue
643
+
644
+ irow = int(test[-1]) - 1
645
+
646
+ # Get list of all channels with wirebond notation.
647
+ model = self.models[test]
648
+ it = model.get_iter_first()
649
+ out = []
650
+ while it:
651
+ chan, _ = model[it]
652
+ if len(chan) > 0:
653
+ out.append(int(chan))
654
+
655
+ it = model.iter_next(it)
656
+
657
+ # Translate to sensor, hybrids, etc.
658
+ for S in M.sensors:
659
+ for H in S.hybrids:
660
+ H.add_channel(irow, out)
661
+ H.holes[irow] = find_holes(H.channels[irow])
662
+
663
+
664
+ # Now get sensor strips.
665
+ unconnected = M.get_n_unconnected()
666
+ mx_consecutive = M.get_max_consecutive()
667
+ module_holes = M.get_sensor_holes()
668
+
669
+ out = {}
670
+ for irow in range(4):
671
+ key = "MAX_CONT_UNCON_ROW{}".format(irow+1)
672
+ out[key] = mx_consecutive[irow]
673
+
674
+ mxW = 0
675
+ self.write_message("Found {} clusters of unconnected strips in sensor.\n".format(len(module_holes)))
676
+ for H in module_holes:
677
+ self.write_message("{}\n".format(str(H)))
678
+ if H[-1] > mxW:
679
+ mxW = H[-1]
680
+
681
+ if mxW > 0:
682
+ self.write_message("Max width: {}". format(mxW))
683
+
684
+ out["MAX_UNCON_SENSOR_CHAN"] = mxW
685
+ nstrips = 0
686
+ for v in unconnected:
687
+ nstrips += v
688
+
689
+ out["TOTAL_PERC_UNCON_SENSOR_CHAN"] = nstrips/M.nchan
690
+
691
+ return out
692
+
693
+ def get_unconnected(self, skeleton):
694
+ """Fill the test DTO with unconnected information."""
695
+ out = self.compute_unconnected()
696
+ for key, val in out.items():
697
+ skeleton["results"][key] = val
698
+
699
+ def read_file(self, *args):
700
+ """Read local data file."""
701
+ dialog = Gtk.FileChooserDialog(
702
+ title="Please choose a file",
703
+ parent=self,
704
+ action=Gtk.FileChooserAction.OPEN
705
+ )
706
+ dialog.add_buttons(
707
+ Gtk.STOCK_CANCEL,
708
+ Gtk.ResponseType.CANCEL,
709
+ Gtk.STOCK_OPEN,
710
+ Gtk.ResponseType.OK,
711
+ )
712
+ filter_text = Gtk.FileFilter()
713
+ filter_text.set_name("JSON files")
714
+ filter_text.add_mime_type("application/json")
715
+ dialog.add_filter(filter_text)
716
+
717
+ response = dialog.run()
718
+ if response == Gtk.ResponseType.OK:
719
+ ofile = dialog.get_filename()
720
+ self.write_message("Loading data from {}\n".format(ofile))
721
+ with open(ofile, 'r', encoding="utf-8") as data_file:
722
+ data = json.load(data_file)
723
+ self.parse(data)
724
+
725
+ dialog.hide()
726
+ dialog.destroy()
727
+
728
+ def parse(self, data):
729
+ """Parses a JSon dictionary."""
730
+ self.operator.set_text(data["properties"]["OPERATOR"])
731
+ self.machine.set_text(data["properties"]["BOND_MACHINE"])
732
+ self.batch.set_text(data["properties"]["BONDWIRE_BATCH"])
733
+ self.SN.set_text(data["component"])
734
+ self.date.set_text(data["date"])
735
+ for key, val in data["results"].items():
736
+ model = self.models[key]
737
+ model.clear()
738
+ for chan, msg in val.items():
739
+ model.append([chan, msg])
740
+
741
+ model.append(["", ""])
742
+
743
+ def get_list_of_channels(self, data):
744
+ """Creates the lists of channels."""
745
+ for key, model in self.models.items():
746
+ iter = model.get_iter_first()
747
+ out = {}
748
+ while iter:
749
+ chan, comm = model[iter]
750
+ if len(chan) > 0:
751
+ out[chan] = comm
752
+
753
+ iter = model.iter_next(iter)
754
+
755
+ data["results"][key] = out
756
+
757
+ def save_test(self, *args):
758
+ """Save Test file."""
759
+ dialog = Gtk.FileChooserDialog(
760
+ title="Please choose a file", parent=self, action=Gtk.FileChooserAction.SAVE
761
+ )
762
+ dialog.add_buttons(
763
+ Gtk.STOCK_CANCEL,
764
+ Gtk.ResponseType.CANCEL,
765
+ Gtk.STOCK_OPEN,
766
+ Gtk.ResponseType.OK,
767
+ )
768
+ filter_text = Gtk.FileFilter()
769
+ filter_text.set_name("JSON files")
770
+ filter_text.add_mime_type("application/json")
771
+ dialog.add_filter(filter_text)
772
+
773
+ response = dialog.run()
774
+ if response == Gtk.ResponseType.OK:
775
+ ofile = dialog.get_filename()
776
+ data = self.get_test_data()
777
+ with open(ofile, 'w') as of:
778
+ json.dump(data, of, indent=3)
779
+
780
+ dialog.hide()
781
+ dialog.destroy()
782
+
783
+ def get_test_data(self):
784
+ """Get the test data."""
785
+ data = {
786
+ "date": ITkDButils.get_db_date(),
787
+ "properties": {},
788
+ "results": {},
789
+ "comments": [],
790
+ "defects": []
791
+ }
792
+ self.get_test_header(data)
793
+ self.get_list_of_channels(data)
794
+ return data
795
+
796
+ def get_test_header(self, data):
797
+ """Get Basic test data."""
798
+ SN = self.SN.get_text()
799
+ operator = self.operator.get_text()
800
+ machine = self.machine.get_text()
801
+ batch = self.batch.get_text()
802
+
803
+ if not SN or len(SN)==0:
804
+ SN = dbGtkUtils.get_a_value("Module Serial Number",
805
+ "Module serial Number is missing")
806
+
807
+ if len(operator) == 0 or len(machine) == 0 or len(batch) == 0:
808
+ values = dbGtkUtils.get_a_list_of_values(
809
+ "Missing Values",
810
+ ["SN", "Operator", "Wire Bonder", "Wire Batch"],
811
+ defaults=[SN, operator, machine, batch],
812
+ )
813
+ if len(values) == 4:
814
+ SN, operator, machine, batch = values
815
+ else:
816
+ self.write_message("Something went wrong whilerequesting missing information.")
817
+
818
+ data["component"] = SN
819
+ data["properties"]["OPERATOR"] = operator
820
+ data["properties"]["BOND_MACHINE"] = machine
821
+ data["properties"]["BONDWIRE_BATCH"] = batch
822
+
823
+
824
+ def upload_test(self, *args):
825
+ """Upload test."""
826
+ if self.session is None:
827
+ self.pdb = ITkDBlogin.ITkDBlogin()
828
+ client = self.pdb.get_client()
829
+ if client is None:
830
+ dbGtkUtils.complain("Could not connect to DB with provided credentials.")
831
+ self.pdb.die()
832
+
833
+ else:
834
+ self.session = client
835
+
836
+ defaults = {
837
+ "institution": "IFIC",
838
+ "runNumber": "1",
839
+ "date": ITkDButils.get_db_date()
840
+ }
841
+ skeleton = ITkDButils.get_test_skeleton(self.session, "MODULE", "MODULE_WIRE_BONDING", defaults)
842
+
843
+ self.get_test_header(skeleton)
844
+ self.get_list_of_channels(skeleton)
845
+ self.get_unconnected(skeleton)
846
+
847
+ uploadW = UploadTest.UploadTest(self.session, payload=skeleton)
848
+ # uploadW.run()
849
+
850
+
851
+ def main():
852
+ """Main entry."""
853
+ win = WireBond(title="WireBond")
854
+ win.connect("destroy", Gtk.main_quit)
855
+ try:
856
+ Gtk.main()
857
+
858
+ except KeyboardInterrupt:
859
+ print("Arrrgggg!!!")
860
+
861
+
862
+ if __name__ == "__main__":
863
+ main()