mmcb-rs232-avt 1.0.20__py3-none-any.whl → 1.1.37__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.
mmcbrs232/dmm.py ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Read values from the Keithley DMM6500.
4
+ """
5
+
6
+ import argparse
7
+ import fcntl
8
+ import itertools
9
+ import sys
10
+ import time
11
+
12
+ from mmcbrs232 import common
13
+ from mmcbrs232 import dmm_interface
14
+
15
+
16
+ ##############################################################################
17
+ # command line option handler
18
+ ##############################################################################
19
+
20
+
21
+ def check_arguments():
22
+ """
23
+ handle command line options
24
+
25
+ --------------------------------------------------------------------------
26
+ args : none
27
+ --------------------------------------------------------------------------
28
+ returns
29
+ args : int, <class 'argparse.Namespace'>
30
+ --------------------------------------------------------------------------
31
+ """
32
+ parser = argparse.ArgumentParser(
33
+ description='Reads voltage or current from the Keithley DMM6500.'
34
+ )
35
+
36
+ parser.add_argument(
37
+ 'channel', nargs='?', metavar='channel',
38
+ help=(
39
+ 'Select measurement channel. '
40
+ 'For use with the 2001-SCAN card.'
41
+ ),
42
+ choices=range(1, 11),
43
+ type=int, default=None)
44
+
45
+ parser.add_argument(
46
+ '-p', '--plain',
47
+ action='store_true',
48
+ help='print the requested figure only (deprecated: use -q instead)')
49
+ parser.add_argument(
50
+ '-q', '--quiet',
51
+ action='store_true',
52
+ help='print the requested figure only')
53
+ parser.add_argument(
54
+ '-c', '--capacitance',
55
+ action='store_true',
56
+ help='Read capacitance (F)')
57
+ parser.add_argument(
58
+ '--currentac',
59
+ action='store_true',
60
+ help='Read AC current (A)')
61
+ parser.add_argument(
62
+ '-i', '--currentdc',
63
+ action='store_true',
64
+ help='Read DC current (A)')
65
+ parser.add_argument(
66
+ '-r', '--resistance',
67
+ action='store_true',
68
+ help='Read two-wire resistance (\u03a9)')
69
+ parser.add_argument(
70
+ '-t', '--temperature',
71
+ action='store_true',
72
+ help='Read temperature (\u00b0C)')
73
+ parser.add_argument(
74
+ '--voltageac',
75
+ action='store_true',
76
+ help='Read AC voltage (V)')
77
+ parser.add_argument(
78
+ '-v', '--voltagedc',
79
+ action='store_true',
80
+ help='Read DC voltage (V)')
81
+
82
+ args = parser.parse_args()
83
+
84
+ channel = args.channel
85
+ del args.channel
86
+
87
+ return channel, args
88
+
89
+
90
+ ##############################################################################
91
+ # serial port identification
92
+ ##############################################################################
93
+
94
+
95
+ def port_name():
96
+ """
97
+ Get the port name from the cache ASSUMING that there's only one instrument
98
+ in the test environment.
99
+
100
+ --------------------------------------------------------------------------
101
+ args : none
102
+ --------------------------------------------------------------------------
103
+ returns : string or None
104
+ e.g. '/dev/ttyUSB0'
105
+ --------------------------------------------------------------------------
106
+ """
107
+ _cached_instruments = common.cache_read({'instrument'})
108
+
109
+ _channels = []
110
+ for _port, _details in _cached_instruments.items():
111
+ (
112
+ _config,
113
+ _device_type,
114
+ _serial_number,
115
+ _model,
116
+ _manufacturer,
117
+ _device_channels,
118
+ _release_delay,
119
+ ) = _details
120
+
121
+ for _device_channel in _device_channels:
122
+ _channels.append(
123
+ common.Channel(
124
+ _port,
125
+ _config,
126
+ _serial_number,
127
+ _model,
128
+ _manufacturer,
129
+ _device_channel,
130
+ _device_type,
131
+ _release_delay,
132
+ None,
133
+ )
134
+ )
135
+
136
+ try:
137
+ _channel = _channels[0]
138
+ except IndexError:
139
+ return None
140
+
141
+ return _channel.port
142
+
143
+
144
+ ##############################################################################
145
+ # main
146
+ ##############################################################################
147
+
148
+
149
+ def main():
150
+ """ Read values from the Keithley DMM6500 """
151
+
152
+ channel, args = check_arguments()
153
+
154
+ with open(common.RS232_LOCK_GLOBAL, 'a') as lock_file:
155
+
156
+ # acquire the RS232 global lock
157
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
158
+
159
+ # --------------------------------------------------------------------
160
+
161
+ dmm = dmm_interface.Dmm6500()
162
+ if dmm.init_failed:
163
+ print('could not initialise serial port')
164
+ return 3
165
+
166
+ if channel is not None:
167
+ # isolate all relays
168
+ dmm.mux_open_all_channels()
169
+ # enable relay for given channel
170
+ dmm.mux_close_channel(channel)
171
+
172
+ configure = {
173
+ 'capacitance': dmm.configure_read_capacitance,
174
+ 'currentac': dmm.configure_read_ac_current,
175
+ 'currentdc': dmm.configure_read_dc_current,
176
+ 'resistance': dmm.configure_read_resistance,
177
+ 'temperature': dmm.configure_read_temperature,
178
+ 'voltagedc': dmm.configure_read_dc_voltage,
179
+ 'voltageac': dmm.configure_read_ac_voltage,
180
+ }
181
+
182
+ # AC quantities sometimes return None on the first attempt
183
+ retries = 3
184
+
185
+ success = []
186
+
187
+ for function, required in vars(args).items():
188
+ if not required or function == 'plain' or function == 'quiet':
189
+ continue
190
+
191
+ configure[function]()
192
+
193
+ value = None
194
+ for _ in itertools.repeat(None, retries):
195
+ value = dmm.read_value()
196
+ if value is not None:
197
+ success.append(True)
198
+ break
199
+ time.sleep(0.1)
200
+
201
+ if args.plain or args.quiet:
202
+ print(value)
203
+ else:
204
+ print(f'{function}, {common.si_prefix(value)}, {value}')
205
+
206
+ if channel is not None:
207
+ # isolate relay
208
+ dmm.mux_open_channel(channel)
209
+
210
+ # --------------------------------------------------------------------
211
+
212
+ # release the RS232 global lock
213
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
214
+
215
+ return 0 if any(success) else 3
216
+
217
+
218
+ ##############################################################################
219
+ if __name__ == '__main__':
220
+ sys.exit(main())
@@ -9,8 +9,8 @@ import weakref
9
9
 
10
10
  import serial
11
11
 
12
- from mmcb_rs232 import common
13
- from mmcb_rs232 import lexicon
12
+ from mmcbrs232 import common
13
+ from mmcbrs232 import lexicon
14
14
 
15
15
 
16
16
  class Production:
@@ -96,7 +96,7 @@ class Dmm6500:
96
96
  """check (indirectly) if the serial port has been closed"""
97
97
  return not self._finalizer.alive
98
98
 
99
- def _send_command(self, command):
99
+ def _send_command(self, command, argument1=None):
100
100
  """
101
101
  Issue command to instrument.
102
102
 
@@ -111,7 +111,7 @@ class Dmm6500:
111
111
  self._pipeline,
112
112
  self._ser,
113
113
  self._channel,
114
- lexicon.instrument(self._channel.model, command),
114
+ lexicon.instrument(self._channel.model, command, argument1),
115
115
  )
116
116
 
117
117
  def configure_read_capacitance(self):
@@ -160,3 +160,24 @@ class Dmm6500:
160
160
  value = None
161
161
 
162
162
  return value
163
+
164
+ def mux_close_channel(self, channel):
165
+ """
166
+ Close channel (switch mux relay output to rear terminals; relay on).
167
+ For use with the 2001-SCAN card.
168
+ """
169
+ self._send_command('mux_close_channel', channel)
170
+
171
+ def mux_open_channel(self, channel):
172
+ """
173
+ Open channel (isolate relay from rear terminals).
174
+ For use with the 2001-SCAN card.
175
+ """
176
+ self._send_command('mux_open_channel', channel)
177
+
178
+ def mux_open_all_channels(self):
179
+ """
180
+ Open all channels (isolate all relays from rear terminals).
181
+ For use with the 2001-SCAN card.
182
+ """
183
+ self._send_command('mux_open_all_channels')
@@ -25,6 +25,7 @@ import contextlib
25
25
  import csv
26
26
  import ctypes
27
27
  import datetime
28
+ import fcntl
28
29
  import functools
29
30
  import itertools
30
31
  import logging
@@ -54,9 +55,9 @@ from yoctopuce import yocto_api as yapi
54
55
  from yoctopuce import yocto_temperature as ytemp
55
56
  from yoctopuce import yocto_humidity as yhumi
56
57
 
57
- from mmcb_rs232 import common
58
- from mmcb_rs232 import lexicon
59
- from mmcb_rs232 import sequence
58
+ from mmcbrs232 import common
59
+ from mmcbrs232 import lexicon
60
+ from mmcbrs232 import sequence
60
61
 
61
62
 
62
63
  ##############################################################################
@@ -994,7 +995,7 @@ def sense(pipeline, zmq_skt, settings):
994
995
  pi@raspberrypi:~/dev/1d-phantom/utilities $ sudo netstat -ltnp
995
996
  Active Internet connections (only servers)
996
997
  Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
997
- tcp 0 0 0.0.0.0:5556 0.0.0.0:* LISTEN 32539/python3
998
+ tcp 0 0 0.0.0.0:5566 0.0.0.0:* LISTEN 32539/python3
998
999
  tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 776/sshd
999
1000
  tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 27597/cupsd
1000
1001
  tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 992/exim4
@@ -1030,7 +1031,7 @@ def sense(pipeline, zmq_skt, settings):
1030
1031
  """
1031
1032
  # configure communication with sense.py
1032
1033
  # Use REP/REQ (with handshaking) instead of PUB/SUB to avoid packet loss
1033
- port = 5556
1034
+ port = 5566
1034
1035
  try:
1035
1036
  zmq_skt.bind(f'tcp://*:{port}')
1036
1037
  except zmq.error.ZMQError as zerr:
@@ -2775,7 +2776,19 @@ def main():
2775
2776
  # Check status of outputs and interlock (inhibit) on all power supplies
2776
2777
  ##########################################################################
2777
2778
 
2778
- common.initial_power_supply_check(settings, pipeline, psus, channels)
2779
+ with open(common.RS232_LOCK_GLOBAL, 'a') as lock_file:
2780
+
2781
+ # acquire the RS232 global lock
2782
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
2783
+
2784
+ # --------------------------------------------------------------------
2785
+
2786
+ common.initial_power_supply_check(settings, pipeline, psus, channels)
2787
+
2788
+ # --------------------------------------------------------------------
2789
+
2790
+ # release the RS232 global lock
2791
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
2779
2792
 
2780
2793
  ##########################################################################
2781
2794
  # detect presence of temperature and humidity sensors
@@ -2820,10 +2833,23 @@ def main():
2820
2833
 
2821
2834
  _gidfp_pf = functools.partial(get_iv_data_from_psu, settings=settings,
2822
2835
  pipeline=pipeline, graceful_quit=graceful_quit)
2823
- with cf.ThreadPoolExecutor() as executor:
2824
- board_iv = (executor.submit(_gidfp_pf, channel) for channel in channels)
2825
- for future in cf.as_completed(board_iv):
2826
- consignment.packets.append(future.result())
2836
+
2837
+ with open(common.RS232_LOCK_GLOBAL, 'a') as lock_file:
2838
+
2839
+ # acquire the RS232 global lock
2840
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
2841
+
2842
+ # --------------------------------------------------------------------
2843
+
2844
+ with cf.ThreadPoolExecutor() as executor:
2845
+ board_iv = (executor.submit(_gidfp_pf, channel) for channel in channels)
2846
+ for future in cf.as_completed(board_iv):
2847
+ consignment.packets.append(future.result())
2848
+
2849
+ # --------------------------------------------------------------------
2850
+
2851
+ # release the RS232 global lock
2852
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
2827
2853
 
2828
2854
  ##########################################################################
2829
2855
  # release resources for YoctoPuce API and threads
@@ -563,6 +563,18 @@ def instrument(model, command, argument1=None, argument2=None):
563
563
  'identify': '*IDN?',
564
564
  'read value': 'print(dmm.measure.read())',
565
565
  'reset': 'dmm.reset()',
566
+
567
+ # --------------------------------------------------------------------
568
+ # multiplexer 2001-SCAN expansion card
569
+ # --------------------------------------------------------------------
570
+
571
+ 'mux_close_channel': f'channel.close("{argument1}")',
572
+ 'mux_close_all_channels': 'channel.close("1,2,3,4,5,6,7,8,9,10")',
573
+ 'mux_open_channel': f'channel.open("{argument1}")',
574
+ 'mux_open_all_channels': 'channel.open("1,2,3,4,5,6,7,8,9,10")',
575
+
576
+ # this should return '[1]=0' (channel open) or '[1]=channel.IND_CLOSED'
577
+ 'read channel getstate': f'print(channel.getstate("{argument1}"))',
566
578
  }
567
579
 
568
580
  instructions = {