-
Notifications
You must be signed in to change notification settings - Fork 1
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 service for AimTTiPL-P #149
Conversation
from dcps import AimTTiPPL | ||
|
||
|
||
class AimTtiPlp(Service): |
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.
Not sure I like this class name...
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.
@ehpor should I use this abomination of a class name or do you have other suggestions? :P An alternative would be AimTTiPLP
like in the dcps
library, although I think that might override it in the name space (same name)?
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.
Name doesn't matter to me. I would not use the same name as the library one.
try: | ||
# Get an update for this channel | ||
voltage = self.voltage.get_next_frame(10).data[0] | ||
current = self.current.get_next_frame(10).data[0] |
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 won't work as expected. If you don't get an update frame on the voltage and the current in the same iteration of this loop, then the setVoltage()
and setCurrent()
will not be called. You should use threads to monitor both. Then, you'd also wanna use a lock to avoid simultaneous access to the physical device from those two threads.
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.
Yup, like any other service that needs to monitor several data streams. I also need to extend this to several output channels with two data streams each, but I stopped so I can think about how to do that.
Sounds like it's as simple as having twice as many (monitored) data streams/threads as output channels (each requiring a voltage and current data stream). Is that right? Is there any better way to do that than to call them 'voltage_{channelnumber}' and 'current_{channelnumber}' and spin off a monitoring thread for each?
@ehpor this should now be functional (albeit untested) code, do you agree? I decided to split monitoring methods in two separate ones to save myself the |
I might have gone a little too fast with this implementation - it is currently very limited. The power supply in question has various modes it can be run in and I don't think I will be able to implement all possibilities (they're not endless, but about 6 too many compared to what we need). Being able to switch between constant voltage and constant current mode seems to be the absolute basis, but even so I feel like I would have to add respective data streams and mode switches. Not sure if I should move this over to thd-controls instead and just implement the one mode we're using right now. I will put this on hold to think about it. |
f34b639
to
1068198
Compare
Hey @ehpor could you help me out with the couple of weird problems I have? I am testing this on hardware right now and I feel like I am very close to make things work. I manage to establish a connection to the device with this service and get returns from the commands (that do not use data streams). I am equally able to access the data streams of the service, submit to and read from them, however, my device shows no reaction. Since I am perfectly able to control it just by using the code from within the service in a separate terminal through a connection without using catkit2, it looks to me like I messed up the threads and how they're set up with the main function. Do you maybe see what is wrong? Also, when I Ctlr+C on the server, I can see that this service gets closed down, but it immediately tries to restart which yields a bunch of:
Eventually, the restarting request seems to time out and everything closes all right, but I do not know what is causing this. This also causes the GUI to get stuck, which I have to force shut down. |
try: | ||
# Get an update for this channel | ||
frame = self.current_commands[channel_name].get_next_frame(10) | ||
value = frame.data |
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.
Note: value
is a Numpy array here. You probably want frame.data[0]
.
self.device._instWrite(str) | ||
|
||
# Give some time for power suppluy to respond | ||
time.sleep(self.device._wait) |
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.
Prefer self.sleep()
since this also quits sleeping if the service is being asked to shut down (ie. it checks the shutdown flag internally).
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.
Same for other places in this file.
Reading and writing from/to datastreams is completely independent from the service being responsive. No service code is ran when interacting with its datastreams.
I looked briefly at the code, but the threads look fine by eye. I found one error though with how you're reading from the datastreams. And one related to sleeping, which is very likely unrelated.
This is likely the GUI trying to restart the service.
|
for channel_name in self.channels.keys(): | ||
self.set_voltage(channel_name, value=0) | ||
self.set_current(channel_name, value=0) | ||
self.device.outputOff(self.channels[channel_name]['channel']) |
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 don't think you're joining the threads.
value = frame.data | ||
|
||
# Update the device | ||
with self.lock_for_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.
I'd move this lock to inside self.set_current()
and the same for self.set_voltage()
. That offers more protection.
b7c38bb
to
bc06d45
Compare
Thanks @ehpor! I will be back in the lab on Monday to test on hardware. |
After some final small fixes, this is now working as expected on hardware. |
@ehpor thanks for helping me figure this one out! I had quite some troubles considering what a simple device it is... |
str = 'TRIPRST' | ||
self.device._instWrite(str) | ||
|
||
# Give some time for power suppluy to respond |
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.
Tiny typo in "suppluy"
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.
One general remark, I got a bit confused by all the "channels" names. There's a self.channels
dict, that has channel_names
dict as keys, that have channel
, that is a number. Maybe call everywhere this last-stage channel
channel_number
everywhere, as it is done in some places? (eg L164)
Apart from that, everything looks good, very tiny typos in commented code.
"""Attempt to clear all trip conditions. | ||
|
||
This only works if the device has not been set up so that it can only reset trip conditions manually on the | ||
front panel, or by cycling the AC power. | ||
""" | ||
str = 'TRIPRST' | ||
self.device._instWrite(str) | ||
|
||
# Give some time for power suppluy to respond |
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.
Minor comment: would there be a way to log if it worked or not? Like a feedback from the device saying "Reset trip conditions not possible"
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 scanning the manual real quick, it doesn't seem there is. There are ways to get potential errors back however I never tested that.
str = 'OCP{} {}'.format(channel_number, value) | ||
self.device._instWrite(str) | ||
|
||
# Give some time for power suppluy to respond |
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.
Also tiny typo
str = 'OVP{} {}'.format(channel_number, value) | ||
self.device._instWrite(str) | ||
|
||
# Give some time for power suppluy to respond |
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.
Copy-pasted tiny typo? :P
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.
Indeed ^^
Yeah I get that, however the structure with the dict is necessary because I want to have named channels like in the case of the DMs. There, the |
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.
Looks good
Thanks @raphaelpclt! |
Fixes #148.
Manufacturer info:
https://www.aimtti.com/product-category/dc-power-supplies/aim-plseries
Note how "The New PL-P Series is the programmable (remote control) version of the New PL Series [...]", since the above website lists both variants (PL and PL-P).
Usage example for the
dcps
library taken from here:https://github.com/sgoadhouse/dcps/blob/master/dcps/AimTTiPLP.py
Edit: Could also be interesting to look into https://msl-equipment.readthedocs.io/en/latest/_api/msl.equipment.resources.aim_tti.html#module-msl.equipment.resources.aim_tti
Todo:
Notes: