itkdb-gtk 0.0.18__py3-none-any.whl → 0.9.1.dev1__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.

@@ -10,277 +10,61 @@ webApp aqui:
10
10
  https://itk-pdb-webapps-strips.web.cern.ch
11
11
 
12
12
  """
13
+ import sys
13
14
  import os
14
15
  import json
15
- import warnings
16
+ import tempfile
17
+ import copy
16
18
  from pathlib import Path
17
19
  import shutil
18
20
 
19
- import gi
20
- import tempfile
21
-
22
- gi.require_version("Gtk", "3.0")
23
- from gi.repository import Gtk, Gio
24
-
25
-
26
21
  from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
27
22
  from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
28
23
  import numpy as np
29
24
  import matplotlib as mpl
30
25
  import matplotlib.pyplot as plt
31
26
 
27
+ import gi
28
+
29
+ gi.require_version("Gtk", "3.0")
30
+ from gi.repository import Gtk, Gio
31
+
32
32
  try:
33
33
  import itkdb_gtk
34
-
34
+
35
35
  except ImportError:
36
- import sys
37
- from pathlib import Path
38
- cwd = Path(sys.argv[0]).parent.parent
36
+ cwd = Path(__file__).parent.parent
39
37
  sys.path.append(cwd.as_posix())
40
38
 
41
- from itkdb_gtk import dbGtkUtils, ITkDBlogin, ITkDButils, UploadTest
39
+ from itkdb_gtk import dbGtkUtils, ITkDBlogin, ITkDButils, UploadTest, SensorUtils
42
40
 
43
41
  # Check if Gtk can be open
44
42
  gtk_runs, gtk_args = Gtk.init_check()
45
43
 
46
44
 
47
45
  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)
46
+ """Remove files given in the input list.
259
47
 
260
48
  Args:
261
- I (array): Current
262
- T1 (float): Original temperature
263
- T2 (float): New temperature.
264
-
265
- Return:
266
- Array with scaled currents.
49
+ flist (list): list of filenames.
267
50
 
268
51
  """
269
- factor = (T2 / T1) ** 2 * np.exp((-1.21 / 8.62) * (1 / T2 - 1 / T1))
270
- return factor * I
52
+ for f in flist:
53
+ os.unlink(f)
271
54
 
272
55
 
273
56
  class IVwindow(dbGtkUtils.ITkDBWindow):
274
57
  """GUI for IV file handling."""
275
58
 
276
- def __init__(self, session, title="IV window", options=None):
59
+ def __init__(self, session, title="IV window", options=None, help=help):
277
60
  """Initialization."""
278
61
  super().__init__(
279
- session=session, title=title, show_search=None, gtk_runs=gtk_runs
62
+ session=session, title=title, show_search=None, gtk_runs=gtk_runs, help=help
280
63
  )
281
64
  self.mdata = {}
282
65
  self.mod_type = {}
283
66
  self.mod_SN = {}
67
+ self.last_folder = None
284
68
  self.difference = None
285
69
  self.canvas = None
286
70
 
@@ -304,7 +88,7 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
304
88
  image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
305
89
  button.add(image)
306
90
  button.set_tooltip_text("Click to upload test")
307
- button.connect("clicked", self.upload_test)
91
+ button.connect("clicked", self.do_upload)
308
92
  self.hb.pack_end(button)
309
93
 
310
94
  # File entry and search button
@@ -330,12 +114,12 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
330
114
  grid.attach(self.double_file, 1, 2, 1, 1)
331
115
  grid.attach(self.double_SN, 2, 2, 1, 1)
332
116
 
333
- btn = Gtk.Button(label="Compute difference")
334
- btn.connect("clicked", self.on_difference)
335
- grid.attach(btn, 1, 3, 1, 1)
117
+ #btn = Gtk.Button(label="Compute difference")
118
+ #btn.connect("clicked", self.on_difference)
119
+ #grid.attach(btn, 1, 3, 1, 1)
336
120
 
337
121
  btn = Gtk.Button(label="Upload to DB")
338
- btn.connect("clicked", self.upload_test)
122
+ btn.connect("clicked", self.do_upload)
339
123
  grid.attach(btn, 2, 3, 1, 1)
340
124
 
341
125
  self.mainBox.pack_start(grid, False, True, 0)
@@ -358,7 +142,7 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
358
142
  toolbar = NavigationToolbar(self.canvas)
359
143
  except TypeError:
360
144
  toolbar = NavigationToolbar(self.canvas, self)
361
-
145
+
362
146
  self.mainBox.pack_start(toolbar, False, False, 0)
363
147
 
364
148
  # The text view
@@ -366,184 +150,88 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
366
150
 
367
151
  self.show_all()
368
152
 
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
153
+ def get_difference_data(self):
154
+ """Returns the double data witht the difference."""
155
+ # Prepare the data
156
+ ddata = copy.deepcopy(self.mdata["double"])
376
157
 
377
- # Check if we are dealing with sensors or modules
378
- is_module = "Module_SN" in mdata
158
+ ndata = len(self.difference)
159
+ ddata["curve"]["V"] = np.abs(ddata["curve"]["V"][:ndata])
160
+ ddata["curve"]["I"] = np.abs(self.difference)
161
+ ddata["curve"]["S"] = ddata["curve"]["S"][:ndata]
162
+ return ddata
379
163
 
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
164
+ def upload_test(self, mdata, mod_type):
165
+ """Upload available tests."""
386
166
 
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
167
+ # Get JSon skeleton filled
168
+ test = SensorUtils.sensor_data_to_json(self.session, mdata, mod_type, self)
462
169
 
463
170
  # 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
- ]
171
+ # First geet the fine name.
172
+ fnam = SensorUtils.build_file_name(mdata)
503
173
 
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
174
 
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
- )
175
+ data_out = tempfile.NamedTemporaryFile("w", prefix=fnam, suffix=".dat", delete=False)
176
+ SensorUtils.save_sensor_data(data_out, mdata, name=fnam)
177
+
178
+ js_out = tempfile.NamedTemporaryFile("w", prefix="payload-", suffix=".json", delete=False)
543
179
  js_out.write(json.dumps(test, indent=3, cls=dbGtkUtils.MyEncoder))
544
180
  js_out.close()
545
181
 
546
- if dbGtkUtils.ask_for_confirmation("Save Data File locally?", "Saves attached file also locally."):
182
+ attachment = ITkDButils.Attachment(data_out.name, "resultsFile", fnam)
183
+ uploadW = UploadTest.UploadTest(self.session, js_out.name, attachment)
184
+ uploadW.connect("destroy", remove_files, [data_out.name, js_out.name])
185
+
186
+
187
+ def do_upload(self, *args):
188
+ """The upload button has been clicked.
189
+
190
+ We present a dialog where we ask if the new data file should be stored
191
+ locally and if both (single and difference) tests should be uploaded.
192
+ """
193
+ if "single" not in self.mdata:
194
+ return
195
+
196
+ if "double" not in self.mdata:
197
+ # upload only the single test.
198
+ if dbGtkUtils.ask_for_confirmation(
199
+ "Uploading Single data",
200
+ "No data for double module/sensor.\nUpload single test ?."):
201
+
202
+ self.upload_test(self.mdata["single"], self.mod_type["single"])
203
+
204
+ return
205
+
206
+ # We create the dialog.
207
+ dlg = Gtk.Dialog(title="Add Attachment", parent=self, flags=0)
208
+ dlg.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
209
+ Gtk.STOCK_OK, Gtk.ResponseType.OK)
210
+ area = dlg.get_content_area()
211
+ grid = Gtk.Grid(column_spacing=5, row_spacing=1)
212
+ area.add(grid)
213
+
214
+ label = Gtk.Label(label="Save locally new data file ?")
215
+ save_locally = Gtk.Switch()
216
+ grid.attach(label, 0, 0, 1, 1)
217
+ grid.attach(save_locally, 1, 0, 1, 1)
218
+
219
+ label = Gtk.Label(label="Upload both tests ?")
220
+ do_both = Gtk.Switch()
221
+ grid.attach(label, 0, 1, 1, 1)
222
+ grid.attach(do_both, 1, 1, 1, 1)
223
+
224
+ dlg.show_all()
225
+ rc = dlg.run()
226
+ dlg.hide()
227
+ if rc != Gtk.ResponseType.OK:
228
+ dlg.destroy()
229
+ return
230
+
231
+ mdata = self.get_difference_data()
232
+ if save_locally.get_active():
233
+ # Save locally.
234
+ fnam = SensorUtils.build_file_name(mdata)
547
235
  fc = Gtk.FileChooserDialog(title="Save data file", action=Gtk.FileChooserAction.SAVE)
548
236
  fc.add_buttons(
549
237
  Gtk.STOCK_CANCEL,
@@ -552,17 +240,24 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
552
240
  Gtk.ResponseType.OK,
553
241
  )
554
242
 
243
+ if self.last_folder:
244
+ fc.set_current_folder(self.last_folder)
245
+
555
246
  fc.set_current_name("{}.dat".format(fnam))
556
247
  rc = fc.run()
557
248
  if rc == Gtk.ResponseType.OK:
558
- shutil.copyfile(data_out.name, fc.get_filename())
559
-
249
+ SensorUtils.save_sensor_data(fc.get_filename(), mdata)
250
+
560
251
  fc.hide()
561
252
  fc.destroy()
562
253
 
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])
254
+ # Upload double
255
+ self.upload_test(mdata, self.mod_type["double"])
256
+
257
+ if do_both.get_active():
258
+ self.upload_test(self.mdata["single"], self.mod_type["single"])
259
+
260
+ dlg.destroy()
566
261
 
567
262
  def on_refresh(self, *args):
568
263
  """Refresh canvas."""
@@ -574,8 +269,8 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
574
269
  """Find module (SN) on database
575
270
 
576
271
  Args:
577
- ----
578
- SN (str): Module Serial number
272
+ SN (str): Module Serial number.
273
+
579
274
  """
580
275
  md = ITkDButils.get_DB_component(self.session, SN)
581
276
  if md is None:
@@ -585,18 +280,61 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
585
280
 
586
281
  return md
587
282
 
283
+ def update_folder(self, fnam):
284
+ """Sets last folder."""
285
+
286
+ self.last_folder = Path(fnam).parent.as_posix()
287
+ self.single_file.set_current_folder(self.last_folder)
288
+ self.double_file.set_current_folder(self.last_folder)
289
+
290
+ def show_single_curve(self):
291
+ """Shows the single curve."""
292
+ try:
293
+ mdata = self.mdata["single"]
294
+ except KeyError:
295
+ return
296
+
297
+ is_module = is_module = "Module_SN" in mdata
298
+
299
+ self.show_curve(
300
+ 131,
301
+ mdata["curve"]["V"],
302
+ mdata["curve"]["I"],
303
+ self.mod_type["single"][0:4] if is_module else "Single",
304
+ mdata["curve"]["labels"][0],
305
+ mdata["curve"]["labels"][1],
306
+ )
307
+
308
+ def show_double_curve(self):
309
+ """Shows the double curve."""
310
+ try:
311
+ mdata = self.mdata["double"]
312
+ except KeyError:
313
+ return
314
+
315
+ self.show_curve(
316
+ 133,
317
+ mdata["curve"]["V"],
318
+ mdata["curve"]["I"],
319
+ "Double",
320
+ mdata["curve"]["labels"][0],
321
+ None,
322
+ )
323
+
588
324
  def on_single_file_set(self, *args):
589
- """File chosen."""
325
+ """Single sensor file chosen."""
590
326
  obj_type = ["sensor", "module"]
591
327
  fnam = self.single_file.get_filename()
592
328
  if fnam is None or not Path(fnam).exists():
593
329
  dbGtkUtils.complain("Could not find data file", fnam, parent=self)
594
330
 
595
- mdata = self.read_file(fnam)
331
+ mdata = SensorUtils.read_sensor_file(fnam)
332
+ self.update_folder(fnam)
596
333
 
597
334
  is_module = 1
598
335
  try:
599
336
  SN = mdata["Module_SN"]
337
+
600
338
  except KeyError:
601
339
  SN = mdata["Component"]
602
340
  is_module = 0
@@ -616,39 +354,24 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
616
354
 
617
355
  self.single_SN.set_text("{} - {}".format(SN, md["type"]["name"]))
618
356
  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
- )
357
+ self.show_single_curve()
627
358
 
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"]
359
+ # Compute difference if single already available
360
+ if "double" in self.mdata:
361
+ self.show_double_curve()
362
+ self.on_difference()
643
363
 
644
- halfM_SN = SN
364
+ def check_double_SN(self, SN, is_module):
365
+ """Check that the double SN is a good one."""
366
+ obj_type = ["sensor", "module"]
645
367
  if "single" in self.mod_SN:
646
368
  if self.mod_SN["single"] == SN:
647
369
  dbGtkUtils.complain(
648
- "Wrong {}} SN", "{} already used.".format(obj_type[is_module], SN)
370
+ "Wrong SN {}".format(SN),
371
+ "{} already used.".format(obj_type[is_module])
649
372
  )
650
373
  self.double_file.unselect_all()
651
- return
374
+ return None
652
375
 
653
376
  # Check that it exists in the DB
654
377
  if len(SN) != 14 or SN[0:4] != "20US":
@@ -656,16 +379,21 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
656
379
  SN = dbGtkUtils.get_a_value(
657
380
  "Invalid SN", "Give Ring or corresponding Half Module SN"
658
381
  )
382
+ return None
659
383
 
660
- self.write_message("Reading data for module {}\n".format(SN))
661
384
  md = self.find_module(SN)
662
385
  if md is None:
663
386
  self.write_message("...object does not exist.\n")
664
387
  self.double_file.unselect_all()
665
- return
388
+ return None
666
389
 
390
+ return md
391
+
392
+ def get_true_SN(self, md):
393
+ """Get the actual SN of the 'double' object."""
667
394
  found_child = False
668
- if md["type"]["name"].find("Ring") >= 0:
395
+ has_ring = md["type"]["name"].find("Ring")
396
+ if has_ring >= 0:
669
397
  self.write_message("...This is a Ring module. Searching children in DB\n")
670
398
  for child in md["children"]:
671
399
  if child["component"]:
@@ -695,9 +423,39 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
695
423
  str(ITkDButils.get_db_response()),
696
424
  )
697
425
  self.double_file.unselect_all()
698
- return
426
+ return None
699
427
 
700
428
  self.write_message("... {}\n".format(halfM_SN))
429
+ return halfM_SN
430
+
431
+ return md["serialNumber"]
432
+
433
+ def on_double_file_set(self, *args):
434
+ "File chosen for the 'double module'"
435
+ obj_type = ["sensor", "module"]
436
+ fnam = self.double_file.get_filename()
437
+ if fnam is None or not Path(fnam).exists():
438
+ dbGtkUtils.complain("Could not find data file", fnam, parent=self)
439
+
440
+ mdata = SensorUtils.read_sensor_file(fnam)
441
+ self.update_folder(fnam)
442
+ is_module = 1
443
+ # Check SN in data file
444
+ try:
445
+ SN = mdata["Module_SN"]
446
+ except KeyError:
447
+ is_module = 0
448
+ SN = mdata["Component"]
449
+
450
+ halfM_SN = SN
451
+ md = self.check_double_SN(SN, is_module)
452
+ if md is None:
453
+ return
454
+
455
+ self.write_message("Reading data for {} {}\n".format(obj_type[is_module], SN))
456
+ halfM_SN = self.get_true_SN(md)
457
+ if halfM_SN is None:
458
+ return
701
459
 
702
460
  if "single" in self.mod_type:
703
461
  if is_module and self.mod_type["single"] == md["type"]["code"]:
@@ -714,14 +472,7 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
714
472
  self.mdata["double"] = mdata
715
473
 
716
474
  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
- )
475
+ self.show_double_curve()
725
476
 
726
477
  # Compute difference if single already available
727
478
  if "single" in self.mdata:
@@ -737,7 +488,7 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
737
488
 
738
489
  is_module = "Module_SN" in self.mdata["double"]
739
490
  double_I = self.mdata["double"]["curve"]["I"]
740
- single_I = scale_iv(
491
+ single_I = SensorUtils.scale_iv(
741
492
  self.mdata["single"]["curve"]["I"],
742
493
  self.mdata["single"]["Temperature"] + 273.0,
743
494
  self.mdata["double"]["Temperature"] + 273.0,
@@ -781,51 +532,9 @@ class IVwindow(dbGtkUtils.ITkDBWindow):
781
532
  ax.grid()
782
533
  self.on_refresh()
783
534
 
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
535
 
826
536
  def main():
827
537
  """Main entryy."""
828
- import sys
829
538
 
830
539
  # DB login
831
540
  dlg = ITkDBlogin.ITkDBlogin()