-
Notifications
You must be signed in to change notification settings - Fork 319
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
Add read until terminator support for ip instrument #971
Conversation
qcodes/instrument/ip.py
Outdated
if partresult == b'': | ||
log.warning("Got empty response from Socket recv() " | ||
"Connection broken.") | ||
elif (partresult[-rtl:] == self.read_terminator or |
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 would use "endswith" here:
https://www.tutorialspoint.com/python/string_endswith.htm
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 is slightly more clean
Should we not make the read_terminator default to terminator? I think we should always call recv until the read terminator has been seen. |
I agree. I wanted to test with other instruments before doing that but just doing a single recv without any checks is fundamentally wrong |
Codecov Report
@@ Coverage Diff @@
## master #971 +/- ##
==========================================
+ Coverage 65.56% 67.16% +1.60%
==========================================
Files 227 145 -82
Lines 30626 17972 -12654
==========================================
- Hits 20080 12071 -8009
+ Misses 10546 5901 -4645 |
Other things I am concerned about:
Maybe the following link can help us: http://code.activestate.com/recipes/408859-socketrecv-three-ways-to-turn-it-into-recvall/ |
|
I think we should be careful and not assume we are reading before another command is send. As is done in the example code I linked to:
All of this will complicate our code dramaticaly so I am wondering if we really should do all of this. I am leaning towards a "yes" though as it will make our code more robust. |
This kind of issues is also addressed in pyvisa-py (tcpip-socket backend). You may be interested in giving it a look. |
In general we should strongly consider if we really need this IP class. I would expect we can and I think we should replace both the magnet drivers with visa drivers that connects to visa over sockets. |
I agree! We do not want to duplicate efforts |
|
@WilliamHPNielsen would be useful to look at this in the context of the mercury |
I'll test it with the old (current) |
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.
From what I could see playing around with the MercuryiPS, this works well.
@sohailc, do you see any reason to not merge this guy? |
Well I still don't like the way partial messages are treated. I think there is a more robust way to do this and one which does not throw away anything. Here is an example of how I would do this: https://gist.github.com/sohailc/a0fcbfff5359f5fc4cc1ad9f2e0707ba |
@sohailc imho this is of little consequence. If we ever read more than one reply it is likely due to a non empty output buffer even before whatever write command we did. Most likely the output of the last recv is the answer that you actually want. In principle we need to prevent this from ever happening as the reply is likely to be wrong no matter what. Honestly perhaps we should just raise and close the connection in that case. |
Jens wrote: "If we ever read more than one reply it is likely due to a non empty output buffer even before whatever write command we did." I'm sorry Jens, but I just don't agree. I think we need to write our code and flexible and general as possible and not include any assumptions like the one you are making. Keep in mind that this modification will not only affect the MercuryiPS driver but all IPInstruments. If we receive jibberish from the Mercury power controller the proper location to raise and close the connection is in the instrument driver, not in the base IPInstrument class which I think we should program as general as possible. I you guys disagree feel free to merge this PR, but it is just not the way I would do things. |
Ok Sohail I will be more explicit. I think your solution will result in wrong results in all relevant cases for all our IP Instruments. I have identified a real use case where it will result in incorrect results. This applies to all IPInstruments and not specifically the mercury. I cannot see any use cases where it does anything useful. If you can identify such a use case I am happy to reconsider Try looking at the _recv function and how it is used. It is only used after a send command. Each send command must result in a single reply or no reply. If it does not the whole class is fundamentally broken. |
I have not tested my particular implementation with any instruments. I was trying to explain that there should be more robust ways of dealing with instrument replies. I am sure you are right that my solution will result in wrong results, but can you share your reasoning? Which use cases have you identified? As for your question of when my solution would be useful, what about if a reply to a _recv command is so long that an instrument sends it in two or more chunks? We have never seen that but we cannot altogether rule it out, especially for older instruments. |
You know what, I think you are right. The case that a reply is send in two chucks is already taken care of in this PR. I am approving this PR |
qcodes/instrument/ip.py
Outdated
@@ -183,7 +215,7 @@ def ask_raw(self, cmd): | |||
def __del__(self): | |||
self.close() | |||
|
|||
def snapshot_base(self, update=False): | |||
def snapshot_base(self, update=False, params_to_skip_update=None): |
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.
Please add this arg in the docstring with a description what "params_to_skip_update" means
@@ -25,6 +29,11 @@ class IPInstrument(Instrument): | |||
|
|||
terminator (str): Character(s) to terminate each send. Default '\n'. | |||
|
|||
read_terminator: Character(s) to look for at the end of each read. |
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 wonder if the read_terminator should not default to terminator
rtloc = result.find(self.read_terminator) | ||
if rtloc >= 0: | ||
if rtloc + rtl < len(result): | ||
log.warning("Multiple (partial) results received. " |
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.
Maybe we can have a look at this to see how we can handle receiving multiple partial messages: https://gist.github.com/sohailc/1a5db7bb260f36b94b552e630ea844f6
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.
A little better:
https://gist.github.com/sohailc/a0fcbfff5359f5fc4cc1ad9f2e0707ba
Here is a real world example for an instrument with two parameters volt and curr The output buffer on the instrument contains a reply to a command that has never been read i.e. "1.0e1V\n". This is not read due to a crash at an earlier run. It's not about 2 or more chunks that's already handled correctly we are in any case reading until a termination char in this PR. It is only about two or more termination characters within one message. And as far as I can see your solution will still not handle that hypothetical situation correctly. If a command to the instrument returns two partial results separated by a newline char the ask command will still only return the first part and the next one will be left in the internal buffer of the Instrument for the next ask command giving an incorrect result here. |
@sohailc Just saw your message glad that we understand each other. This still need real world testing before merging can be considdered. And I think we should look into better handling of Interrupts within the _recv command. I suspect these are a source on the kind of inconsistency that I talk about |
Actually my solution does not handle it any better. I stand by what I said earlier. I think we should raise. |
Also, I am not raising issues dealing with multiprocessing/threading, but technically I think our solution might be thread unsafe (right?). I am not objecting because for now QCoDeS is single process anyway. But what if two processes call _recv and the internal instrument buffer contains two responses? I suppose we can deal with these kind of issues when we implement multiprocessing in the weeks/months to come? |
I suspect that no instrument is thread safe see #621 for an example of what can happen in a Visa instrument. We should have a good system, locks or otherwise as part of a multiprocessing architecture to ensure that an instrument can only be called from one thread |
@jenshnielsen said:
As you saw, I did some preliminary testing (basically: it doesn't crash upon init, and commands are sent and read). I'll be happy to try and reproduce an actual fail case and see how the code lives up. What would you consider sufficient testing with an actual instrument? |
The one thing that i suspect still needs improving is the behavior when doing interrupts in the middle of get and set commands. I think it's hard to reproduce the issue that this aims to fix in a real world scenario. It is so far pure speculation that a recv may only return a partial command. (But according to the specs it is possible) But I suspect this may happen with high volumes of network traffic perhaps coupled with high cpu utilization |
We are generally moving away from the ip instrument so lets drop this |
Hopefully fix stability issues that we have been seeing with the AMI 430 magnet
The idea is to ensure that we always recv until we get read termination char. The current implementation assumes that one recv is always enough to get all the data. That is probably true in almost all cases but might be wrong under heavily congested network traffic.
Still need to check the Terminator for the Mercury. Judging from the manual there may not be one.
Currently it is set to None