itkdb-gtk 0.0.14__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,857 @@
1
+ #!/usr/bin/env python3
2
+ """Read IV files and create plots.
3
+
4
+ Analisis de la IV con macros de la webApp
5
+
6
+ SENSOR_IV_Analysis.py in
7
+ https://gitlab.cern.ch/atlas-itk/sw/db/production_database_scripts.git
8
+
9
+ webApp aqui:
10
+ https://itk-pdb-webapps-strips.web.cern.ch
11
+
12
+ """
13
+ import os
14
+ import json
15
+ import warnings
16
+ from pathlib import Path
17
+ import shutil
18
+
19
+ import gi
20
+ import tempfile
21
+
22
+ gi.require_version("Gtk", "3.0")
23
+ from gi.repository import Gtk, Gio
24
+
25
+
26
+ from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
27
+ from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
28
+ import numpy as np
29
+ import matplotlib as mpl
30
+ import matplotlib.pyplot as plt
31
+
32
+ try:
33
+ import itkdb_gtk
34
+
35
+ except ImportError:
36
+ import sys
37
+ from pathlib import Path
38
+ cwd = Path(sys.argv[0]).parent.parent
39
+ sys.path.append(cwd.as_posix())
40
+
41
+ from itkdb_gtk import dbGtkUtils, ITkDBlogin, ITkDButils, UploadTest
42
+
43
+ # Check if Gtk can be open
44
+ gtk_runs, gtk_args = Gtk.init_check()
45
+
46
+
47
+ def remove_files(W, flist):
48
+ for f in flist:
49
+ os.unlink(f)
50
+
51
+
52
+ #
53
+ # The following is taken from
54
+ # https://gitlab.cern.ch/atlas-itk/sw/db/production_database_scripts.git
55
+ #
56
+ mm = 1e-3
57
+ cm = 1e-2
58
+ UpperV = 500 # For sensor to pass, I must be < Imax up until this voltage and no breakdown must be detected before then.
59
+ StabilityV = 700 # Voltage with multiple current readings to check stability
60
+
61
+ AreaDict = {
62
+ "Unknown": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
63
+ #
64
+ "ATLAS12": 95.7 * 95.64 * mm * mm,
65
+ "ATLAS17LS": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
66
+ #
67
+ "ATLAS18R0": 89.9031 * cm * cm,
68
+ "ATLAS18R1": 89.0575 * cm * cm,
69
+ "ATLAS18R2": 74.1855 * cm * cm,
70
+ "ATLAS18R3": 80.1679 * cm * cm,
71
+ "ATLAS18R4": 87.4507 * cm * cm,
72
+ "ATLAS18R5": 91.1268 * cm * cm,
73
+ #
74
+ "ATLAS18SS": 93.6269 * cm * cm,
75
+ "ATLAS18LS": 93.6269 * cm * cm,
76
+ #
77
+ "ATLASDUMMY18": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
78
+ }
79
+
80
+
81
+ def LocateMicroDischarge(
82
+ I,
83
+ V,
84
+ sm_window=2,
85
+ bd_limit=5.5,
86
+ allow_running_bd=True,
87
+ use_additional_cond=False,
88
+ tolerence=0.05,
89
+ voltage_span=4,
90
+ fit_window=5,
91
+ ):
92
+ """
93
+ Function for BDV estimation - if questions please contact Vera Latonova (vera.latonova@cern.ch).
94
+ I,V must have same shape and voltages must be in ascending order,
95
+ same indexes of I&V arrays must correspond each other,
96
+ only invalid data or holdstep should be stripped before
97
+ but it is not necessary. Measurments U=0&I=0 are removed.
98
+ If there is same or higher amount of same voltages in row than
99
+ sm_window, from this sequence we cannot estimete dI/dV and
100
+ we have to remove this averaged point.
101
+
102
+ It is assumed that only parameter use_additional_cond would be
103
+ changed by caller. Changing of other parameters may affect
104
+ BDV unexpectedly.
105
+
106
+
107
+ @param[in] I - array of currents without any cut
108
+ @param[in] V - array of voltages, ascending order, without any cut
109
+ @param[in] sm_window - size of smoothing window
110
+ @param[in] bd_limit - BD limit for |U| < 500V
111
+ @param[in] allow_running_bd - allow increase bd_limit for |U| > 500
112
+ @param[in] use_additional_cond - use additional BD contition
113
+ @param[in] tolerence - configuration of additional condition
114
+ @param[in] voltage_span - max width of hump on spectra which may be neglected
115
+ in voltage steps in additional contition
116
+ @param[in] fit_window - number of points used for linear fit before BD voltage
117
+
118
+ @return BD voltage (always positive) or NO_BD_CONST = 9.99e99 if not found.
119
+ """
120
+ NO_BD_CONST = 9.99e99
121
+
122
+ # add nan to the end of array
123
+ V = np.abs(V)
124
+ I = np.abs(I)
125
+
126
+ # skip zeros
127
+ ind = np.where(np.logical_or(I != 0, V != 0))
128
+ V = V[ind]
129
+ I = I[ind]
130
+
131
+ V_ = np.append(V, np.nan * np.ones(sm_window - 1))
132
+ I_ = np.append(I, np.nan * np.ones(sm_window - 1))
133
+
134
+ # make 2D array of I's, V's each row_ind shifted by row_ind index
135
+ # i.e from array [1,3,5] we make (for sm_window=2) 2D array
136
+ # [ 1,3,5,nan]
137
+ # [nan,5,1,3]
138
+ # than get average from each column -> I_avg, V_avg
139
+ r = np.arange(sm_window)
140
+
141
+ V2 = np.outer(np.ones(sm_window), V_)
142
+ row_ind, col_ind = np.ogrid[: V2.shape[0], : V2.shape[1]]
143
+ col_ind = col_ind - r[:, np.newaxis]
144
+ V2 = V2[row_ind, col_ind]
145
+ # strip fields with nans
146
+ V2 = np.transpose(V2[:, (sm_window - 1) : -(sm_window - 1)])
147
+
148
+ I2 = np.outer(np.ones(sm_window), I_)
149
+ row_ind, col_ind = np.ogrid[: I2.shape[0], : I2.shape[1]]
150
+ col_ind = col_ind - r[:, np.newaxis]
151
+ I2 = I2[row_ind, col_ind]
152
+ I2 = np.transpose(I2[:, (sm_window - 1) : -(sm_window - 1)])
153
+
154
+ # get V & I averages
155
+ try:
156
+ V_avg = np.average(V2, axis=1)
157
+ I_avg = np.average(I2, axis=1)
158
+ except ZeroDivisionError:
159
+ # not enough data
160
+ return NO_BD_CONST
161
+
162
+ # find dI / dV array
163
+ # I'm not able to write this without cycle
164
+ dIdV = np.array([])
165
+ for i in range(V2.shape[0]):
166
+ with warnings.catch_warnings():
167
+ warnings.filterwarnings("error")
168
+ try:
169
+ dIdV = np.append(dIdV, np.polyfit(V2[i, :], I2[i, :], 1)[0])
170
+ except (np.RankWarning, TypeError):
171
+ dIdV = np.append(dIdV, np.nan)
172
+
173
+ # stripping U[n] == U[n+1] (i.e. hodlsetp) => fit cannot be sucessful =>
174
+ # dIdV is nan @holdstep
175
+ ind = np.where(np.isfinite(dIdV))
176
+ I_avg = I_avg[ind]
177
+ V_avg = V_avg[ind]
178
+ dIdV = dIdV[ind]
179
+
180
+ # get running BDV limit & compare
181
+ bd_limit_running = bd_limit + np.where(
182
+ allow_running_bd and V_avg > 500, (V_avg - 500.0) / 100.0, 0
183
+ )
184
+ V_avg_BD_ind = dIdV / (I_avg / V_avg) > bd_limit_running
185
+ V_avg_BD = V_avg[V_avg_BD_ind]
186
+
187
+ # Estimate BDV
188
+ BDV = np.array([])
189
+
190
+ # no break-down
191
+ if V_avg_BD.shape == (0,):
192
+ return NO_BD_CONST
193
+
194
+ # if V_avg_BD_ind[0] == True ... BDV <- V[0]
195
+ # for others V_avg_BD_ind[n] == True BDV <- (V_avg[n] + V_avg[n-1])/2
196
+ if V_avg_BD_ind[0]:
197
+ BDV = np.append(BDV, V[0])
198
+ V_avg_BD_ind[0] = False
199
+
200
+ BDV = np.append(
201
+ BDV,
202
+ (V_avg[np.where(V_avg_BD_ind)] + V_avg[np.where(V_avg_BD_ind)[0] - 1]) / 2.0,
203
+ )
204
+
205
+ ###########################################################################
206
+ ## Application of additional condition ####################################
207
+ ###########################################################################
208
+ if not use_additional_cond:
209
+ return BDV[0]
210
+
211
+ # get index if V <= BDV
212
+ B = np.where(np.less.outer(BDV, V))
213
+ col_ind = np.mgrid[: BDV.shape[0], : V.shape[0]][1]
214
+ col_ind[B[0], B[1]] = 0
215
+ V_BDV_ind = np.max(col_ind, axis=1)
216
+
217
+ back_ok_v_ind = 0
218
+ while True:
219
+ with warnings.catch_warnings():
220
+ warnings.filterwarnings("error")
221
+ try:
222
+ a, b = np.polyfit(
223
+ V[
224
+ max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
225
+ back_ok_v_ind, V_BDV_ind[0]
226
+ )
227
+ ],
228
+ I[
229
+ max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
230
+ back_ok_v_ind, V_BDV_ind[0]
231
+ )
232
+ ],
233
+ 1,
234
+ )
235
+ except (np.RankWarning, TypeError):
236
+ return BDV[0]
237
+
238
+ ind = np.where(1 - (a * V + b) / I <= tolerence)[0]
239
+ try:
240
+ back_ok_v_ind = np.min(ind[ind > V_BDV_ind[0] + 1])
241
+ except ValueError:
242
+ # sensor is not going back
243
+ return BDV[0]
244
+ # hump is too long -- it cannot be skipped
245
+ if back_ok_v_ind - V_BDV_ind[0] > voltage_span:
246
+ return BDV[0]
247
+
248
+ # skip BDVs inside hump
249
+ ind = BDV >= V[back_ok_v_ind]
250
+ BDV = BDV[ind]
251
+ V_BDV_ind = V_BDV_ind[ind]
252
+ if V_avg_BD.shape == (0,):
253
+ return NO_BD_CONST
254
+ return NO_BD_CONST
255
+
256
+
257
+ def scale_iv(I, T1, T2):
258
+ """Normalize corrent to given temperature (T2)
259
+
260
+ Args:
261
+ I (array): Current
262
+ T1 (float): Original temperature
263
+ T2 (float): New temperature.
264
+
265
+ Return:
266
+ Array with scaled currents.
267
+
268
+ """
269
+ factor = (T2 / T1) ** 2 * np.exp((-1.21 / 8.62) * (1 / T2 - 1 / T1))
270
+ return factor * I
271
+
272
+
273
+ class IVwindow(dbGtkUtils.ITkDBWindow):
274
+ """GUI for IV file handling."""
275
+
276
+ def __init__(self, session, title="IV window", options=None):
277
+ """Initialization."""
278
+ super().__init__(
279
+ session=session, title=title, show_search=None, gtk_runs=gtk_runs
280
+ )
281
+ self.mdata = {}
282
+ self.mod_type = {}
283
+ self.mod_SN = {}
284
+ self.difference = None
285
+ self.canvas = None
286
+
287
+ self.init_window()
288
+
289
+ def init_window(self):
290
+ """Prepare the Gtk window."""
291
+ self.hb.props.title = "IV data"
292
+
293
+ button = Gtk.Button()
294
+ icon = Gio.ThemedIcon(name="view-refresh-symbolic")
295
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
296
+ button.add(image)
297
+ button.set_tooltip_text("Click to refresh canvas.")
298
+ button.connect("clicked", self.on_refresh)
299
+ self.hb.pack_end(button)
300
+
301
+ # Button to upload
302
+ button = Gtk.Button()
303
+ icon = Gio.ThemedIcon(name="document-send-symbolic")
304
+ image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
305
+ button.add(image)
306
+ button.set_tooltip_text("Click to upload test")
307
+ button.connect("clicked", self.upload_test)
308
+ self.hb.pack_end(button)
309
+
310
+ # File entry and search button
311
+ self.single_file = Gtk.FileChooserButton()
312
+ self.single_file.connect("file-set", self.on_single_file_set)
313
+
314
+ self.double_file = Gtk.FileChooserButton()
315
+ self.double_file.connect("file-set", self.on_double_file_set)
316
+
317
+ self.single_SN = Gtk.Label(label="(None)")
318
+ self.double_SN = Gtk.Label(label="(None)")
319
+
320
+ grid = Gtk.Grid(column_spacing=5, row_spacing=1)
321
+
322
+ grid.attach(Gtk.Label(label="Files"), 1, 0, 1, 1)
323
+ grid.attach(Gtk.Label(label="Serial No."), 2, 0, 1, 1)
324
+
325
+ grid.attach(Gtk.Label(label="Single Data File"), 0, 1, 1, 1)
326
+ grid.attach(self.single_file, 1, 1, 1, 1)
327
+ grid.attach(self.single_SN, 2, 1, 1, 1)
328
+
329
+ grid.attach(Gtk.Label(label="Double Data File"), 0, 2, 1, 1)
330
+ grid.attach(self.double_file, 1, 2, 1, 1)
331
+ grid.attach(self.double_SN, 2, 2, 1, 1)
332
+
333
+ btn = Gtk.Button(label="Compute difference")
334
+ btn.connect("clicked", self.on_difference)
335
+ grid.attach(btn, 1, 3, 1, 1)
336
+
337
+ btn = Gtk.Button(label="Upload to DB")
338
+ btn.connect("clicked", self.upload_test)
339
+ grid.attach(btn, 2, 3, 1, 1)
340
+
341
+ self.mainBox.pack_start(grid, False, True, 0)
342
+
343
+ self.fig = mpl.figure.Figure()
344
+ self.fig.tight_layout()
345
+ sw = Gtk.ScrolledWindow() # Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
346
+
347
+ # A scrolled window border goes outside the scrollbars and viewport
348
+ sw.set_border_width(10)
349
+ sw.set_size_request(310, 310)
350
+
351
+ self.canvas = FigureCanvas(self.fig) # a Gtk.DrawingArea
352
+ self.canvas.set_size_request(400, 300)
353
+ sw.add(self.canvas)
354
+ self.mainBox.pack_start(sw, True, True, 0)
355
+
356
+ # Create toolbar
357
+ try:
358
+ toolbar = NavigationToolbar(self.canvas)
359
+ except TypeError:
360
+ toolbar = NavigationToolbar(self.canvas, self)
361
+
362
+ self.mainBox.pack_start(toolbar, False, False, 0)
363
+
364
+ # The text view
365
+ self.mainBox.pack_start(self.message_panel.frame, True, True, 5)
366
+
367
+ self.show_all()
368
+
369
+ def upload_test(self, *args):
370
+ """Upload available tests."""
371
+ try:
372
+ mdata = self.mdata["double"]
373
+ except KeyError:
374
+ dbGtkUtils.complain("Cannot Upload to DB", "Data not yet ready.")
375
+ return
376
+
377
+ # Check if we are dealing with sensors or modules
378
+ is_module = "Module_SN" in mdata
379
+
380
+ if is_module:
381
+ test = ITkDButils.get_test_skeleton(
382
+ self.session, "MODULE", self.mdata["double"]["TestType"]
383
+ )
384
+ tp = "ATLAS18{}".format(self.mod_type["double"][0:2])
385
+ area = AreaDict[tp] / cm**2
386
+
387
+ else:
388
+ test = ITkDButils.get_test_skeleton(
389
+ self.session, "SENSOR", self.mdata["double"]["TestType"]
390
+ )
391
+ area = AreaDict[self.mod_type["double"]] / cm**2
392
+
393
+ # The data arrays
394
+ V = np.abs(mdata["curve"]["V"])
395
+ I = np.abs(mdata["curve"]["I"])
396
+ passed = True
397
+
398
+ # Find Current @ 500V
399
+ indx = np.where(V == 500)[0]
400
+ i_500 = I[indx][0] / area
401
+ self.write_message("I @ 500V = {:.2f} nA/cm2\n".format(i_500))
402
+
403
+ # Compute current stability
404
+ IStability = abs(I[abs(V) == StabilityV])
405
+ IVariation = -1
406
+ if np.size(IStability) > 1: # Maybe make == 4?
407
+ IVariation = abs(np.std(IStability) / np.mean(IStability))
408
+
409
+ self.write_message("I stability = {:.6f} nA\n".format(IVariation))
410
+
411
+ # Search for Micro discharges
412
+ # Check for micro-discharge in non-normalized current,
413
+ # removing duplicate Voltage entries (e.g. for stability measurements)
414
+ comments = []
415
+ defects = []
416
+ UniqueVs, UniqueIndices = np.unique(V, return_index=True)
417
+ MicroDischargeV = LocateMicroDischarge(I[UniqueIndices], UniqueVs)
418
+ if MicroDischargeV < np.max(V):
419
+ comments.append("Found micro discharge: {:.1f} V\n".format(MicroDischargeV))
420
+ self.write_message(comments[-1])
421
+
422
+ if MicroDischargeV < UpperV:
423
+ txt = "microdischarge happening before {:.1f}V.".format(UpperV)
424
+ defects.append({
425
+ "name": "MicroDischarge",
426
+ "description": txt,
427
+ "properties": {}
428
+ }
429
+ )
430
+ self.write_message("...{}. FAILED\n".format(txt))
431
+ passed = False
432
+
433
+ test["component"] = self.mod_SN["double"]
434
+ test["institution"] = mdata["Institute"]
435
+ test["runNumber"] = mdata["RunNumber"]
436
+ test["date"] = ITkDButils.get_db_date(
437
+ "{} {}".format(mdata["Date"], mdata["Time"])
438
+ )
439
+ test["passed"] = passed
440
+ test["problems"] = False
441
+ test["properties"]["VBIAS_SMU"] = mdata["Vbias_SMU"]
442
+ test["properties"]["RSERIES"] = mdata["Rseries"]
443
+ test["properties"]["TEST_DMM"] = mdata["Test_DMM"]
444
+ test["properties"]["RSHUNT"] = mdata["Rshunt"]
445
+ test["properties"]["RUNNUMBER"] = mdata["RunNumber"]
446
+ test["properties"]["COMMENTS"] = mdata["Comments"]
447
+ test["properties"]["ALGORITHM_VERSION"] = "0.0.0"
448
+ if is_module:
449
+ test["properties"]["SOFTWARE_TYPE_VERSION"] = "pyProbe"
450
+ test["properties"]["MODULE_STAGE"] = mdata["Module_Stage"]
451
+
452
+ test["results"]["TEMPERATURE"] = mdata["Temperature"]
453
+ test["results"]["HUMIDITY"] = mdata["Humidity"]
454
+ test["results"]["VBD"] = MicroDischargeV
455
+ test["results"]["I_500V"] = i_500
456
+ test["results"]["VOLTAGE"] = -np.abs(V)
457
+ test["results"]["CURRENT"] = -np.abs(I)
458
+ test["results"]["RMS_STABILITY"] = IVariation
459
+ test["results"]["SHUNT_VOLTAGE"] = np.zeros(V.shape)
460
+ test["defects"] = defects
461
+ test["comments"] = comments
462
+
463
+ # write attachment.
464
+ if is_module:
465
+ items = [
466
+ "Type",
467
+ "Wafer",
468
+ "Module_SN",
469
+ "Module_Stage",
470
+ "Date",
471
+ "Time",
472
+ "Institute",
473
+ "TestType",
474
+ "Vbias_SMU",
475
+ "Rseries",
476
+ "Test_DMM",
477
+ "Rshunt",
478
+ "Software type and version, fw version",
479
+ "RunNumber",
480
+ "Temperature",
481
+ "Humidity",
482
+ "Comments",
483
+ ]
484
+ else:
485
+ items = [
486
+ "Type",
487
+ "Batch",
488
+ "Wafer",
489
+ "Component",
490
+ "Date",
491
+ "Time",
492
+ "Institute",
493
+ "TestType",
494
+ "Vbias_SMU",
495
+ "Rseries",
496
+ "Test_DMM",
497
+ "Rshunt",
498
+ "RunNumber",
499
+ "Temperature",
500
+ "Humidity",
501
+ "Comments",
502
+ ]
503
+
504
+ try:
505
+ fnam = "{}_{}_IV_{}-".format(
506
+ self.mod_SN["double"], mdata["Module_Stage"], mdata["RunNumber"]
507
+ )
508
+ except KeyError:
509
+ fnam = "{}_W_IV_{}".format(self.mod_SN["double"], mdata["RunNumber"])
510
+
511
+ data_out = tempfile.NamedTemporaryFile(
512
+ "w", prefix=fnam, suffix=".dat", delete=False
513
+ )
514
+ data_out.write("{}\n".format(fnam))
515
+ for key in items:
516
+ if key == "Module_SN" or key == "Component":
517
+ data_out.write("{}: {}\n".format(key, self.mod_SN["double"]))
518
+ else:
519
+ data_out.write("{}: {}\n".format(key, mdata[key]))
520
+
521
+ for il, label in enumerate(mdata["curve"]["labels"]):
522
+ if il:
523
+ data_out.write("\t")
524
+ data_out.write(label)
525
+ data_out.write("\n")
526
+
527
+ ncol = len(mdata["curve"]["labels"])
528
+ ndata = len(self.difference)
529
+ for i in range(ndata):
530
+ v = -abs(V[i])
531
+ iv = -abs(self.difference[i])
532
+ if ncol > 2:
533
+ data_out.write("{:.2f}\t{:.2f}\t{:.2f}\n".format(v, iv, 0.0))
534
+ else:
535
+ data_out.write("{:.2f}\t{:.2f}\n".format(v, iv))
536
+
537
+ print(data_out.name)
538
+ data_out.close()
539
+
540
+ js_out = tempfile.NamedTemporaryFile(
541
+ "w", prefix="payload-", suffix=".json", delete=False
542
+ )
543
+ js_out.write(json.dumps(test, indent=3, cls=dbGtkUtils.MyEncoder))
544
+ js_out.close()
545
+
546
+ if dbGtkUtils.ask_for_confirmation("Save Data File locally?", "Saves attached file also locally."):
547
+ fc = Gtk.FileChooserDialog(title="Save data file", action=Gtk.FileChooserAction.SAVE)
548
+ fc.add_buttons(
549
+ Gtk.STOCK_CANCEL,
550
+ Gtk.ResponseType.CANCEL,
551
+ Gtk.STOCK_OPEN,
552
+ Gtk.ResponseType.OK,
553
+ )
554
+
555
+ fc.set_current_name("{}.dat".format(fnam))
556
+ rc = fc.run()
557
+ if rc == Gtk.ResponseType.OK:
558
+ shutil.copyfile(data_out.name, fc.get_filename())
559
+
560
+ fc.hide()
561
+ fc.destroy()
562
+
563
+ attachment = ITkDButils.Attachment(data_out.name, "resultsFile", fnam)
564
+ uploadW = UploadTest.UploadTest(self.session, js_out.name, attachment)
565
+ uploadW.connect("destroy", remove_files, [data_out.name, js_out.name])
566
+
567
+ def on_refresh(self, *args):
568
+ """Refresh canvas."""
569
+ if self.fig and self.canvas:
570
+ self.fig.tight_layout()
571
+ self.canvas.draw()
572
+
573
+ def find_module(self, SN):
574
+ """Find module (SN) on database
575
+
576
+ Args:
577
+ ----
578
+ SN (str): Module Serial number
579
+ """
580
+ md = ITkDButils.get_DB_component(self.session, SN)
581
+ if md is None:
582
+ dbGtkUtils.complain(
583
+ "Could not find {}".format(SN), str(ITkDButils.get_db_response())
584
+ )
585
+
586
+ return md
587
+
588
+ def on_single_file_set(self, *args):
589
+ """File chosen."""
590
+ obj_type = ["sensor", "module"]
591
+ fnam = self.single_file.get_filename()
592
+ if fnam is None or not Path(fnam).exists():
593
+ dbGtkUtils.complain("Could not find data file", fnam, parent=self)
594
+
595
+ mdata = self.read_file(fnam)
596
+
597
+ is_module = 1
598
+ try:
599
+ SN = mdata["Module_SN"]
600
+ except KeyError:
601
+ SN = mdata["Component"]
602
+ is_module = 0
603
+
604
+ self.write_message("Reading data for {} {}\n".format(obj_type[is_module], SN))
605
+ md = self.find_module(SN)
606
+ if md is None:
607
+ self.write_message("...object does not exist.\n")
608
+ self.single_file.unselect_all()
609
+ return
610
+
611
+ # All good
612
+ self.mod_SN["single"] = SN
613
+ self.mdata["single"] = mdata
614
+ self.mod_type["single"] = md["type"]["code"]
615
+ print(self.mod_type["single"])
616
+
617
+ self.single_SN.set_text("{} - {}".format(SN, md["type"]["name"]))
618
+ self.fig.clf()
619
+ self.show_curve(
620
+ 131,
621
+ mdata["curve"]["V"],
622
+ mdata["curve"]["I"],
623
+ self.mod_type["single"][0:4] if is_module else "Single",
624
+ mdata["curve"]["labels"][0],
625
+ mdata["curve"]["labels"][1],
626
+ )
627
+
628
+ def on_double_file_set(self, *args):
629
+ "File chosen for the 'double module'"
630
+ obj_type = ["sensor", "module"]
631
+ fnam = self.double_file.get_filename()
632
+ if fnam is None or not Path(fnam).exists():
633
+ dbGtkUtils.complain("Could not find data file", fnam, parent=self)
634
+
635
+ mdata = self.read_file(fnam)
636
+ is_module = 1
637
+ # Check SN in data file
638
+ try:
639
+ SN = mdata["Module_SN"]
640
+ except KeyError:
641
+ is_module = 0
642
+ SN = mdata["Component"]
643
+
644
+ halfM_SN = SN
645
+ if "single" in self.mod_SN:
646
+ if self.mod_SN["single"] == SN:
647
+ dbGtkUtils.complain(
648
+ "Wrong {}} SN", "{} already used.".format(obj_type[is_module], SN)
649
+ )
650
+ self.double_file.unselect_all()
651
+ return
652
+
653
+ # Check that it exists in the DB
654
+ if len(SN) != 14 or SN[0:4] != "20US":
655
+ self.write_message("Invalid SN: {}\n".format(SN))
656
+ SN = dbGtkUtils.get_a_value(
657
+ "Invalid SN", "Give Ring or corresponding Half Module SN"
658
+ )
659
+
660
+ self.write_message("Reading data for module {}\n".format(SN))
661
+ md = self.find_module(SN)
662
+ if md is None:
663
+ self.write_message("...object does not exist.\n")
664
+ self.double_file.unselect_all()
665
+ return
666
+
667
+ found_child = False
668
+ if md["type"]["name"].find("Ring") >= 0:
669
+ self.write_message("...This is a Ring module. Searching children in DB\n")
670
+ for child in md["children"]:
671
+ if child["component"]:
672
+ ctype = child["type"]["code"]
673
+ if ctype.find("MODULE") < 0:
674
+ continue
675
+
676
+ cSN = child["component"]["serialNumber"]
677
+ if cSN == self.mod_SN["single"]:
678
+ continue
679
+
680
+ halfM_SN = cSN
681
+ found_child = True
682
+ self.write_message("...found {}\n".format(halfM_SN))
683
+ break
684
+
685
+ if not found_child:
686
+ self.write_message("Requesting a Half Module SN\n")
687
+ halfM_SN = dbGtkUtils.get_a_value(
688
+ "Give Half Module SN", "Serial Number"
689
+ )
690
+
691
+ md = ITkDButils.get_DB_component(self.session, halfM_SN)
692
+ if md is None:
693
+ dbGtkUtils.complain(
694
+ "Could not find {}".format(halfM_SN),
695
+ str(ITkDButils.get_db_response()),
696
+ )
697
+ self.double_file.unselect_all()
698
+ return
699
+
700
+ self.write_message("... {}\n".format(halfM_SN))
701
+
702
+ if "single" in self.mod_type:
703
+ if is_module and self.mod_type["single"] == md["type"]["code"]:
704
+ dbGtkUtils.complain(
705
+ "Wrong module type.",
706
+ "Module type cannot be {}".format(self.mod_type["single"]),
707
+ )
708
+
709
+ self.double_file.unselect_all()
710
+ return
711
+
712
+ self.mod_SN["double"] = halfM_SN
713
+ self.mod_type["double"] = md["type"]["code"]
714
+ self.mdata["double"] = mdata
715
+
716
+ self.double_SN.set_text("{} - {}".format(halfM_SN, md["type"]["name"]))
717
+ self.show_curve(
718
+ 133,
719
+ mdata["curve"]["V"],
720
+ mdata["curve"]["I"],
721
+ "Double",
722
+ mdata["curve"]["labels"][0],
723
+ None,
724
+ )
725
+
726
+ # Compute difference if single already available
727
+ if "single" in self.mdata:
728
+ self.on_difference()
729
+
730
+ def on_difference(self, *args):
731
+ """Compute difference."""
732
+ if "single" not in self.mdata or "double" not in self.mdata:
733
+ dbGtkUtils.complain(
734
+ "Data needed", "Check if single oand doubel module data are available"
735
+ )
736
+ return
737
+
738
+ is_module = "Module_SN" in self.mdata["double"]
739
+ double_I = self.mdata["double"]["curve"]["I"]
740
+ single_I = scale_iv(
741
+ self.mdata["single"]["curve"]["I"],
742
+ self.mdata["single"]["Temperature"] + 273.0,
743
+ self.mdata["double"]["Temperature"] + 273.0,
744
+ )
745
+
746
+ try:
747
+ nmin = double_I.size
748
+ self.difference = double_I - single_I
749
+ except ValueError:
750
+ nmin = np.min([double_I.size, single_I.size])
751
+ self.write_message(
752
+ "Size of current arrays is not the same: {} {}\n".format(
753
+ double_I.size, single_I.size
754
+ )
755
+ )
756
+ self.difference = double_I[:nmin] - single_I[:nmin]
757
+
758
+ self.show_curve(
759
+ 132,
760
+ self.mdata["double"]["curve"]["V"][:nmin],
761
+ self.difference,
762
+ self.mod_type["double"][0:4] if is_module else "Diff",
763
+ self.mdata["double"]["curve"]["labels"][0],
764
+ None,
765
+ )
766
+
767
+ def show_curve(self, subplot, X, Y, title=None, xlabel="X", ylabel="Y"):
768
+ """Shows data"""
769
+ ax = self.fig.add_subplot(subplot)
770
+ plt.cla()
771
+ if xlabel:
772
+ ax.set_xlabel(xlabel)
773
+
774
+ if ylabel:
775
+ ax.set_ylabel(ylabel)
776
+
777
+ if title:
778
+ ax.set_title(title)
779
+
780
+ ax.plot(X, Y)
781
+ ax.grid()
782
+ self.on_refresh()
783
+
784
+ @staticmethod
785
+ def read_file(fnam):
786
+ """Read a data file. Return dictionary with all teh data."""
787
+ labels = []
788
+ metadata = {}
789
+ with open(fnam, "r", encoding="utf-8") as ifile:
790
+ first = True
791
+ for line in ifile:
792
+ if first:
793
+ first = False
794
+ ipos = line.rfind(".")
795
+ metadata["fname"] = line[:ipos]
796
+ continue
797
+
798
+ if line.find("Voltage [V]") >= 0 or line.find("Voltage[V]") >= 0:
799
+ labels = line.split("\t")
800
+ break
801
+
802
+ rc = line.find(":")
803
+ if rc >= 0:
804
+ key = line[:rc].strip()
805
+ val = line[rc + 1 :].strip()
806
+ if key in ["Temperature", "Humidity"]:
807
+ metadata[key] = float(val)
808
+ else:
809
+ metadata[key] = val
810
+
811
+ V = []
812
+ I = []
813
+ for line in ifile:
814
+ data = [float(s) for s in line.split()]
815
+ V.append(data[0])
816
+ I.append(data[1])
817
+
818
+ metadata["curve"] = {
819
+ "V": np.abs(np.array(V)),
820
+ "I": np.abs(np.array(I)),
821
+ "labels": labels[0:2],
822
+ }
823
+ return metadata
824
+
825
+
826
+ def main():
827
+ """Main entryy."""
828
+ import sys
829
+
830
+ # DB login
831
+ dlg = ITkDBlogin.ITkDBlogin()
832
+ client = dlg.get_client()
833
+ if client is None:
834
+ print("Could not connect to DB with provided credentials.")
835
+ dlg.die()
836
+ sys.exit()
837
+
838
+ client.user_gui = dlg
839
+
840
+ # Start the Application
841
+ win = IVwindow(client)
842
+ win.show_all()
843
+ win.set_accept_focus(True)
844
+ win.present()
845
+ win.connect("destroy", Gtk.main_quit)
846
+
847
+ try:
848
+ Gtk.main()
849
+ except KeyboardInterrupt:
850
+ print("Arrggggg !!!")
851
+
852
+ dlg.die()
853
+ print("Bye !!")
854
+ sys.exit()
855
+
856
+ if __name__ == "__main__":
857
+ main()