Repository navigation
New AMI430 driver #700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Jens Hedegaard Nielsen (jenshnielsen)
merged 30 commits into
microsoft:master
from
sohailc:master
Sep 4, 2017
Merged
New AMI430 driver #700
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
fbcdd2d
Added environment.yml file
3757cdc
Added requirements to environment.yml
d4c8ed1
Added requirements to environment.yml
1397535
Works under linux now
sohailc 2888633
1) added american instrument driver from the main qcodes repo
0927761
the mock IP instrument now remembers the set point
478d8ec
added test for spherical coordinates. Making test for cylindrical coo…
f726a31
Added a module for handy converting between spherical, cartesian and …
ef19eff
additional work
c0095af
resolved merge conflict
c591196
1) The driver is adhering to the qcodes specs now
eb2ab7d
1) The driver is adhering to the qcodes specs now
9f321c3
1) Made tests to ensure that the set points correctly reach the instr…
cc6073d
Added a test to verify that ramp downs are performed first
4f0fd7b
Merge branch 'master' of git://github.com/qcodes/qcodes
5838d0d
Added the ability to define more complex regions for safe set points.…
1ef9f8e
Re-wrote the instrument mocker so we have a cleaner design.
f245439
Merge branch 'master' of git://github.com/qcodes/qcodes
dd0aee0
changing how mocking works
ccabe4e
Mocking no longer requires sockets and threading. Major rewrite of th…
d69583c
Mocking no longer requires sockets and threading. Major rewrite of th…
590925e
Merge branch 'master' of git://github.com/qcodes/qcodes
14f683e
further clean up of the mocking mechanism.
013c38b
Solved a bug which raised a value error when asking for spherical coo…
b2f7203
Merge branch 'master' of git://github.com/qcodes/qcodes
f52052d
1) Added tests for the field vector
6423afa
Added a notebook showing the results of the testing of the new AMI430…
0809e6d
* Processed Jens comments, except changing the doc string format and …
1c93c8e
Merge branch 'master' of git://github.com/qcodes/qcodes
77a55e8
Processed the rest of Jens comments
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
454 changes: 454 additions & 0 deletions
454
docs/examples/driver_examples/Qcodes example with AMI430.ipynb
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| name: qcodes | ||
| channels: | ||
| - defaults | ||
| - conda-forge | ||
| dependencies: | ||
| - h5py=2.6.0 | ||
| - matplotlib=2.0.2 | ||
| - pyqtgraph | ||
| - python=3.6 | ||
| - jsonschema | ||
| - pyvisa | ||
| - QtPy=1.2.0 | ||
| - sip | ||
| - pyqt=5.6.0 | ||
| - numpy | ||
| - jupyter=1.0.0 | ||
| - typing | ||
| - hypothesis | ||
| - pytest | ||
| - pytest-runner |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| import re | ||
| import time | ||
| from datetime import datetime | ||
|
|
||
|
|
||
| class MockAMI430: | ||
| states = { | ||
| "RAMPING to target field/current": "1", | ||
| "HOLDING at the target field/current": "2", | ||
| "PAUSED": "3", | ||
| "Ramping in MANUAL UP mode": "4", | ||
| "Ramping in MANUAL DOWN mode": "5", | ||
| "ZEROING CURRENT (in progress)": "6", | ||
| "Quench detected": "7", | ||
| "At ZERO current": "8", | ||
| "Heating persistent switch": "9", | ||
| "Cooling persistent switch": "10" | ||
| } | ||
|
|
||
| field_units = { | ||
| "tesla": "1", | ||
| "kilogauss": "0" | ||
| } | ||
|
|
||
| ramp_rate_units = { | ||
| "A/s": "0", | ||
| "A/min": "1" | ||
| } | ||
|
|
||
| quench_state = {False: "0", True: "1"} | ||
|
|
||
| def __init__(self, name): | ||
|
|
||
| self.name = name | ||
| self.log_messages = [] | ||
|
|
||
| self._field_mag = 0 | ||
| self._field_target = 0 | ||
| self._state = MockAMI430.states["HOLDING at the target field/current"] | ||
|
|
||
| self.handlers = { | ||
| "RAMP:RATE:UNITS": { | ||
| "get": MockAMI430.ramp_rate_units["A/s"], | ||
| "set": None | ||
| }, | ||
| "FIELD:UNITS": { | ||
| "get": MockAMI430.field_units["tesla"], | ||
| "set": None | ||
| }, | ||
| "*IDN": { | ||
| "get": "v0.1 Mock", | ||
| "set": None | ||
| }, | ||
| "STATE": { | ||
| "get": self._getter("_state"), | ||
| "set": self._setter("_state") | ||
| }, | ||
| "FIELD:MAG": { | ||
| "get": self._getter("_field_mag"), | ||
| "set": None | ||
| }, | ||
| "QU": { | ||
| "get": MockAMI430.quench_state[False], # We are never in a quenching state so always return the | ||
| # same value | ||
| "set": None | ||
| }, | ||
| "PERS": { | ||
| "get": "0", | ||
| "set": None | ||
| }, | ||
| "PAUSE": { | ||
| "get": self._is_paused, | ||
| "set": self._do_pause | ||
| }, | ||
| "CONF:FIELD:TARG": { | ||
| "get": None, # To get the field target, send a message "FIELD:TARG?" | ||
| "set": self._setter("_field_target") | ||
| }, | ||
| "FIELD:TARG": { | ||
| "get": self._getter("_field_target"), | ||
| "set": None | ||
| }, | ||
| "PS": { | ||
| "get": "0", # The heater is off | ||
| "set": None | ||
| }, | ||
| "RAMP": { | ||
| "set": self._do_ramp, | ||
| "get": None | ||
| }, | ||
| "RAMP:RATE:CURRENT": { | ||
| "get": "0.1000,50.0000", | ||
| "set": None | ||
| }, | ||
| "COIL": { | ||
| "get": "1", | ||
| "set": None | ||
| } | ||
| } | ||
|
|
||
| @staticmethod | ||
| def message_parser(gs, msg_str, key): | ||
| """ | ||
| * If gs = "get": | ||
| Let suppose key = "RAMP:RATE:UNITS", then if we get msg_str = "RAMP:RATE:UNITS?" then match will be True and | ||
| args = None. If msg_str = "RAMP:RATE:UNITS:10?" then match = True and args = "10". On the other hand if | ||
| key = "RAMP" then both "RAMP:RATE:UNITS?" and "RAMP:RATE:UNITS:10?" will cause match to be False | ||
|
|
||
| * If gs = "set" | ||
| If key = "STATE" and msg_str = "STATE 2,1" then match = True and args = "2,1". If key="STATE" and | ||
| msg_str = STATE:ELSE 2,1 then match is False. | ||
|
|
||
| Consult [1] for a complete description of the AMI430 protocol. | ||
|
|
||
| [1] http://www.americanmagnetics.com/support/manuals/mn-4Q06125PS-430.pdf | ||
|
|
||
| Parameters: | ||
| gs (string): "get", or "set" | ||
| msg_str (string): the message string the mock instrument gets. | ||
| key (string): one of the keys in self.handlers | ||
|
|
||
| Returns: | ||
| match (bool): if the key and the msg_str match, then match = True | ||
| args (string): if any arguments are present in the message string these will be passed along. This is | ||
| always None when match = False | ||
|
|
||
| """ | ||
| if msg_str == key: # If the message string matches a key exactly we have a match with no arguments | ||
| return True, None | ||
|
|
||
| # We use regular expressions to find out if the message string and the key match. We need to replace reserved | ||
| # regular expression characters in the key. For instance replace "*IDN" with "\*IDN". | ||
| reserved_re_characters = "\^${}[]().*+?|<>-&" | ||
| for c in reserved_re_characters: | ||
| key = key.replace(c, "\{}".format(c)) | ||
|
|
||
| s = {"get": "(:[^:]*)?\?$", "set": "([^:]+)"}[gs] # Get and set messages use different regular expression | ||
| # patterns to determine a match | ||
| search_string = "^" + key + s | ||
| r = re.search(search_string, msg_str) | ||
| match = r is not None | ||
|
|
||
| args = None | ||
| if match: | ||
| args = r.groups()[0] | ||
| if args is not None: | ||
| args = args.strip(":") | ||
|
|
||
| return match, args | ||
|
|
||
| def _getter(self, attribute): | ||
| return lambda _: getattr(self, attribute) | ||
|
|
||
| def _setter(self, attribute): | ||
| return lambda value: setattr(self, attribute, value) | ||
|
|
||
| def _log(self, msg): | ||
|
|
||
| now = datetime.now() | ||
| log_line = "[{}] {}: {}".format(now.strftime("%d:%m:%Y-%H:%M:%S.%f"), self.name, msg) | ||
| self.log_messages.append(log_line) | ||
|
|
||
| def _handle_messages(self, msg): | ||
| """ | ||
| Parameters: | ||
| msg (string): a message received through the socket communication layer | ||
|
|
||
| Returns: | ||
| rval (string or None): If the type of message requests a value (a get message) then this value is returned | ||
| by this function. A set message will return a None value. | ||
| """ | ||
|
|
||
| gs = {True: "get", False: "set"}[msg.endswith("?")] # A "get" message ends with a "?" and will invoke the get | ||
| # part of the handler defined in self.handlers. | ||
|
|
||
| rval = None | ||
| handler = None | ||
|
|
||
| for key in self.handlers: # Find which handler is suitable to handle the message | ||
| match, args = MockAMI430.message_parser(gs, msg, key) | ||
| if not match: | ||
| continue | ||
|
|
||
| handler = self.handlers[key][gs] | ||
| if callable(handler): | ||
| rval = handler(args) | ||
| else: | ||
| rval = handler | ||
|
|
||
| break | ||
|
|
||
| if handler is None: | ||
| self._log("Command {} unknown".format(msg)) | ||
|
|
||
| return rval | ||
|
|
||
| def _do_pause(self, _): | ||
| self._state = MockAMI430.states["PAUSED"] | ||
|
|
||
| def _is_paused(self): | ||
| return self._state == MockAMI430.states["PAUSED"] | ||
|
|
||
| def _do_ramp(self, _): | ||
| self._log("Ramping to {}".format(self._field_target)) | ||
| self._state = MockAMI430.states["RAMPING to target field/current"] | ||
| time.sleep(0.1) # Lets pretend to be ramping for a bit | ||
| self._field_mag = self._field_target | ||
| self._state = MockAMI430.states["HOLDING at the target field/current"] | ||
|
|
||
| def get_log_messages(self): | ||
| return self.log_messages | ||
|
|
||
| def ask(self, msg): | ||
| return self._handle_messages(msg) | ||
|
|
||
| def write(self, msg): | ||
| self._handle_messages(msg) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have not been enforcing it but we have been trying to move towards pep8 and <79 char per line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not exactly understand why this is giving me pep8 issues. My pycharm editor also complains about this line, but I do not understand why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The line is longer than 79 characters now? no. The wrapping was to make it shorter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is complaining that the dictionary can be re-written as a dict literal. So literal:
some_dict = {"A": 1, "B": 2}
vs.
dict(A=1, B=1).
But as far as I can see the dictionary is literal, so I do not understand the complaint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link has some explaination: link, but I do not understand how this applies here. Anyway, I do not think this issue is very important and I propose to ignore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that seems wrong to me. I think its safe to ignore that