mmcb-rs232-avt 1.0.14__py3-none-any.whl → 1.0.18__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.
- mmcb_rs232/__init__.py +0 -0
- mmcb_rs232/common.py +2605 -0
- mmcb_rs232/detect.py +1053 -0
- mmcb_rs232/dmm.py +126 -0
- mmcb_rs232/dmm_interface.py +162 -0
- mmcb_rs232/iv.py +2868 -0
- mmcb_rs232/lexicon.py +580 -0
- mmcb_rs232/psuset.py +938 -0
- mmcb_rs232/psustat.py +705 -0
- mmcb_rs232/psuwatch.py +540 -0
- mmcb_rs232/sequence.py +483 -0
- mmcb_rs232/ult80.py +500 -0
- {mmcb_rs232_avt-1.0.14.dist-info → mmcb_rs232_avt-1.0.18.dist-info}/METADATA +1 -1
- mmcb_rs232_avt-1.0.18.dist-info/RECORD +17 -0
- mmcb_rs232_avt-1.0.18.dist-info/top_level.txt +1 -0
- mmcb_rs232_avt-1.0.14.dist-info/RECORD +0 -5
- mmcb_rs232_avt-1.0.14.dist-info/top_level.txt +0 -1
- {mmcb_rs232_avt-1.0.14.dist-info → mmcb_rs232_avt-1.0.18.dist-info}/WHEEL +0 -0
- {mmcb_rs232_avt-1.0.14.dist-info → mmcb_rs232_avt-1.0.18.dist-info}/entry_points.txt +0 -0
mmcb_rs232/psuset.py
ADDED
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Set the voltage on a power supply connected by RS232.
|
|
4
|
+
|
|
5
|
+
All power supplies supported by detect.py are usable by this script.
|
|
6
|
+
|
|
7
|
+
the script will return an appropriate unix return value on completion, so
|
|
8
|
+
the return value can be used by the shell, e.g.
|
|
9
|
+
|
|
10
|
+
./psuset.py -50 && ./capture2.py -t 60
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import logging
|
|
15
|
+
import math
|
|
16
|
+
import sys
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
import types
|
|
20
|
+
|
|
21
|
+
import serial
|
|
22
|
+
import numpy as np
|
|
23
|
+
|
|
24
|
+
from mmcb import common
|
|
25
|
+
from mmcb import lexicon
|
|
26
|
+
from mmcb import sequence
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
##############################################################################
|
|
30
|
+
# command line option handler
|
|
31
|
+
##############################################################################
|
|
32
|
+
|
|
33
|
+
def check_voltage(val):
|
|
34
|
+
"""
|
|
35
|
+
check voltage range
|
|
36
|
+
|
|
37
|
+
--------------------------------------------------------------------------
|
|
38
|
+
args
|
|
39
|
+
val : float
|
|
40
|
+
bias voltage
|
|
41
|
+
--------------------------------------------------------------------------
|
|
42
|
+
returns : float
|
|
43
|
+
--------------------------------------------------------------------------
|
|
44
|
+
"""
|
|
45
|
+
val = float(val)
|
|
46
|
+
if not -1100 <= val <= 1100:
|
|
47
|
+
raise argparse.ArgumentTypeError(
|
|
48
|
+
f'{val}: '
|
|
49
|
+
'voltage value should be between -1100 and 1100')
|
|
50
|
+
return val
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_settlingtime(val):
|
|
54
|
+
"""
|
|
55
|
+
check settling time
|
|
56
|
+
|
|
57
|
+
--------------------------------------------------------------------------
|
|
58
|
+
args
|
|
59
|
+
val : float
|
|
60
|
+
settling time in seconds
|
|
61
|
+
--------------------------------------------------------------------------
|
|
62
|
+
returns : float
|
|
63
|
+
--------------------------------------------------------------------------
|
|
64
|
+
"""
|
|
65
|
+
val = float(val)
|
|
66
|
+
if not 0.0 <= val <= 60.0:
|
|
67
|
+
raise argparse.ArgumentTypeError(
|
|
68
|
+
f'{val}: '
|
|
69
|
+
'settling time value should be between 0 and 60 seconds')
|
|
70
|
+
return val
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def check_arguments(settings):
|
|
74
|
+
"""
|
|
75
|
+
handle command line options
|
|
76
|
+
|
|
77
|
+
--------------------------------------------------------------------------
|
|
78
|
+
args
|
|
79
|
+
settings : dictionary
|
|
80
|
+
contains core information about the test environment
|
|
81
|
+
--------------------------------------------------------------------------
|
|
82
|
+
returns : none
|
|
83
|
+
--------------------------------------------------------------------------
|
|
84
|
+
"""
|
|
85
|
+
parser = argparse.ArgumentParser(
|
|
86
|
+
description='Set voltage and/or current limit of a single power\
|
|
87
|
+
supply channel connected by RS232. Currently supported PSUs are: Hameg\
|
|
88
|
+
(Rohde & Schwarz) HMP4040; Keithley 2410, 2614b; ISEG SHQ 222M, 224M;\
|
|
89
|
+
and Agilent E3647A, E3634A (the latter may also be branded Keysight\
|
|
90
|
+
or Hewlett-Packard). When multiple power supplies are\
|
|
91
|
+
connected, use command line options --manufacturer, --model,\
|
|
92
|
+
--serial, --channel and --port to identify the individual power\
|
|
93
|
+
supply. For brevity, specify the minimum number of identifying\
|
|
94
|
+
parameters to uniquely identify the device; if there is only one\
|
|
95
|
+
single-channel power supply attached, just specify the voltage to be\
|
|
96
|
+
set. Note that ISEG power supplies can take a long time to settle to\
|
|
97
|
+
final values, and at low voltages they may never converge to the set\
|
|
98
|
+
voltage.')
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
'voltage', nargs='*', metavar='voltage',
|
|
101
|
+
help='value in volts, values range -1100 to +1100',
|
|
102
|
+
type=check_voltage, default=None)
|
|
103
|
+
parser.add_argument(
|
|
104
|
+
'--manufacturer', nargs=1, metavar='manufacturer',
|
|
105
|
+
choices=['agilent', 'hameg', 'iseg', 'keithley'],
|
|
106
|
+
help='PSU manufacturer.',
|
|
107
|
+
default=None)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
'--model', nargs=1, metavar='model',
|
|
110
|
+
choices=['2410', '2614b', 'e3634a', 'e3647a', 'hmp4040', 'shq'],
|
|
111
|
+
help='PSU model.',
|
|
112
|
+
default=None)
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
'--serial', nargs=1, metavar='serial',
|
|
115
|
+
help='PSU serial number. A part of the serial may be supplied if it\
|
|
116
|
+
is unique amongst connected devices.',
|
|
117
|
+
default=None)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
'--channel', nargs=1, metavar='channel',
|
|
120
|
+
choices=['1', '2', '3', '4', 'a', 'A', 'b', 'B', 'c', 'C', 'd', 'D'],
|
|
121
|
+
help='PSU channel number. These can be specified numerically or\
|
|
122
|
+
alphabetically.',
|
|
123
|
+
default=None)
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
'--port', nargs=1, metavar='port',
|
|
126
|
+
help='Serial port identifier. This is useful where multiple Agilent/\
|
|
127
|
+
Keysight/Hewlett-Packard power supplies are connected, since they do\
|
|
128
|
+
not provide a serial number over RS232. A part of the serial may be\
|
|
129
|
+
supplied if it is unique amongst connected devices.',
|
|
130
|
+
default=None)
|
|
131
|
+
parser.add_argument(
|
|
132
|
+
'--reset',
|
|
133
|
+
action='store_true',
|
|
134
|
+
help='Reset power supply before setting voltage (Keithley 2410).')
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
'-i', '--immediate',
|
|
137
|
+
action='store_true',
|
|
138
|
+
help='On high-voltage power supplies, set the specified voltage\
|
|
139
|
+
immediately. By default the voltage will ramp from the current\
|
|
140
|
+
value to the specified value (Keithley and ISEG). For low-voltage\
|
|
141
|
+
power supplies the specified voltage is always set immediately\
|
|
142
|
+
unless --peltier is used with the HMP4040.')
|
|
143
|
+
parser.add_argument(
|
|
144
|
+
'-l', '--limit', nargs=1, metavar='current_limit',
|
|
145
|
+
help='Set the power supply channel current limit.\
|
|
146
|
+
Values can be specified with either scientific (10e-9) or\
|
|
147
|
+
engineering notation (10n). Supported for Keithley 2410, 2614b;\
|
|
148
|
+
Rohde & Schwarz HMP4040. For the latter, the minimum value is 1mA.',
|
|
149
|
+
type=common.check_current)
|
|
150
|
+
parser.add_argument(
|
|
151
|
+
'-p', '--peltier',
|
|
152
|
+
action='store_true',
|
|
153
|
+
help='Use gradual voltage changes for HMP4040.')
|
|
154
|
+
parser.add_argument(
|
|
155
|
+
'-v', '--voltspersecond', nargs=1, metavar='rateofchange',
|
|
156
|
+
help='When not using --immediate, this option sets the rate of change\
|
|
157
|
+
in volts per second. The default is 10V/s (Keithley and ISEG).')
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
'-s', '--settlingtime', nargs=1, metavar='settlingtime',
|
|
160
|
+
help='Custom settling time in seconds between asserting voltage\
|
|
161
|
+
and reading back. Default is 0.5s.',
|
|
162
|
+
type=check_settlingtime, default=[0.5])
|
|
163
|
+
parser.add_argument(
|
|
164
|
+
'--forwardbias',
|
|
165
|
+
action='store_true',
|
|
166
|
+
help='For HV supplies biassing detectors it is expected negative\
|
|
167
|
+
voltages will be used, so by default user requests for positive values\
|
|
168
|
+
will be suppressed, unless this option is used. Implemented for the\
|
|
169
|
+
Keithley 2410/2614b only.')
|
|
170
|
+
|
|
171
|
+
group1 = parser.add_mutually_exclusive_group()
|
|
172
|
+
group1.add_argument(
|
|
173
|
+
'-f', '--front',
|
|
174
|
+
action='store_true',
|
|
175
|
+
help='Use front output (Keithley 2410).')
|
|
176
|
+
group1.add_argument(
|
|
177
|
+
'-r', '--rear',
|
|
178
|
+
action='store_true',
|
|
179
|
+
help='Use rear output (Keithley 2410).')
|
|
180
|
+
|
|
181
|
+
group2 = parser.add_mutually_exclusive_group()
|
|
182
|
+
group2.add_argument(
|
|
183
|
+
'--on',
|
|
184
|
+
action='store_true',
|
|
185
|
+
help='Turn PSU (channel) output on, by default the script leaves the\
|
|
186
|
+
power supply output unchanged (Keithley and Agilent).')
|
|
187
|
+
group2.add_argument(
|
|
188
|
+
'--off',
|
|
189
|
+
action='store_true',
|
|
190
|
+
help='Turn PSU output off, by default the script leaves the power\
|
|
191
|
+
supply output unchanged (Keithley and Agilent).')
|
|
192
|
+
|
|
193
|
+
group3 = parser.add_mutually_exclusive_group()
|
|
194
|
+
group3.add_argument(
|
|
195
|
+
'--verbose',
|
|
196
|
+
action='store_true',
|
|
197
|
+
help='Display all power supply channels detected, and search terms\
|
|
198
|
+
supplied')
|
|
199
|
+
group3.add_argument(
|
|
200
|
+
'-q', '--quiet',
|
|
201
|
+
action='store_true',
|
|
202
|
+
help='Minimal text output during normal operation')
|
|
203
|
+
|
|
204
|
+
args = parser.parse_args()
|
|
205
|
+
|
|
206
|
+
settings['verbose'] = args.verbose
|
|
207
|
+
settings['quiet'] = args.quiet
|
|
208
|
+
|
|
209
|
+
# default: leave power supply settings unchanged
|
|
210
|
+
if args.front:
|
|
211
|
+
settings['rear'] = False
|
|
212
|
+
elif args.rear:
|
|
213
|
+
settings['rear'] = True
|
|
214
|
+
|
|
215
|
+
# default: leave power supply settings unchanged
|
|
216
|
+
if args.on:
|
|
217
|
+
settings['on'] = True
|
|
218
|
+
elif args.off:
|
|
219
|
+
settings['on'] = False
|
|
220
|
+
|
|
221
|
+
if args.reset:
|
|
222
|
+
settings['reset'] = args.reset
|
|
223
|
+
|
|
224
|
+
if args.immediate:
|
|
225
|
+
settings['immediate'] = args.immediate
|
|
226
|
+
|
|
227
|
+
if args.forwardbias:
|
|
228
|
+
settings['forwardbias'] = args.forwardbias
|
|
229
|
+
|
|
230
|
+
if args.peltier:
|
|
231
|
+
settings['peltier'] = args.peltier
|
|
232
|
+
|
|
233
|
+
# voltage will always be present
|
|
234
|
+
if args.voltage is not None and len(args.voltage) == 1:
|
|
235
|
+
settings['voltage'] = args.voltage[0]
|
|
236
|
+
|
|
237
|
+
if args.serial:
|
|
238
|
+
settings['serial'] = args.serial[0]
|
|
239
|
+
|
|
240
|
+
if args.channel:
|
|
241
|
+
settings['channel'] = args.channel[0].lower()
|
|
242
|
+
|
|
243
|
+
if args.manufacturer:
|
|
244
|
+
settings['manufacturer'] = args.manufacturer[0]
|
|
245
|
+
|
|
246
|
+
if args.model:
|
|
247
|
+
settings['model'] = args.model[0]
|
|
248
|
+
|
|
249
|
+
if args.port:
|
|
250
|
+
settings['port'] = args.port[0]
|
|
251
|
+
|
|
252
|
+
if args.voltspersecond:
|
|
253
|
+
settings['voltspersecond'] = int(args.voltspersecond[0])
|
|
254
|
+
|
|
255
|
+
if args.limit:
|
|
256
|
+
settings['current_limit'] = args.limit[0]
|
|
257
|
+
|
|
258
|
+
settings['settlingtime'] = args.settlingtime[0]
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
##############################################################################
|
|
262
|
+
# set psu values
|
|
263
|
+
##############################################################################
|
|
264
|
+
|
|
265
|
+
def read_measured_voltage(settings, pipeline, ser, dev):
|
|
266
|
+
"""
|
|
267
|
+
read the voltage as measured at the psu output terminals
|
|
268
|
+
|
|
269
|
+
--------------------------------------------------------------------------
|
|
270
|
+
args
|
|
271
|
+
settings : dictionary
|
|
272
|
+
contains core information about the test environment
|
|
273
|
+
pipeline : instance of class Production
|
|
274
|
+
contains all the queues through which the production pipeline
|
|
275
|
+
processes communicate
|
|
276
|
+
ser : serial.Serial
|
|
277
|
+
reference for serial port
|
|
278
|
+
dev : instance of class Channel
|
|
279
|
+
contains details of a device and its serial port
|
|
280
|
+
--------------------------------------------------------------------------
|
|
281
|
+
returns
|
|
282
|
+
success : bool
|
|
283
|
+
--------------------------------------------------------------------------
|
|
284
|
+
"""
|
|
285
|
+
measured_voltage = None
|
|
286
|
+
|
|
287
|
+
if dev.manufacturer == 'iseg':
|
|
288
|
+
command_string = lexicon.power(dev.model, 'read voltage', channel=dev.channel)
|
|
289
|
+
local_buffer = common.atomic_send_command_read_response(pipeline, ser, dev,
|
|
290
|
+
command_string)
|
|
291
|
+
|
|
292
|
+
measured_voltage = common.iseg_value_to_float(local_buffer)
|
|
293
|
+
|
|
294
|
+
elif dev.manufacturer == 'keithley':
|
|
295
|
+
command_string = lexicon.power(dev.model, 'read voltage', channel=dev.channel)
|
|
296
|
+
local_buffer = common.atomic_send_command_read_response(pipeline, ser, dev,
|
|
297
|
+
command_string)
|
|
298
|
+
|
|
299
|
+
if local_buffer is not None:
|
|
300
|
+
separator = ',' if dev.model == '2410' else None
|
|
301
|
+
items = (float(x) for x in local_buffer.split(separator))
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
measured_voltage = next(items)
|
|
305
|
+
except (StopIteration, ValueError):
|
|
306
|
+
message = f'{dev.ident} problem reading measured voltage'
|
|
307
|
+
common.log_with_colour(logging.WARNING, message)
|
|
308
|
+
|
|
309
|
+
elif dev.manufacturer in {'agilent', 'hameg'}:
|
|
310
|
+
if dev.model == 'e3634a':
|
|
311
|
+
command_string = lexicon.power(dev.model, 'set remote')
|
|
312
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
313
|
+
|
|
314
|
+
command_string = lexicon.power(dev.model, 'read voltage', channel=dev.channel)
|
|
315
|
+
local_buffer = common.atomic_send_command_read_response(pipeline, ser, dev,
|
|
316
|
+
command_string)
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
volt = float(local_buffer)
|
|
320
|
+
except ValueError:
|
|
321
|
+
pass
|
|
322
|
+
else:
|
|
323
|
+
measured_voltage = common.decimal_quantize(volt, settings['decimal_places'])
|
|
324
|
+
|
|
325
|
+
return measured_voltage
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def check_measured_voltage(settings, pipeline, ser, dev, set_voltage):
|
|
329
|
+
"""
|
|
330
|
+
compare the voltage as measured at the psu output terminals to the
|
|
331
|
+
given voltage
|
|
332
|
+
|
|
333
|
+
--------------------------------------------------------------------------
|
|
334
|
+
args
|
|
335
|
+
settings : dictionary
|
|
336
|
+
contains core information about the test environment
|
|
337
|
+
pipeline : instance of class Production
|
|
338
|
+
contains all the queues through which the production pipeline
|
|
339
|
+
processes communicate
|
|
340
|
+
ser : serial.Serial
|
|
341
|
+
reference for serial port
|
|
342
|
+
dev : instance of class Channel
|
|
343
|
+
contains details of a device and its serial port
|
|
344
|
+
set_voltage : decimal.Decimal
|
|
345
|
+
--------------------------------------------------------------------------
|
|
346
|
+
returns
|
|
347
|
+
success : bool
|
|
348
|
+
--------------------------------------------------------------------------
|
|
349
|
+
"""
|
|
350
|
+
success = False
|
|
351
|
+
measured_voltage = read_measured_voltage(settings, pipeline, ser, dev)
|
|
352
|
+
|
|
353
|
+
# comparison
|
|
354
|
+
if measured_voltage is not None:
|
|
355
|
+
set_voltage = common.decimal_quantize(set_voltage, settings['decimal_places'])
|
|
356
|
+
measured_voltage = common.decimal_quantize(measured_voltage, settings['decimal_places'])
|
|
357
|
+
|
|
358
|
+
volt = common.si_prefix(measured_voltage)
|
|
359
|
+
common.log_with_colour(logging.INFO, f'measured: {volt}V')
|
|
360
|
+
|
|
361
|
+
if set_voltage == measured_voltage:
|
|
362
|
+
success = True
|
|
363
|
+
|
|
364
|
+
return success
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def current_limit(settings, pipeline, ser, dev):
|
|
368
|
+
"""
|
|
369
|
+
Set and check current limit on Hameg HMP4040, Keithley 2614b and 2410.
|
|
370
|
+
|
|
371
|
+
HMP4040 notes:
|
|
372
|
+
|
|
373
|
+
The PSU seems to support setting of values down to 100uA for current limit
|
|
374
|
+
values < 1A, and down to 1mA for current limit >= 1A. However, support
|
|
375
|
+
seems patchy. 0.9992A, 0.0012 and 1.2m set correctly, but 0.0002A, 0.0002
|
|
376
|
+
and 0.2m do not. So limit this to 1mA (3 decimal places) to avoid
|
|
377
|
+
confusion.
|
|
378
|
+
|
|
379
|
+
--------------------------------------------------------------------------
|
|
380
|
+
args
|
|
381
|
+
settings : dictionary
|
|
382
|
+
contains core information about the test environment
|
|
383
|
+
pipeline : instance of class Production
|
|
384
|
+
contains all the queues through which the production pipeline
|
|
385
|
+
processes communicate
|
|
386
|
+
ser : serial.Serial
|
|
387
|
+
reference for serial port
|
|
388
|
+
dev : instance of class Channel
|
|
389
|
+
contains details of a device and its serial port
|
|
390
|
+
--------------------------------------------------------------------------
|
|
391
|
+
returns : none
|
|
392
|
+
--------------------------------------------------------------------------
|
|
393
|
+
"""
|
|
394
|
+
if dev.model not in ('hmp4040', '2614b', '2410'):
|
|
395
|
+
return
|
|
396
|
+
|
|
397
|
+
# set current limit
|
|
398
|
+
if dev.model == 'hmp4040':
|
|
399
|
+
dec_places = 3
|
|
400
|
+
compliance = f'{settings["current_limit"]:.{dec_places}f}'
|
|
401
|
+
else:
|
|
402
|
+
compliance = f'{settings["current_limit"]:e}'
|
|
403
|
+
|
|
404
|
+
command_string = lexicon.power(dev.model, 'set current limit',
|
|
405
|
+
compliance, channel=dev.channel)
|
|
406
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
407
|
+
|
|
408
|
+
# read back current limit from PSU
|
|
409
|
+
command_string = lexicon.power(dev.model, 'get current limit', channel=dev.channel)
|
|
410
|
+
response = common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
411
|
+
one_percent = 0.005
|
|
412
|
+
onehundred_microamps = 0.0001
|
|
413
|
+
close_enough = math.isclose(float(response), settings['current_limit'],
|
|
414
|
+
rel_tol=one_percent, abs_tol=onehundred_microamps)
|
|
415
|
+
|
|
416
|
+
# report current limit
|
|
417
|
+
message = ('requested current limit '
|
|
418
|
+
f'{common.si_prefix(settings["current_limit"])}A, '
|
|
419
|
+
f'set to {common.si_prefix(response)}A')
|
|
420
|
+
if close_enough:
|
|
421
|
+
common.log_with_colour(logging.INFO, message)
|
|
422
|
+
else:
|
|
423
|
+
common.log_with_colour(logging.WARNING, message)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def hmp4040_constant_current(ser, pipeline, dev):
|
|
427
|
+
"""
|
|
428
|
+
Check if HMP4040 channel is configured for constant current.
|
|
429
|
+
|
|
430
|
+
--------------------------------------------------------------------------
|
|
431
|
+
args
|
|
432
|
+
ser : serial.Serial
|
|
433
|
+
reference for serial port
|
|
434
|
+
pipeline : instance of class Production
|
|
435
|
+
contains all the queues through which the production pipeline
|
|
436
|
+
processes communicate
|
|
437
|
+
dev : instance of class Channel
|
|
438
|
+
contains details of a device and its serial port
|
|
439
|
+
--------------------------------------------------------------------------
|
|
440
|
+
returns : bool
|
|
441
|
+
--------------------------------------------------------------------------
|
|
442
|
+
"""
|
|
443
|
+
assert dev.manufacturer == 'hameg', 'function only callable for Hameg PSU'
|
|
444
|
+
|
|
445
|
+
command_string = lexicon.power(dev.model, 'read channel mode', channel=dev.channel)
|
|
446
|
+
register = common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
447
|
+
|
|
448
|
+
try:
|
|
449
|
+
regval = int(register)
|
|
450
|
+
except ValueError:
|
|
451
|
+
retval = False
|
|
452
|
+
else:
|
|
453
|
+
constant_curr = (regval & (1 << 0)) != 0
|
|
454
|
+
retval = constant_curr
|
|
455
|
+
|
|
456
|
+
return retval
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def configure_lvpsu(settings, pipeline, ser, dev):
|
|
460
|
+
"""
|
|
461
|
+
Set the power supply voltage and read back the value to confirm it has
|
|
462
|
+
been correctly set.
|
|
463
|
+
|
|
464
|
+
Handles: Agilent E3634A, E3647A; Hameg HMP4040.
|
|
465
|
+
|
|
466
|
+
For Agilent, though the low range is set by default, if the voltage value
|
|
467
|
+
submitted exceeds the range threshold by over a volt or so, the PSU will
|
|
468
|
+
automatically select the high range.
|
|
469
|
+
|
|
470
|
+
--------------------------------------------------------------------------
|
|
471
|
+
args
|
|
472
|
+
settings : dictionary
|
|
473
|
+
contains core information about the test environment
|
|
474
|
+
pipeline : instance of class Production
|
|
475
|
+
contains all the queues through which the production pipeline
|
|
476
|
+
processes communicate
|
|
477
|
+
ser : serial.Serial
|
|
478
|
+
reference for serial port
|
|
479
|
+
dev : instance of class Channel
|
|
480
|
+
contains details of a device and its serial port
|
|
481
|
+
--------------------------------------------------------------------------
|
|
482
|
+
returns
|
|
483
|
+
success : bool
|
|
484
|
+
True if the device was found, False otherwise
|
|
485
|
+
--------------------------------------------------------------------------
|
|
486
|
+
"""
|
|
487
|
+
success = False
|
|
488
|
+
|
|
489
|
+
if settings['reset']:
|
|
490
|
+
if dev.manufacturer == 'hameg':
|
|
491
|
+
# reset to default conditions, output off
|
|
492
|
+
# allow device time to complete reset before sending more commands
|
|
493
|
+
command_string = lexicon.power(dev.model, 'reset')
|
|
494
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
495
|
+
common.log_with_colour(logging.INFO, 'reset')
|
|
496
|
+
time.sleep(0.2)
|
|
497
|
+
else:
|
|
498
|
+
message = f'{dev.manufacturer} {dev.model}: reset not supported'
|
|
499
|
+
common.log_with_colour(logging.WARNING, message)
|
|
500
|
+
|
|
501
|
+
# set and verify current limit
|
|
502
|
+
if settings['current_limit'] is not None:
|
|
503
|
+
current_limit(settings, pipeline, ser, dev)
|
|
504
|
+
|
|
505
|
+
# change output on/off if requested, otherwise do not change it
|
|
506
|
+
if settings['on'] is not None:
|
|
507
|
+
comtxt = 'output on' if settings['on'] else 'output off'
|
|
508
|
+
command_string = lexicon.power(dev.model, comtxt, channel=dev.channel)
|
|
509
|
+
common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
510
|
+
common.log_with_colour(logging.INFO, comtxt)
|
|
511
|
+
|
|
512
|
+
# set voltage
|
|
513
|
+
if settings['voltage'] is None:
|
|
514
|
+
# success is not modified before this point
|
|
515
|
+
return True
|
|
516
|
+
|
|
517
|
+
# constrain the voltage to be set to the given number of decimal places
|
|
518
|
+
voltage = common.decimal_quantize(settings['voltage'], settings['decimal_places'])
|
|
519
|
+
|
|
520
|
+
if voltage < 0:
|
|
521
|
+
message = f'{dev.manufacturer} {dev.model}: cannot use a negative voltage'
|
|
522
|
+
common.log_with_colour(logging.ERROR, message)
|
|
523
|
+
else:
|
|
524
|
+
message = f'voltage set to {common.si_prefix(voltage)}V'
|
|
525
|
+
common.log_with_colour(logging.INFO, message)
|
|
526
|
+
|
|
527
|
+
if dev.manufacturer == 'hameg' and settings['peltier']:
|
|
528
|
+
measured_voltage = read_measured_voltage(settings, pipeline, ser, dev)
|
|
529
|
+
if measured_voltage is not None:
|
|
530
|
+
transition_voltage(settings, pipeline, measured_voltage, voltage, ser, dev)
|
|
531
|
+
else:
|
|
532
|
+
common.set_psu_voltage(settings, pipeline, voltage, ser, dev)
|
|
533
|
+
|
|
534
|
+
# Agilent E3647A seems to require a minimum of half a second to apply
|
|
535
|
+
# the setting before any further RS232 interaction is reliable
|
|
536
|
+
time.sleep(0.5)
|
|
537
|
+
|
|
538
|
+
# Arguably the voltage set for the channel should be read back too, so
|
|
539
|
+
# for the case where the channel output is off, some confirmation that
|
|
540
|
+
# the value has been set correctly can be given to the user.
|
|
541
|
+
#
|
|
542
|
+
# check measured voltage (can only be read back if output is on)
|
|
543
|
+
if not common.report_output_status(ser, pipeline, dev):
|
|
544
|
+
# allow a little time for the voltage to settle before reading
|
|
545
|
+
time.sleep(settings['settlingtime'])
|
|
546
|
+
|
|
547
|
+
if dev.manufacturer == 'hameg' and hmp4040_constant_current(ser, pipeline, dev):
|
|
548
|
+
# When configured as a constant current source, the measured
|
|
549
|
+
# voltage will not match the set voltage
|
|
550
|
+
success = True
|
|
551
|
+
else:
|
|
552
|
+
success = check_measured_voltage(settings, pipeline, ser, dev, voltage)
|
|
553
|
+
else:
|
|
554
|
+
message = 'with PSU output switched off, set voltage cannot be read back'
|
|
555
|
+
common.log_with_colour(logging.WARNING, message)
|
|
556
|
+
common.log_with_colour(logging.WARNING, 'assuming all is well')
|
|
557
|
+
success = True
|
|
558
|
+
|
|
559
|
+
return success
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def configure_hvpsu(settings, pipeline, ser, dev):
|
|
563
|
+
"""
|
|
564
|
+
Amend power supply settings. If a change of voltage has been requested,
|
|
565
|
+
read back the value to confirm it has been correctly set.
|
|
566
|
+
|
|
567
|
+
Process requests relevant to the selected power supply in this order:
|
|
568
|
+
|
|
569
|
+
* reset
|
|
570
|
+
* set range
|
|
571
|
+
* select front/rear output
|
|
572
|
+
* output on/off
|
|
573
|
+
* set voltage
|
|
574
|
+
|
|
575
|
+
Handles: Keithley 2410, 2614b; ISEG SHQ.
|
|
576
|
+
|
|
577
|
+
--------------------------------------------------------------------------
|
|
578
|
+
args
|
|
579
|
+
settings : dictionary
|
|
580
|
+
contains core information about the test environment
|
|
581
|
+
pipeline : instance of class Production
|
|
582
|
+
contains all the queues through which the production pipeline
|
|
583
|
+
processes communicate
|
|
584
|
+
ser : serial.Serial
|
|
585
|
+
reference for serial port
|
|
586
|
+
dev : instance of class Channel
|
|
587
|
+
contains details of a device and its serial port
|
|
588
|
+
--------------------------------------------------------------------------
|
|
589
|
+
returns
|
|
590
|
+
success : bool
|
|
591
|
+
True if the device was found, False otherwise
|
|
592
|
+
--------------------------------------------------------------------------
|
|
593
|
+
"""
|
|
594
|
+
success = False
|
|
595
|
+
|
|
596
|
+
# ------------------------------------------------------------------------
|
|
597
|
+
# protection against accidental forward bias operation
|
|
598
|
+
# ------------------------------------------------------------------------
|
|
599
|
+
|
|
600
|
+
if settings['voltage'] > 0 and not settings['forwardbias']:
|
|
601
|
+
common.log_with_colour(
|
|
602
|
+
logging.ERROR,
|
|
603
|
+
'HV PSU positive voltage specified without --forwardbias'
|
|
604
|
+
)
|
|
605
|
+
return success
|
|
606
|
+
|
|
607
|
+
if settings['voltage'] < 0 and settings['forwardbias']:
|
|
608
|
+
common.log_with_colour(
|
|
609
|
+
logging.ERROR,
|
|
610
|
+
'HV PSU negative voltage specified with --forwardbias'
|
|
611
|
+
)
|
|
612
|
+
return success
|
|
613
|
+
|
|
614
|
+
# ------------------------------------------------------------------------
|
|
615
|
+
|
|
616
|
+
if dev.manufacturer == 'keithley':
|
|
617
|
+
command_string = lexicon.power(dev.model, 'clear event registers')
|
|
618
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
619
|
+
|
|
620
|
+
if settings['reset']:
|
|
621
|
+
if dev.manufacturer == 'keithley' and dev.model == '2410':
|
|
622
|
+
# reset to default conditions, output off
|
|
623
|
+
# allow device time to complete reset before sending more commands
|
|
624
|
+
common.log_with_colour(logging.INFO, 'reset')
|
|
625
|
+
command_string = lexicon.power(dev.model, 'reset')
|
|
626
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
627
|
+
time.sleep(0.2)
|
|
628
|
+
else:
|
|
629
|
+
message = f'{dev.manufacturer} {dev.model}: reset not supported'
|
|
630
|
+
common.log_with_colour(logging.WARNING, message)
|
|
631
|
+
|
|
632
|
+
# set and verify current limit
|
|
633
|
+
if settings['current_limit'] is not None:
|
|
634
|
+
current_limit(settings, pipeline, ser, dev)
|
|
635
|
+
|
|
636
|
+
# set voltage
|
|
637
|
+
if settings['voltage'] is not None:
|
|
638
|
+
# as a general rule this script should avoid making changes to the
|
|
639
|
+
# user's power supply settings, since the context it's being used in
|
|
640
|
+
# may be unclear. However, for this project the highest voltage range
|
|
641
|
+
# will always be used, and it is not unreasonable to set it here.
|
|
642
|
+
#
|
|
643
|
+
# It would be more correct for 'configure range' to be performed
|
|
644
|
+
# immediately after a reset (above, selectively). Performing it here
|
|
645
|
+
# (always) is more friendly to the user, e.g. if the user has just
|
|
646
|
+
# switched on the power supply (it may well be in a low voltage range)
|
|
647
|
+
# and runs this script with a high voltage, it will generate a series
|
|
648
|
+
# of
|
|
649
|
+
# "settings conflict" errors (on the 2410) as the script tries to set
|
|
650
|
+
# voltages higher than the current range allows.
|
|
651
|
+
common.log_with_colour(logging.INFO, 'configure range')
|
|
652
|
+
command_string = lexicon.power(dev.model, 'configure range', channel=dev.channel)
|
|
653
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
654
|
+
|
|
655
|
+
# Change front/rear routing if requested, otherwise do not change it.
|
|
656
|
+
#
|
|
657
|
+
# Since changing this setting will turn the output off, it must be performed
|
|
658
|
+
# before processing any user request to turn the output on or off.
|
|
659
|
+
if settings['rear'] is not None:
|
|
660
|
+
if dev.manufacturer == 'keithley' and dev.model == '2410':
|
|
661
|
+
destination = 'REAR' if settings['rear'] else 'FRON'
|
|
662
|
+
command_string = lexicon.power(dev.model, 'set route', destination)
|
|
663
|
+
common.send_command(pipeline, ser, dev, command_string)
|
|
664
|
+
else:
|
|
665
|
+
message = f'{dev.manufacturer} {dev.model}: front/rear not supported'
|
|
666
|
+
common.log_with_colour(logging.WARNING, message)
|
|
667
|
+
|
|
668
|
+
# change output on/off if requested, otherwise do not change it
|
|
669
|
+
if settings['on'] is not None:
|
|
670
|
+
if dev.manufacturer == 'keithley':
|
|
671
|
+
comtxt = 'output on' if settings['on'] else 'output off'
|
|
672
|
+
common.log_with_colour(logging.INFO, comtxt)
|
|
673
|
+
command_string = lexicon.power(dev.model, comtxt, channel=dev.channel)
|
|
674
|
+
common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
675
|
+
else:
|
|
676
|
+
message = f'{dev.manufacturer} {dev.model}: output on/off not supported'
|
|
677
|
+
common.log_with_colour(logging.WARNING, message)
|
|
678
|
+
|
|
679
|
+
##############################################################################
|
|
680
|
+
# set voltage
|
|
681
|
+
if settings['voltage'] is None:
|
|
682
|
+
# success is not modified before this point
|
|
683
|
+
return True
|
|
684
|
+
|
|
685
|
+
# constrain the voltage to be set to the given number of decimal places
|
|
686
|
+
voltage = common.decimal_quantize(settings['voltage'], settings['decimal_places'])
|
|
687
|
+
message = f'setting voltage: {common.si_prefix(voltage)}V'
|
|
688
|
+
common.log_with_colour(logging.INFO, message)
|
|
689
|
+
|
|
690
|
+
# set voltage
|
|
691
|
+
if settings['immediate']:
|
|
692
|
+
common.set_psu_voltage(settings, pipeline, voltage, ser, dev)
|
|
693
|
+
else:
|
|
694
|
+
measured_voltage = read_measured_voltage(settings, pipeline, ser, dev)
|
|
695
|
+
if measured_voltage is not None:
|
|
696
|
+
transition_voltage(settings, pipeline, measured_voltage, voltage, ser, dev)
|
|
697
|
+
|
|
698
|
+
# check set voltage (can only be read back if output is on)
|
|
699
|
+
if not common.report_output_status(ser, pipeline, dev):
|
|
700
|
+
# allow a little time for the voltage to settle before reading
|
|
701
|
+
time.sleep(settings['settlingtime'])
|
|
702
|
+
success = check_measured_voltage(settings, pipeline, ser, dev, voltage)
|
|
703
|
+
else:
|
|
704
|
+
# keithley 2410, 2614b
|
|
705
|
+
message = 'with PSU output switched off, set voltage cannot be read back'
|
|
706
|
+
common.log_with_colour(logging.WARNING, message)
|
|
707
|
+
common.log_with_colour(logging.WARNING, 'assuming all is well')
|
|
708
|
+
success = True
|
|
709
|
+
|
|
710
|
+
return success
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def transition_voltage(settings, pipeline, initial_voltage, target_voltage, ser, dev):
|
|
714
|
+
"""
|
|
715
|
+
Gradual voltage transitions.
|
|
716
|
+
|
|
717
|
+
--------------------------------------------------------------------------
|
|
718
|
+
args
|
|
719
|
+
settings : dictionary
|
|
720
|
+
contains core information about the test environment
|
|
721
|
+
pipeline : instance of class Production
|
|
722
|
+
contains all the queues through which the production pipeline
|
|
723
|
+
processes communicate
|
|
724
|
+
initial_voltage : float
|
|
725
|
+
set voltage at present
|
|
726
|
+
target_voltage : float
|
|
727
|
+
voltage to transition to
|
|
728
|
+
ser : serial.Serial
|
|
729
|
+
reference for serial port
|
|
730
|
+
dev : instance of class Channel
|
|
731
|
+
contains details of a device and its serial port
|
|
732
|
+
--------------------------------------------------------------------------
|
|
733
|
+
returns : none
|
|
734
|
+
--------------------------------------------------------------------------
|
|
735
|
+
"""
|
|
736
|
+
if initial_voltage != target_voltage:
|
|
737
|
+
message = f'{dev.ident}, transitioning from {initial_voltage}V to {target_voltage}V'
|
|
738
|
+
common.log_with_colour(logging.INFO, message)
|
|
739
|
+
|
|
740
|
+
if dev.manufacturer == 'iseg':
|
|
741
|
+
# use power supply's internal ramp feature to avoid having manage
|
|
742
|
+
# the ISEG SHQ's tardy response time
|
|
743
|
+
|
|
744
|
+
# read voltage rate of change
|
|
745
|
+
command_string = lexicon.power(dev.model,
|
|
746
|
+
'read max rate of change',
|
|
747
|
+
channel=dev.channel)
|
|
748
|
+
max_vroc = common.atomic_send_command_read_response(pipeline, ser, dev,
|
|
749
|
+
command_string)
|
|
750
|
+
|
|
751
|
+
# set voltage rate of change for ramp
|
|
752
|
+
if settings['voltspersecond'] is None:
|
|
753
|
+
volts_per_second = 10
|
|
754
|
+
else:
|
|
755
|
+
volts_per_second = settings['voltspersecond']
|
|
756
|
+
|
|
757
|
+
command_string = lexicon.power(dev.model,
|
|
758
|
+
'set voltage max rate of change',
|
|
759
|
+
volts_per_second,
|
|
760
|
+
channel=dev.channel)
|
|
761
|
+
common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
762
|
+
|
|
763
|
+
# auto ramp will progress towards the given voltage at the given rate
|
|
764
|
+
command_string = lexicon.power(dev.model,
|
|
765
|
+
'set voltage',
|
|
766
|
+
abs(target_voltage),
|
|
767
|
+
channel=dev.channel)
|
|
768
|
+
|
|
769
|
+
common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
770
|
+
|
|
771
|
+
# wait to reach given voltage
|
|
772
|
+
common.wait_for_voltage_to_stabilise(ser, pipeline, dev, target_voltage)
|
|
773
|
+
|
|
774
|
+
# revert voltage rate of change to original value
|
|
775
|
+
command_string = lexicon.power(dev.model,
|
|
776
|
+
'set voltage max rate of change',
|
|
777
|
+
max_vroc,
|
|
778
|
+
channel=dev.channel)
|
|
779
|
+
common.atomic_send_command_read_response(pipeline, ser, dev, command_string)
|
|
780
|
+
|
|
781
|
+
elif dev.manufacturer == 'keithley':
|
|
782
|
+
timestamp = None
|
|
783
|
+
duration = 1 if settings['voltspersecond'] is None else 10 / settings['voltspersecond']
|
|
784
|
+
step = 10
|
|
785
|
+
|
|
786
|
+
for voltage in sequence.to_original(initial_voltage, target_voltage, step):
|
|
787
|
+
timestamp = common.rate_limit(timestamp, duration)
|
|
788
|
+
common.set_psu_voltage(settings, pipeline, voltage, ser, dev)
|
|
789
|
+
|
|
790
|
+
common.log_with_colour(logging.INFO, f'{dev.ident}, transition complete')
|
|
791
|
+
|
|
792
|
+
elif dev.model == 'hmp4040':
|
|
793
|
+
# quick addition - special case for Peltier usage
|
|
794
|
+
# aim for 2V in 30s, 1.5s per 0.1V step
|
|
795
|
+
initial_voltage = float(initial_voltage)
|
|
796
|
+
target_voltage = float(target_voltage)
|
|
797
|
+
timestamp = None
|
|
798
|
+
step = 0.1 if target_voltage > initial_voltage else -0.1
|
|
799
|
+
duration = 30 / (2 / abs(step))
|
|
800
|
+
|
|
801
|
+
for voltage in np.arange(initial_voltage + step, target_voltage + step, step):
|
|
802
|
+
timestamp = common.rate_limit(timestamp, duration)
|
|
803
|
+
common.set_psu_voltage(settings, pipeline, voltage, ser, dev)
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
##############################################################################
|
|
807
|
+
# main
|
|
808
|
+
##############################################################################
|
|
809
|
+
|
|
810
|
+
def main():
|
|
811
|
+
""" set the voltage on a power supply connected by RS232 """
|
|
812
|
+
|
|
813
|
+
status = types.SimpleNamespace(success=0, unreserved_error_code=3)
|
|
814
|
+
|
|
815
|
+
success = False
|
|
816
|
+
settings = {
|
|
817
|
+
'alias': None,
|
|
818
|
+
'channel': None,
|
|
819
|
+
'current_limit': None,
|
|
820
|
+
'debug': None,
|
|
821
|
+
'decimal_places': 2,
|
|
822
|
+
'forwardbias': False,
|
|
823
|
+
'immediate': False,
|
|
824
|
+
'manufacturer': None,
|
|
825
|
+
'model': None,
|
|
826
|
+
'on': None,
|
|
827
|
+
'peltier': False,
|
|
828
|
+
'port': None,
|
|
829
|
+
'quiet': False,
|
|
830
|
+
'rear': None,
|
|
831
|
+
'reset': False,
|
|
832
|
+
'serial': None,
|
|
833
|
+
'settlingtime': 0.5,
|
|
834
|
+
'verbose': None,
|
|
835
|
+
'voltage': None,
|
|
836
|
+
'voltspersecond': 10
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
check_arguments(settings)
|
|
840
|
+
|
|
841
|
+
##########################################################################
|
|
842
|
+
# enable logging to screen
|
|
843
|
+
##########################################################################
|
|
844
|
+
|
|
845
|
+
logging.basicConfig(
|
|
846
|
+
level=logging.DEBUG,
|
|
847
|
+
format='%(asctime)s : %(levelname)s : %(message)s')
|
|
848
|
+
|
|
849
|
+
# enable logging to screen
|
|
850
|
+
console = logging.StreamHandler()
|
|
851
|
+
console.setLevel(logging.INFO)
|
|
852
|
+
|
|
853
|
+
# set logging time to UTC to match session timestamp
|
|
854
|
+
logging.Formatter.converter = time.gmtime
|
|
855
|
+
|
|
856
|
+
##########################################################################
|
|
857
|
+
# filter power supplies from cache leaving just a single channel
|
|
858
|
+
##########################################################################
|
|
859
|
+
|
|
860
|
+
psus = common.cache_read(['hvpsu', 'lvpsu'])
|
|
861
|
+
channels = common.ports_to_channels(settings, psus)
|
|
862
|
+
|
|
863
|
+
if settings['verbose']:
|
|
864
|
+
print('detected power supply channels:')
|
|
865
|
+
for channel in channels:
|
|
866
|
+
print(channel)
|
|
867
|
+
print('search terms:')
|
|
868
|
+
maxlen = max(len(x) for x in settings)
|
|
869
|
+
for setting, value in settings.items():
|
|
870
|
+
print(f'{setting:>{maxlen}} {value}')
|
|
871
|
+
|
|
872
|
+
if not common.unique(settings, psus, channels):
|
|
873
|
+
print('could not identify a single power supply channel')
|
|
874
|
+
print('check use of --manufacturer, --model, --serial, --port, and --channel')
|
|
875
|
+
sys.exit()
|
|
876
|
+
|
|
877
|
+
##########################################################################
|
|
878
|
+
# set up resources for threads
|
|
879
|
+
#
|
|
880
|
+
# this is overkill since only one power supply channel will be used
|
|
881
|
+
# but does allow the same well-tested interface employed by other scripts
|
|
882
|
+
# in the suite to be used
|
|
883
|
+
##########################################################################
|
|
884
|
+
|
|
885
|
+
class Production:
|
|
886
|
+
""" Locks to support threaded operation. """
|
|
887
|
+
portaccess = {port: threading.Lock()
|
|
888
|
+
for port in {channel.port for channel in channels}}
|
|
889
|
+
|
|
890
|
+
pipeline = Production()
|
|
891
|
+
|
|
892
|
+
##########################################################################
|
|
893
|
+
# Check status of outputs and interlock (inhibit) on all power supplies
|
|
894
|
+
##########################################################################
|
|
895
|
+
|
|
896
|
+
common.initial_power_supply_check(settings, pipeline, psus, channels, psuset=True)
|
|
897
|
+
|
|
898
|
+
##########################################################################
|
|
899
|
+
# display details of selected power supply
|
|
900
|
+
##########################################################################
|
|
901
|
+
|
|
902
|
+
try:
|
|
903
|
+
channel = channels[0]
|
|
904
|
+
except IndexError:
|
|
905
|
+
pass
|
|
906
|
+
else:
|
|
907
|
+
text = f'selected power supply: {channel.manufacturer} {channel.model}'
|
|
908
|
+
if channel.serial_number:
|
|
909
|
+
text += f' s.no. {channel.serial_number}'
|
|
910
|
+
if channel.channel:
|
|
911
|
+
text += f' channel {channel.channel}'
|
|
912
|
+
if settings['port'] is not None:
|
|
913
|
+
text += f' port {channel.port}'
|
|
914
|
+
common.log_with_colour(logging.INFO, text, quiet=settings['quiet'])
|
|
915
|
+
|
|
916
|
+
##########################################################################
|
|
917
|
+
# set values on given power supply channel
|
|
918
|
+
##########################################################################
|
|
919
|
+
|
|
920
|
+
setpsu = {'lvpsu': configure_lvpsu, 'hvpsu': configure_hvpsu}
|
|
921
|
+
|
|
922
|
+
with serial.Serial(port=channel.port) as ser:
|
|
923
|
+
ser.apply_settings(channel.config)
|
|
924
|
+
ser.reset_input_buffer()
|
|
925
|
+
ser.reset_output_buffer()
|
|
926
|
+
success = setpsu[channel.category](settings, pipeline, ser, channel)
|
|
927
|
+
if success:
|
|
928
|
+
common.log_with_colour(
|
|
929
|
+
logging.INFO, 'operation successful',
|
|
930
|
+
quiet=settings['quiet']
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
return status.success if success else status.unreserved_error_code
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
##############################################################################
|
|
937
|
+
if __name__ == '__main__':
|
|
938
|
+
sys.exit(main())
|