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.
Files changed (62) hide show
  1. {multipyvu-3.3.0/src/MultiPyVu.egg-info → multipyvu-3.5.4}/PKG-INFO +50 -26
  2. {multipyvu-3.3.0 → multipyvu-3.5.4}/README.md +43 -23
  3. {multipyvu-3.3.0 → multipyvu-3.5.4}/setup.py +4 -4
  4. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/BRT.py +25 -16
  5. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandChamber.py +9 -5
  6. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandField.py +29 -25
  7. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandMultiVu.py +1 -1
  8. multipyvu-3.5.4/src/MultiPyVu/CommandRotator.py +567 -0
  9. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandSdo.py +14 -10
  10. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandTemperature.py +5 -3
  11. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/Command_factory.py +30 -2
  12. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuClient.py +68 -8
  13. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuClient_base.py +190 -88
  14. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/MultiVuDataFile.py +5 -6
  15. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuServer.py +14 -14
  16. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/SocketMessage.py +175 -158
  17. multipyvu-3.5.4/src/MultiPyVu/SocketMessageClient.py +503 -0
  18. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/SocketMessageServer.py +387 -207
  19. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/__version.py +1 -1
  20. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/instrument.py +3 -3
  21. multipyvu-3.5.4/src/MultiPyVu/logging_config.py +76 -0
  22. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/project_vars.py +7 -0
  23. {multipyvu-3.3.0 → multipyvu-3.5.4/src/MultiPyVu.egg-info}/PKG-INFO +50 -26
  24. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/SOURCES.txt +1 -0
  25. multipyvu-3.3.0/src/MultiPyVu/SocketMessageClient.py +0 -290
  26. multipyvu-3.3.0/src/MultiPyVu/logging_config.py +0 -72
  27. {multipyvu-3.3.0 → multipyvu-3.5.4}/LICENSE +0 -0
  28. {multipyvu-3.3.0 → multipyvu-3.5.4}/pyproject.toml +0 -0
  29. {multipyvu-3.3.0 → multipyvu-3.5.4}/setup.cfg +0 -0
  30. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/CommandWaitFor.py +0 -0
  31. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/ICommand.py +0 -0
  32. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/IEventManager.py +0 -0
  33. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/IServer.py +0 -0
  34. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/__init__.py +0 -0
  35. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/__version.py +0 -0
  36. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/MultiVuDataFile/is_pathname_valid.py +0 -0
  37. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/ParseInputs.py +0 -0
  38. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/__init__.py +0 -0
  39. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/__main__.py +0 -0
  40. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/check_windows_esc.py +0 -0
  41. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/exceptions.py +0 -0
  42. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/font/Play-Bold.ttf +0 -0
  43. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/font/Play-Regular.ttf +0 -0
  44. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/font/__init__.py +0 -0
  45. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/generate_sdo_objects.py +0 -0
  46. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/Controller.py +0 -0
  47. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/IController.py +0 -0
  48. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/IView.py +0 -0
  49. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/ViewFactory.py +0 -0
  50. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/ViewTk.py +0 -0
  51. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/gui/__init__.py +0 -0
  52. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/images/Q.png +0 -0
  53. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/images/QD_logo.jpg +0 -0
  54. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/images/__init__.py +0 -0
  55. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/logging_config.yaml +0 -0
  56. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/scripts/__init__.py +0 -0
  57. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/scripts/helper_scripts.py +0 -0
  58. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/scripts/whats_my_ip_address.cmd +0 -0
  59. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu/sdo_object.py +0 -0
  60. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/dependency_links.txt +0 -0
  61. {multipyvu-3.3.0 → multipyvu-3.5.4}/src/MultiPyVu.egg-info/requires.txt +0 -0
  62. {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
1
+ Metadata-Version: 2.4
2
2
  Name: MultiPyVu
3
- Version: 3.3.0
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.8
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
  ![qd_logo](https://qdusa.com/images/QD_logo.png)
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.8 or higher
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 then, using the example script _run_server.py_, go to a command prompt and type:
152
+ To start the server on localhost, open MultiVu, and type:
147
153
  ```cmd
148
- $ python run_server.py
154
+ $ python -m MultiPyVu
149
155
  ```
150
- The server can also be started using a gui:
156
+ One can also use the example script _run_server.py_, go to a command prompt and type:
151
157
  ```cmd
152
- $ python -m MultiPyVu
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 useful flags to specify settings. These can be found by typing -h, which brings up help information:
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
- <put scripting commands here>
174
- <do post-processing once the client has been closed>
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
- <put scripting commands here>
190
+ # put scripting commands here
184
191
  client.close_client()
185
- <do post-processing once the client has been closed>
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 one could also write one script to control them both.
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
- <put scripting commands here>
207
- <do post-processing now that the client and server have been closed>
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 client.subsystem enum, 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.
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
- <put scripting commands here>
423
- <do post-processing now that the client and server have been closed>
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.8 or higher
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 then, using the example script _run_server.py_, go to a command prompt and type:
103
+ To start the server on localhost, open MultiVu, and type:
102
104
  ```cmd
103
- $ python run_server.py
105
+ $ python -m MultiPyVu
104
106
  ```
105
- The server can also be started using a gui:
107
+ One can also use the example script _run_server.py_, go to a command prompt and type:
106
108
  ```cmd
107
- $ python -m MultiPyVu
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 useful flags to specify settings. These can be found by typing -h, which brings up help information:
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
- <put scripting commands here>
129
- <do post-processing once the client has been closed>
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
- <put scripting commands here>
141
+ # put scripting commands here
139
142
  client.close_client()
140
- <do post-processing once the client has been closed>
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 one could also write one script to control them both.
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
- <put scripting commands here>
162
- <do post-processing now that the client and server have been closed>
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 client.subsystem enum, 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.
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
- <put scripting commands here>
378
- <do post-processing now that the client and server have been closed>
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.8",
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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
- msg = f'bridge_number must be 1, 2, or 3 ({bridge_number = })'
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 set_state_imp(self, mode: int) -> Union[str, int]:
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.set_state_imp(set_mode)
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
- if error > 0:
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 set_state_imp(self, mode: int) -> Union[str, int]:
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 set_state_imp(self, mode: int) -> Union[str, int]:
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 = 'rate_per_minute must be a float '
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 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]:
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.set_state_imp(field,
241
- set_rate_per_sec,
242
- set_approach,
243
- set_driven
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
- if can_error > 0:
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 set_state_imp(self,
294
- field: float,
295
- set_rate_per_sec: float,
296
- set_approach: int,
297
- set_driven: int
298
- ) -> Union[str, int]:
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 set_state_imp(self,
497
- field: float,
498
- set_rate_per_sec: float,
499
- set_approach: int,
500
- set_driven: int
501
- ) -> Union[str, int]:
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'Potential threading error: {e}')
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)