MultiPyVu 3.3.0__tar.gz → 3.5.4__tar.gz
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.
- {multipyvu-3.3.0/src/MultiPyVu.egg-info → multipyvu-3.5.4}/PKG-INFO +50 -26
- {multipyvu-3.3.0 → multipyvu-3.5.4}/README.md +43 -23
- {multipyvu-3.3.0 → multipyvu-3.5.4}/setup.py +4 -4
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/BRT.py +25 -16
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandChamber.py +9 -5
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandField.py +29 -25
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandMultiVu.py +1 -1
- multipyvu-3.5.4/src/MultiPyVu/CommandRotator.py +567 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandSdo.py +14 -10
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandTemperature.py +5 -3
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/Command_factory.py +30 -2
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuClient.py +68 -8
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuClient_base.py +190 -88
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/MultiVuDataFile.py +5 -6
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuServer.py +14 -14
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/SocketMessage.py +175 -158
- multipyvu-3.5.4/src/MultiPyVu/SocketMessageClient.py +503 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/SocketMessageServer.py +387 -207
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/__version.py +1 -1
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/instrument.py +3 -3
- multipyvu-3.5.4/src/MultiPyVu/logging_config.py +76 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/project_vars.py +7 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4/src/MultiPyVu.egg-info}/PKG-INFO +50 -26
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/SOURCES.txt +1 -0
- multipyvu-3.3.0/src/MultiPyVu/SocketMessageClient.py +0 -290
- multipyvu-3.3.0/src/MultiPyVu/logging_config.py +0 -72
- {multipyvu-3.3.0 → multipyvu-3.5.4}/LICENSE +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/pyproject.toml +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/setup.cfg +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandWaitFor.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/ICommand.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/IEventManager.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/IServer.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/__init__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/__version.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/is_pathname_valid.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/ParseInputs.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/__init__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/__main__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/check_windows_esc.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/exceptions.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/font/Play-Bold.ttf +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/font/Play-Regular.ttf +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/font/__init__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/generate_sdo_objects.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/Controller.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/IController.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/IView.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/ViewFactory.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/ViewTk.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/__init__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/images/Q.png +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/images/QD_logo.jpg +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/images/__init__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/logging_config.yaml +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/scripts/__init__.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/scripts/helper_scripts.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/scripts/whats_my_ip_address.cmd +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/sdo_object.py +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/dependency_links.txt +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/requires.txt +0 -0
- {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: MultiPyVu
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5.4
|
|
4
4
|
Summary: Control MultiVu using Python
|
|
5
5
|
Author-email: Damon D Jackson <djackson@qdusa.com>
|
|
6
6
|
Maintainer-email: "Quantum Design, Inc." <apps@qdusa.com>
|
|
@@ -35,13 +35,17 @@ Classifier: Intended Audience :: Science/Research
|
|
|
35
35
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
36
36
|
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
37
37
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
38
|
-
Requires-Python: >=3.
|
|
38
|
+
Requires-Python: >=3.9
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
40
40
|
License-File: LICENSE
|
|
41
41
|
Requires-Dist: pandas
|
|
42
42
|
Requires-Dist: pywin32>=300; platform_system == "Windows"
|
|
43
43
|
Requires-Dist: pillow
|
|
44
44
|
Requires-Dist: pyyaml
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
Dynamic: requires-dist
|
|
47
|
+
Dynamic: requires-python
|
|
48
|
+
Dynamic: summary
|
|
45
49
|
|
|
46
50
|

|
|
47
51
|
# MultiPyVu
|
|
@@ -60,6 +64,7 @@ Requires-Dist: pyyaml
|
|
|
60
64
|
* [is_steady()](#is_steady)
|
|
61
65
|
* [get_aux_temperature()](#aux_therm)
|
|
62
66
|
* [resistivity option](#brt)
|
|
67
|
+
* [horizontal rotator option](#rotator)
|
|
63
68
|
* [Saving & Opening a MultiVu Data File](#save)
|
|
64
69
|
* [Querying the Server Status](#server_status)
|
|
65
70
|
* [Testing the Server Using Scaffolding](#scaffolding)
|
|
@@ -77,7 +82,7 @@ The components of MultiPyVu enable access to the set and read the temperature, f
|
|
|
77
82
|
|
|
78
83
|
### Module Requirements<a class="anchor" id="requirements"></a>
|
|
79
84
|
MultiPyVu uses the following modules:
|
|
80
|
-
- python version 3.
|
|
85
|
+
- python version 3.10 or higher
|
|
81
86
|
- pywin32 - version 300 or higher.
|
|
82
87
|
- pandas - data read back from a *.dat* file is a Pandas Dataframe
|
|
83
88
|
- pillow - supports server gui
|
|
@@ -106,6 +111,7 @@ Measuring\example_stabilize.py | Measures the temperature while stabilizing at e
|
|
|
106
111
|
Measuring\example_sweep.py | Measures the temperature while sweeping the temperature.
|
|
107
112
|
Options\example_aux_therm.py | Gets the temperature of the auxilary thermometer from the OptiCool.
|
|
108
113
|
Options\example_resistivity_option.py | Demonstrates how to set up the BRT module bridges for resistivity measurements and how to save the data.
|
|
114
|
+
Options\example_horizontal_rotator.py | Demonstrates how to move the horizontal rotator to a new position.
|
|
109
115
|
UsefulBatchFiles\whats_my_ip_address.cmd | This batch file script prints out the IP address for the computer, providing an easy way to get the server IP address needed for remote operation. Alternatively, the gui can be used to get the IP address by calling ```python -m MultiPyVu```.
|
|
110
116
|
UsefulBatchFiles\show_process_with_port.cmd | Shows that active processes are running and what port is being used. By default, this looks for processes running on port 5000. To query a different port number, append it to the command line arguments. ```$ show_processes_with_port.cmd 4567```
|
|
111
117
|
UsefulBatchFiles\RunServer.cmd | This runs the server GUI.
|
|
@@ -138,22 +144,23 @@ $ python -m MultiPyVu -get_ip
|
|
|
138
144
|
On the client PC, update the 'host' variable of the 'example_Remote-Client.py' script *with the same server PC IPV4 address* and run it. The script will report the present temperature and field value/status a few times to show the connection is functioning.
|
|
139
145
|
|
|
140
146
|
**Next Steps:**
|
|
141
|
-
It may sometimes be desirable to combine the sample environment parameters (temperature, field) with readings from a user's own instrumentation into a single *.dat* file which can be plotted in MultiVu. This functionality, accomplished using the MultiPyVu.DataFile module, is demonstrated using PyVISA to communicate with a VISA-compatible instrument in the 'example_MvDataFile_VISA.py' example. Note that for this routine to execute properly, the correct instrument bus/address and query string need to be updated in the script.
|
|
147
|
+
It may sometimes be desirable to combine the sample environment parameters (temperature, field) with readings from a user's own instrumentation into a single *.dat* file which can be plotted in MultiVu. This functionality, accomplished using the MultiPyVu.DataFile module, is demonstrated using PyVISA to communicate with a VISA-compatible instrument in the 'Remote Connection/example_MvDataFile_VISA.py' example. Note that for this routine to execute properly, the correct instrument bus/address and query string need to be updated in the script.
|
|
142
148
|
|
|
143
149
|
For further information on the detailed operation of the components of the module, see the following sections.
|
|
144
150
|
|
|
145
151
|
### Using MultiPyVu.Server() and MultiPyVu.Client()<a class="anchor" id="using"></a>
|
|
146
|
-
To start the server on localhost, open MultiVu, and
|
|
152
|
+
To start the server on localhost, open MultiVu, and type:
|
|
147
153
|
```cmd
|
|
148
|
-
$ python
|
|
154
|
+
$ python -m MultiPyVu
|
|
149
155
|
```
|
|
150
|
-
|
|
156
|
+
One can also use the example script _run_server.py_, go to a command prompt and type:
|
|
151
157
|
```cmd
|
|
152
|
-
$ python
|
|
158
|
+
$ python run_server.py
|
|
153
159
|
```
|
|
160
|
+
|
|
154
161
|
As mentioned above, if the client and the server are running on the same computer as MultiVu, then one can use example_local.py as a guide to set up the whole script in one file.
|
|
155
162
|
|
|
156
|
-
There are a list of
|
|
163
|
+
There are a list of flags to specify settings. These can be found by typing -h, which brings up help information:
|
|
157
164
|
```cmd
|
|
158
165
|
$ python -m MultiPyVu -h
|
|
159
166
|
```
|
|
@@ -170,8 +177,8 @@ Both the Server and the Client are context managers, which means to write a scri
|
|
|
170
177
|
import MultiPyVu as mpv
|
|
171
178
|
|
|
172
179
|
with mpv.Client('127.0.0.1') as client:
|
|
173
|
-
|
|
174
|
-
|
|
180
|
+
# put scripting commands here
|
|
181
|
+
# do post-processing once the client has been closed
|
|
175
182
|
```
|
|
176
183
|
|
|
177
184
|
If the host or port are not default values (localhost and 5000, respectively), then these parameters must be specified when instantiating MultiPyVu.Client().
|
|
@@ -180,31 +187,26 @@ Alternatively, one can start a connection to the server using:
|
|
|
180
187
|
```python
|
|
181
188
|
client = mpv.Client(host='127.0.0.1')
|
|
182
189
|
client.open()
|
|
183
|
-
|
|
190
|
+
# put scripting commands here
|
|
184
191
|
client.close_client()
|
|
185
|
-
|
|
192
|
+
# do post-processing once the client has been closed
|
|
186
193
|
```
|
|
187
194
|
Using the above method could end up with the server hung up because errors need to be correctly accounted for. It is safer to use the Client as a context manager using a with-block.
|
|
188
195
|
|
|
189
|
-
If needed, one can turn off socket timeouts so that the client will wait indefinitely for a response from the server. To do this, set the time in seconds using the 'socket_timeout' keyword when instantiating the client, or set the time to *None* to turn off the timeout. By default, the timeout is set to one second.
|
|
190
|
-
```python
|
|
191
|
-
client = mpv.Client(socket_timeout=None)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
196
|
The client can also close the server directly using:
|
|
195
197
|
```python
|
|
196
198
|
client.close_server()
|
|
197
199
|
```
|
|
198
200
|
instead of client.close_client(). Note that once this is called, the server will need to be reopened. Also, this can not be accessed from within a with-block.
|
|
199
201
|
|
|
200
|
-
If the client and server are being run on the same computer, then
|
|
202
|
+
If the client and server are being run on the same computer, then a single script can control them both.
|
|
201
203
|
```python
|
|
202
204
|
import MultiPyVu as mpv
|
|
203
205
|
|
|
204
206
|
with mpv.Server() as server:
|
|
205
207
|
with mpv.Client() as client:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
# put scripting commands here
|
|
209
|
+
# do post-processing now that the client and server have been closed
|
|
208
210
|
```
|
|
209
211
|
|
|
210
212
|
This is convenient as it does not require running two different scripts.
|
|
@@ -271,9 +273,9 @@ chamber = client.get_chamber()
|
|
|
271
273
|
Note that this command is not used by the OptiCool.
|
|
272
274
|
|
|
273
275
|
**Wait For**<a class="anchor" id="waitfor"></a>
|
|
274
|
-
When a setting on a cryostat is configured, it will take time to reach the new set point. If desired, one can wait for the setting to become stable using the .wait_for(delay, timeout, bitmask) command. A delay will set the time in seconds after the setting is stable, which can be useful to make sure a sample has time to thermalize; timeout is the seconds until the command will give up; bitmask tells the system which settings need to be stable. This can be set using the
|
|
276
|
+
When a setting on a cryostat is configured, it will take time to reach the new set point. If desired, one can wait for the setting to become stable using the .wait_for(delay, timeout, bitmask) command. A delay will set the time in seconds after the setting is stable, which can be useful to make sure a sample has time to thermalize; timeout is the seconds until the command will give up; bitmask tells the system which settings need to be stable. This can be set using the waitfor handle, and multiple parameters are combined using bit-wise or. In the example below, the wait_for command will wait at least 90 seconds for the temperature, field, and chamber to stabilize, and will then immediately go on to the next command.
|
|
275
277
|
```python
|
|
276
|
-
client.wait_for(0, 90, client.temperature.waitfor | client.field.waitfor|
|
|
278
|
+
client.wait_for(0, 90, client.temperature.waitfor | client.field.waitfor |
|
|
277
279
|
client.chamber.waitfor)
|
|
278
280
|
```
|
|
279
281
|
|
|
@@ -316,6 +318,18 @@ client.resistivity.get_resistance(bridge_number)
|
|
|
316
318
|
client.resistivity.get_current(bridge_number)
|
|
317
319
|
```
|
|
318
320
|
|
|
321
|
+
**Horizontal Rotator Option**<a class="anchor" id="rotator"></a>
|
|
322
|
+
|
|
323
|
+
For DynaCool, VersaLab, and PPMS customers with the horizontal rotator option, MultiPyVu can be used to adjust the rotator position. The option must be configured before using MultiPyVu. Note that the rate_deg_per_sec is ignored for the PPMS.
|
|
324
|
+
|
|
325
|
+
Once configured, the following commands are simple to use:
|
|
326
|
+
```python
|
|
327
|
+
position = 374.5
|
|
328
|
+
rate_deg_per_sec = 100.0
|
|
329
|
+
client.set_position(position, rate_deg_per_sec)
|
|
330
|
+
pos, status = client.get_position()
|
|
331
|
+
```
|
|
332
|
+
|
|
319
333
|
### Saving & Opening a MultiVu Data File<a class="anchor" id="save"></a>
|
|
320
334
|
The MultiPyVu.Client class can be used in conjunction with 3rd party tools in order to expand the capabilities of measurements from a Quantum Design cryostat. One can set up a VISA connection to a voltmeter, for example, and then collect information while controlling the cryostat temperature, field, and chamber status. This data can be collected into a MultiVu data file using MultiPyVu.DataFile. One can also use this to read a data file into a Pandas DataFrame.
|
|
321
335
|
|
|
@@ -419,8 +433,8 @@ import MultiPyVu as mpv
|
|
|
419
433
|
|
|
420
434
|
with mpv.Server(flags=['-s', 'DYNACOOL']) as server:
|
|
421
435
|
with mpv.Client() as client:
|
|
422
|
-
|
|
423
|
-
|
|
436
|
+
# put scripting commands here
|
|
437
|
+
# do post-processing now that the client and server have been closed
|
|
424
438
|
```
|
|
425
439
|
|
|
426
440
|
### Troubleshooting<a class="anchor" id="troubleshooting"></a>
|
|
@@ -435,6 +449,16 @@ client = mpv.Client(port=6000)
|
|
|
435
449
|
- A log file named QdMultiVu.log shows the traffic between the server and client which can also be useful during troubleshooting.
|
|
436
450
|
|
|
437
451
|
## Changelog<a class="anchor" id="changelog"></a>
|
|
452
|
+
**3.5.4**
|
|
453
|
+
- June 2025
|
|
454
|
+
- Add the horizontal rotator option for DynaCool, VersaLab, and PPMS.
|
|
455
|
+
|
|
456
|
+
**3.4.0**
|
|
457
|
+
- May 2025
|
|
458
|
+
- Minimum python version now 3.9.
|
|
459
|
+
- Implemented socket handshaking to improve the reliability of the socket
|
|
460
|
+
communications.
|
|
461
|
+
|
|
438
462
|
**3.3.0**
|
|
439
463
|
- April 2025
|
|
440
464
|
- Bugfix which didn't allow this to be run on Mac or Linux platforms.
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* [is_steady()](#is_steady)
|
|
16
16
|
* [get_aux_temperature()](#aux_therm)
|
|
17
17
|
* [resistivity option](#brt)
|
|
18
|
+
* [horizontal rotator option](#rotator)
|
|
18
19
|
* [Saving & Opening a MultiVu Data File](#save)
|
|
19
20
|
* [Querying the Server Status](#server_status)
|
|
20
21
|
* [Testing the Server Using Scaffolding](#scaffolding)
|
|
@@ -32,7 +33,7 @@ The components of MultiPyVu enable access to the set and read the temperature, f
|
|
|
32
33
|
|
|
33
34
|
### Module Requirements<a class="anchor" id="requirements"></a>
|
|
34
35
|
MultiPyVu uses the following modules:
|
|
35
|
-
- python version 3.
|
|
36
|
+
- python version 3.10 or higher
|
|
36
37
|
- pywin32 - version 300 or higher.
|
|
37
38
|
- pandas - data read back from a *.dat* file is a Pandas Dataframe
|
|
38
39
|
- pillow - supports server gui
|
|
@@ -61,6 +62,7 @@ Measuring\example_stabilize.py | Measures the temperature while stabilizing at e
|
|
|
61
62
|
Measuring\example_sweep.py | Measures the temperature while sweeping the temperature.
|
|
62
63
|
Options\example_aux_therm.py | Gets the temperature of the auxilary thermometer from the OptiCool.
|
|
63
64
|
Options\example_resistivity_option.py | Demonstrates how to set up the BRT module bridges for resistivity measurements and how to save the data.
|
|
65
|
+
Options\example_horizontal_rotator.py | Demonstrates how to move the horizontal rotator to a new position.
|
|
64
66
|
UsefulBatchFiles\whats_my_ip_address.cmd | This batch file script prints out the IP address for the computer, providing an easy way to get the server IP address needed for remote operation. Alternatively, the gui can be used to get the IP address by calling ```python -m MultiPyVu```.
|
|
65
67
|
UsefulBatchFiles\show_process_with_port.cmd | Shows that active processes are running and what port is being used. By default, this looks for processes running on port 5000. To query a different port number, append it to the command line arguments. ```$ show_processes_with_port.cmd 4567```
|
|
66
68
|
UsefulBatchFiles\RunServer.cmd | This runs the server GUI.
|
|
@@ -93,22 +95,23 @@ $ python -m MultiPyVu -get_ip
|
|
|
93
95
|
On the client PC, update the 'host' variable of the 'example_Remote-Client.py' script *with the same server PC IPV4 address* and run it. The script will report the present temperature and field value/status a few times to show the connection is functioning.
|
|
94
96
|
|
|
95
97
|
**Next Steps:**
|
|
96
|
-
It may sometimes be desirable to combine the sample environment parameters (temperature, field) with readings from a user's own instrumentation into a single *.dat* file which can be plotted in MultiVu. This functionality, accomplished using the MultiPyVu.DataFile module, is demonstrated using PyVISA to communicate with a VISA-compatible instrument in the 'example_MvDataFile_VISA.py' example. Note that for this routine to execute properly, the correct instrument bus/address and query string need to be updated in the script.
|
|
98
|
+
It may sometimes be desirable to combine the sample environment parameters (temperature, field) with readings from a user's own instrumentation into a single *.dat* file which can be plotted in MultiVu. This functionality, accomplished using the MultiPyVu.DataFile module, is demonstrated using PyVISA to communicate with a VISA-compatible instrument in the 'Remote Connection/example_MvDataFile_VISA.py' example. Note that for this routine to execute properly, the correct instrument bus/address and query string need to be updated in the script.
|
|
97
99
|
|
|
98
100
|
For further information on the detailed operation of the components of the module, see the following sections.
|
|
99
101
|
|
|
100
102
|
### Using MultiPyVu.Server() and MultiPyVu.Client()<a class="anchor" id="using"></a>
|
|
101
|
-
To start the server on localhost, open MultiVu, and
|
|
103
|
+
To start the server on localhost, open MultiVu, and type:
|
|
102
104
|
```cmd
|
|
103
|
-
$ python
|
|
105
|
+
$ python -m MultiPyVu
|
|
104
106
|
```
|
|
105
|
-
|
|
107
|
+
One can also use the example script _run_server.py_, go to a command prompt and type:
|
|
106
108
|
```cmd
|
|
107
|
-
$ python
|
|
109
|
+
$ python run_server.py
|
|
108
110
|
```
|
|
111
|
+
|
|
109
112
|
As mentioned above, if the client and the server are running on the same computer as MultiVu, then one can use example_local.py as a guide to set up the whole script in one file.
|
|
110
113
|
|
|
111
|
-
There are a list of
|
|
114
|
+
There are a list of flags to specify settings. These can be found by typing -h, which brings up help information:
|
|
112
115
|
```cmd
|
|
113
116
|
$ python -m MultiPyVu -h
|
|
114
117
|
```
|
|
@@ -125,8 +128,8 @@ Both the Server and the Client are context managers, which means to write a scri
|
|
|
125
128
|
import MultiPyVu as mpv
|
|
126
129
|
|
|
127
130
|
with mpv.Client('127.0.0.1') as client:
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
# put scripting commands here
|
|
132
|
+
# do post-processing once the client has been closed
|
|
130
133
|
```
|
|
131
134
|
|
|
132
135
|
If the host or port are not default values (localhost and 5000, respectively), then these parameters must be specified when instantiating MultiPyVu.Client().
|
|
@@ -135,31 +138,26 @@ Alternatively, one can start a connection to the server using:
|
|
|
135
138
|
```python
|
|
136
139
|
client = mpv.Client(host='127.0.0.1')
|
|
137
140
|
client.open()
|
|
138
|
-
|
|
141
|
+
# put scripting commands here
|
|
139
142
|
client.close_client()
|
|
140
|
-
|
|
143
|
+
# do post-processing once the client has been closed
|
|
141
144
|
```
|
|
142
145
|
Using the above method could end up with the server hung up because errors need to be correctly accounted for. It is safer to use the Client as a context manager using a with-block.
|
|
143
146
|
|
|
144
|
-
If needed, one can turn off socket timeouts so that the client will wait indefinitely for a response from the server. To do this, set the time in seconds using the 'socket_timeout' keyword when instantiating the client, or set the time to *None* to turn off the timeout. By default, the timeout is set to one second.
|
|
145
|
-
```python
|
|
146
|
-
client = mpv.Client(socket_timeout=None)
|
|
147
|
-
```
|
|
148
|
-
|
|
149
147
|
The client can also close the server directly using:
|
|
150
148
|
```python
|
|
151
149
|
client.close_server()
|
|
152
150
|
```
|
|
153
151
|
instead of client.close_client(). Note that once this is called, the server will need to be reopened. Also, this can not be accessed from within a with-block.
|
|
154
152
|
|
|
155
|
-
If the client and server are being run on the same computer, then
|
|
153
|
+
If the client and server are being run on the same computer, then a single script can control them both.
|
|
156
154
|
```python
|
|
157
155
|
import MultiPyVu as mpv
|
|
158
156
|
|
|
159
157
|
with mpv.Server() as server:
|
|
160
158
|
with mpv.Client() as client:
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
# put scripting commands here
|
|
160
|
+
# do post-processing now that the client and server have been closed
|
|
163
161
|
```
|
|
164
162
|
|
|
165
163
|
This is convenient as it does not require running two different scripts.
|
|
@@ -226,9 +224,9 @@ chamber = client.get_chamber()
|
|
|
226
224
|
Note that this command is not used by the OptiCool.
|
|
227
225
|
|
|
228
226
|
**Wait For**<a class="anchor" id="waitfor"></a>
|
|
229
|
-
When a setting on a cryostat is configured, it will take time to reach the new set point. If desired, one can wait for the setting to become stable using the .wait_for(delay, timeout, bitmask) command. A delay will set the time in seconds after the setting is stable, which can be useful to make sure a sample has time to thermalize; timeout is the seconds until the command will give up; bitmask tells the system which settings need to be stable. This can be set using the
|
|
227
|
+
When a setting on a cryostat is configured, it will take time to reach the new set point. If desired, one can wait for the setting to become stable using the .wait_for(delay, timeout, bitmask) command. A delay will set the time in seconds after the setting is stable, which can be useful to make sure a sample has time to thermalize; timeout is the seconds until the command will give up; bitmask tells the system which settings need to be stable. This can be set using the waitfor handle, and multiple parameters are combined using bit-wise or. In the example below, the wait_for command will wait at least 90 seconds for the temperature, field, and chamber to stabilize, and will then immediately go on to the next command.
|
|
230
228
|
```python
|
|
231
|
-
client.wait_for(0, 90, client.temperature.waitfor | client.field.waitfor|
|
|
229
|
+
client.wait_for(0, 90, client.temperature.waitfor | client.field.waitfor |
|
|
232
230
|
client.chamber.waitfor)
|
|
233
231
|
```
|
|
234
232
|
|
|
@@ -271,6 +269,18 @@ client.resistivity.get_resistance(bridge_number)
|
|
|
271
269
|
client.resistivity.get_current(bridge_number)
|
|
272
270
|
```
|
|
273
271
|
|
|
272
|
+
**Horizontal Rotator Option**<a class="anchor" id="rotator"></a>
|
|
273
|
+
|
|
274
|
+
For DynaCool, VersaLab, and PPMS customers with the horizontal rotator option, MultiPyVu can be used to adjust the rotator position. The option must be configured before using MultiPyVu. Note that the rate_deg_per_sec is ignored for the PPMS.
|
|
275
|
+
|
|
276
|
+
Once configured, the following commands are simple to use:
|
|
277
|
+
```python
|
|
278
|
+
position = 374.5
|
|
279
|
+
rate_deg_per_sec = 100.0
|
|
280
|
+
client.set_position(position, rate_deg_per_sec)
|
|
281
|
+
pos, status = client.get_position()
|
|
282
|
+
```
|
|
283
|
+
|
|
274
284
|
### Saving & Opening a MultiVu Data File<a class="anchor" id="save"></a>
|
|
275
285
|
The MultiPyVu.Client class can be used in conjunction with 3rd party tools in order to expand the capabilities of measurements from a Quantum Design cryostat. One can set up a VISA connection to a voltmeter, for example, and then collect information while controlling the cryostat temperature, field, and chamber status. This data can be collected into a MultiVu data file using MultiPyVu.DataFile. One can also use this to read a data file into a Pandas DataFrame.
|
|
276
286
|
|
|
@@ -374,8 +384,8 @@ import MultiPyVu as mpv
|
|
|
374
384
|
|
|
375
385
|
with mpv.Server(flags=['-s', 'DYNACOOL']) as server:
|
|
376
386
|
with mpv.Client() as client:
|
|
377
|
-
|
|
378
|
-
|
|
387
|
+
# put scripting commands here
|
|
388
|
+
# do post-processing now that the client and server have been closed
|
|
379
389
|
```
|
|
380
390
|
|
|
381
391
|
### Troubleshooting<a class="anchor" id="troubleshooting"></a>
|
|
@@ -390,6 +400,16 @@ client = mpv.Client(port=6000)
|
|
|
390
400
|
- A log file named QdMultiVu.log shows the traffic between the server and client which can also be useful during troubleshooting.
|
|
391
401
|
|
|
392
402
|
## Changelog<a class="anchor" id="changelog"></a>
|
|
403
|
+
**3.5.4**
|
|
404
|
+
- June 2025
|
|
405
|
+
- Add the horizontal rotator option for DynaCool, VersaLab, and PPMS.
|
|
406
|
+
|
|
407
|
+
**3.4.0**
|
|
408
|
+
- May 2025
|
|
409
|
+
- Minimum python version now 3.9.
|
|
410
|
+
- Implemented socket handshaking to improve the reliability of the socket
|
|
411
|
+
communications.
|
|
412
|
+
|
|
393
413
|
**3.3.0**
|
|
394
414
|
- April 2025
|
|
395
415
|
- Bugfix which didn't allow this to be run on Mac or Linux platforms.
|
|
@@ -5,12 +5,12 @@ https://packaging.python.org/guides/distributing-packages-using-setuptools/
|
|
|
5
5
|
https://github.com/pypa/sampleproject
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
# Always prefer setuptools over distutils
|
|
9
|
-
from setuptools import setup, find_packages
|
|
10
|
-
import pathlib
|
|
11
8
|
import codecs
|
|
12
9
|
import os.path
|
|
10
|
+
import pathlib
|
|
13
11
|
|
|
12
|
+
# Always prefer setuptools over distutils
|
|
13
|
+
from setuptools import find_packages, setup
|
|
14
14
|
|
|
15
15
|
here = pathlib.Path(__file__).parent.resolve()
|
|
16
16
|
|
|
@@ -51,7 +51,7 @@ setup(
|
|
|
51
51
|
"logging_config.yaml"
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
|
-
python_requires=">=3.
|
|
54
|
+
python_requires=">=3.9",
|
|
55
55
|
install_requires=[
|
|
56
56
|
"pandas",
|
|
57
57
|
"pywin32>=300; platform_system=='Windows'",
|
|
@@ -20,6 +20,23 @@ class Brt():
|
|
|
20
20
|
self.client = client
|
|
21
21
|
self.stabilize_time = 5
|
|
22
22
|
|
|
23
|
+
def _make_bridge_number_error(self, bridge_number: int) -> str:
|
|
24
|
+
"""
|
|
25
|
+
This creates a simple error message saying that the
|
|
26
|
+
selected bridge number is out of range
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
bridge_number
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
error message
|
|
33
|
+
"""
|
|
34
|
+
msg = 'bridge_number must be 1, 2, or 3 '
|
|
35
|
+
# Note that python version 3.7 does not allow formatting
|
|
36
|
+
# strings using {bridge_number = }
|
|
37
|
+
msg += f'(bridge_number = {bridge_number})'
|
|
38
|
+
return msg
|
|
39
|
+
|
|
23
40
|
def bridge_setup(self,
|
|
24
41
|
bridge_number: int,
|
|
25
42
|
channel_on: bool,
|
|
@@ -74,8 +91,7 @@ class Brt():
|
|
|
74
91
|
|
|
75
92
|
sdo = enable_sdo.get(bridge_number, None)
|
|
76
93
|
if sdo is None:
|
|
77
|
-
|
|
78
|
-
raise ValueError(msg)
|
|
94
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
79
95
|
self.client._set_sdo(sdo, int(enable))
|
|
80
96
|
# Pause to let the SDO go through. The pause time is
|
|
81
97
|
# somewhat arbitrary.
|
|
@@ -113,8 +129,7 @@ class Brt():
|
|
|
113
129
|
|
|
114
130
|
sdo = const_current_sdo.get(bridge_number, None)
|
|
115
131
|
if sdo is None:
|
|
116
|
-
|
|
117
|
-
raise ValueError(msg)
|
|
132
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
118
133
|
self.client._set_sdo(sdo, int(const_current))
|
|
119
134
|
for timeout in range(0, 4):
|
|
120
135
|
time.sleep(0.6)
|
|
@@ -149,8 +164,7 @@ class Brt():
|
|
|
149
164
|
|
|
150
165
|
sdo = resistance.get(bridge_number, None)
|
|
151
166
|
if sdo is None:
|
|
152
|
-
|
|
153
|
-
raise ValueError(msg)
|
|
167
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
154
168
|
res, status = self.client._get_sdo(sdo)
|
|
155
169
|
return float(res), status
|
|
156
170
|
|
|
@@ -181,8 +195,7 @@ class Brt():
|
|
|
181
195
|
|
|
182
196
|
sdo = current.get(bridge_number, None)
|
|
183
197
|
if sdo is None:
|
|
184
|
-
|
|
185
|
-
raise ValueError(msg)
|
|
198
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
186
199
|
res, status = self.client._get_sdo(sdo)
|
|
187
200
|
return float(res), status
|
|
188
201
|
|
|
@@ -222,8 +235,7 @@ class Brt():
|
|
|
222
235
|
|
|
223
236
|
sdo = current_sdo.get(bridge_number, None)
|
|
224
237
|
if sdo is None:
|
|
225
|
-
|
|
226
|
-
raise ValueError(msg)
|
|
238
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
227
239
|
self.client._set_sdo(sdo, current_uA)
|
|
228
240
|
# Put in constant current mode
|
|
229
241
|
self._constant_current(bridge_number, True)
|
|
@@ -265,8 +277,7 @@ class Brt():
|
|
|
265
277
|
|
|
266
278
|
sdo = current_sdo.get(bridge_number, None)
|
|
267
279
|
if sdo is None:
|
|
268
|
-
|
|
269
|
-
raise ValueError(msg)
|
|
280
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
270
281
|
self.client._set_sdo(sdo, current_uA)
|
|
271
282
|
|
|
272
283
|
def power_limit(self, bridge_number: int, power_uW: float) -> None:
|
|
@@ -306,8 +317,7 @@ class Brt():
|
|
|
306
317
|
|
|
307
318
|
sdo = current_sdo.get(bridge_number, None)
|
|
308
319
|
if sdo is None:
|
|
309
|
-
|
|
310
|
-
raise ValueError(msg)
|
|
320
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
311
321
|
self.client._set_sdo(sdo, power_uW)
|
|
312
322
|
|
|
313
323
|
def voltage_limit(self, bridge_number: int, voltage_mV: float) -> None:
|
|
@@ -347,8 +357,7 @@ class Brt():
|
|
|
347
357
|
|
|
348
358
|
sdo = current_sdo.get(bridge_number, None)
|
|
349
359
|
if sdo is None:
|
|
350
|
-
|
|
351
|
-
raise ValueError(msg)
|
|
360
|
+
raise ValueError(self._make_bridge_number_error(bridge_number))
|
|
352
361
|
self.client._set_sdo(sdo, voltage_mV)
|
|
353
362
|
|
|
354
363
|
# This works, but is not used for the resistivity option, so currently commented out
|
|
@@ -131,7 +131,7 @@ class CommandChamberBase(ICommand):
|
|
|
131
131
|
raise NotImplementedError
|
|
132
132
|
|
|
133
133
|
@abstractmethod
|
|
134
|
-
def
|
|
134
|
+
def _set_state_imp(self, mode: int) -> Union[str, int]:
|
|
135
135
|
raise NotImplementedError
|
|
136
136
|
|
|
137
137
|
def set_state_server(self, arg_string) -> Union[str, int]:
|
|
@@ -149,7 +149,7 @@ class CommandChamberBase(ICommand):
|
|
|
149
149
|
err_msg += f'\n\t{m.value}: {m.name}'
|
|
150
150
|
raise MultiPyVuError(err_msg)
|
|
151
151
|
self.state = set_mode
|
|
152
|
-
return self.
|
|
152
|
+
return self._set_state_imp(set_mode)
|
|
153
153
|
|
|
154
154
|
def state_code_dict(self):
|
|
155
155
|
return STATE_DICT
|
|
@@ -196,13 +196,17 @@ class CommandChamberImp(ICommandImp, CommandChamberBase):
|
|
|
196
196
|
err_msg = 'get_chamber() is not available for the OptiCool'
|
|
197
197
|
raise MultiPyVuError(err_msg)
|
|
198
198
|
error = self._mvu.GetChamber(state_variant)
|
|
199
|
-
|
|
199
|
+
# On 6/10/25, I found that the PPMS was returning something greater
|
|
200
|
+
# than 1 with the GetTemperature() command. After talking with Mark,
|
|
201
|
+
# I have decided to only check for a value greater than 1 for all
|
|
202
|
+
# systems.
|
|
203
|
+
if can_error > 1:
|
|
200
204
|
raise MultiPyVuError('Error when calling GetChamber()')
|
|
201
205
|
self.state = int(state_variant.value)
|
|
202
206
|
|
|
203
207
|
return 0.0, self.state
|
|
204
208
|
|
|
205
|
-
def
|
|
209
|
+
def _set_state_imp(self, mode: int) -> Union[str, int]:
|
|
206
210
|
can_error = self._mvu.SetChamber(mode)
|
|
207
211
|
self.set_point = mode
|
|
208
212
|
|
|
@@ -397,7 +401,7 @@ class CommandChamberSim(CommandChamberBase,
|
|
|
397
401
|
raise MultiPyVuError(err_msg)
|
|
398
402
|
return 0.0, self.state
|
|
399
403
|
|
|
400
|
-
def
|
|
404
|
+
def _set_state_imp(self, mode: int) -> Union[str, int]:
|
|
401
405
|
self.change_thread = self.get_sim_instance()
|
|
402
406
|
state_string = STATE_DICT[self.state]
|
|
403
407
|
self.change_thread.set_params('',
|
|
@@ -170,7 +170,7 @@ class CommandFieldBase(ICommand):
|
|
|
170
170
|
rate_per_sec = float(rate_per_sec)
|
|
171
171
|
rate_per_sec = abs(rate_per_sec)
|
|
172
172
|
except ValueError:
|
|
173
|
-
err_msg = '
|
|
173
|
+
err_msg = 'rate_per_sec must be a float '
|
|
174
174
|
err_msg += f'(rate_per_sec = \'{rate_per_sec}\')'
|
|
175
175
|
raise ValueError(err_msg)
|
|
176
176
|
|
|
@@ -190,12 +190,12 @@ class CommandFieldBase(ICommand):
|
|
|
190
190
|
raise NotImplementedError
|
|
191
191
|
|
|
192
192
|
@abstractmethod
|
|
193
|
-
def
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
def _set_state_imp(self,
|
|
194
|
+
field: float,
|
|
195
|
+
set_rate_per_sec: float,
|
|
196
|
+
set_approach: int,
|
|
197
|
+
set_driven: int,
|
|
198
|
+
) -> Union[str, int]:
|
|
199
199
|
raise NotImplementedError
|
|
200
200
|
|
|
201
201
|
def set_state_server(self, arg_string) -> Union[str, int]:
|
|
@@ -237,11 +237,11 @@ class CommandFieldBase(ICommand):
|
|
|
237
237
|
err_msg += 'no_overshoot approach mode.'
|
|
238
238
|
raise MultiPyVuError(err_msg)
|
|
239
239
|
|
|
240
|
-
error = self.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
240
|
+
error = self._set_state_imp(field,
|
|
241
|
+
set_rate_per_sec,
|
|
242
|
+
set_approach,
|
|
243
|
+
set_driven
|
|
244
|
+
)
|
|
245
245
|
return error
|
|
246
246
|
|
|
247
247
|
def state_code_dict(self):
|
|
@@ -283,19 +283,23 @@ class CommandFieldImp(ICommandImp, CommandFieldBase):
|
|
|
283
283
|
The value and state number
|
|
284
284
|
"""
|
|
285
285
|
can_error = self._mvu.GetField(value_variant, state_variant)
|
|
286
|
-
|
|
286
|
+
# On 6/10/25, I found that the PPMS was returning something greater
|
|
287
|
+
# than 1 with the GetTemperature() command. After talking with Mark,
|
|
288
|
+
# I have decided to only check for a value greater than 1 for all
|
|
289
|
+
# systems.
|
|
290
|
+
if can_error > 1:
|
|
287
291
|
raise MultiPyVuError('Error when calling GetField()')
|
|
288
292
|
self.current_val = value_variant.value
|
|
289
293
|
self.state = int(state_variant.value)
|
|
290
294
|
|
|
291
295
|
return self.current_val, self.state
|
|
292
296
|
|
|
293
|
-
def
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
def _set_state_imp(self,
|
|
298
|
+
field: float,
|
|
299
|
+
set_rate_per_sec: float,
|
|
300
|
+
set_approach: int,
|
|
301
|
+
set_driven: int
|
|
302
|
+
) -> Union[str, int]:
|
|
299
303
|
can_error = self._mvu.setField(field,
|
|
300
304
|
set_rate_per_sec,
|
|
301
305
|
set_approach,
|
|
@@ -493,12 +497,12 @@ class CommandFieldSim(CommandFieldBase,
|
|
|
493
497
|
def get_state_server(self, value_variant, state_variant, params=''):
|
|
494
498
|
return self.current_val, self.state
|
|
495
499
|
|
|
496
|
-
def
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
500
|
+
def _set_state_imp(self,
|
|
501
|
+
field: float,
|
|
502
|
+
set_rate_per_sec: float,
|
|
503
|
+
set_approach: int,
|
|
504
|
+
set_driven: int
|
|
505
|
+
) -> Union[str, int]:
|
|
502
506
|
self.change_thread = self.get_sim_instance()
|
|
503
507
|
state_string = STATE_DICT[self.state]
|
|
504
508
|
self.change_thread.set_params(self.current_val,
|
|
@@ -99,7 +99,7 @@ class CommandMultiVuBase():
|
|
|
99
99
|
try:
|
|
100
100
|
result, status_number = self._get_state_imp(mv_command, params)
|
|
101
101
|
except AttributeError as e:
|
|
102
|
-
raise MultiPyVuError(f'
|
|
102
|
+
raise MultiPyVuError(f'Command not found: {e}')
|
|
103
103
|
try:
|
|
104
104
|
# Get the translated state code
|
|
105
105
|
code_in_words = mv_command.convert_state_dictionary(status_number)
|