Closed NitramC closed 3 years ago
Yeah, it is a bit ugly, but having all three facets is probably the best solution. Even if you could find a way to dynamically add and remove them, it's likely to be error-prone and lead to user confusion. For instance, imagine the state gets changed out-of band, or you're writing code in an IDE with code completion and can't access a property that would be available at that point of the code.
More generally, if you're interested in thinking about more ergonomic ways to interact with "channels" in instruments, you can check out the scopes/tektronix driver, where I was playing around with the idea of Channel
and Channels
objects.
You could imagine having some kind of "Channel"-like object that can either address both channels simultaneously, or be indexed to address them individually. For example, applied to magnitudes:
# "dual mode"
inst.mag = x
# "single mode"
inst.mag[1] = x
inst.mag[2] = y
This also might be a terrible and confusing idea, but it's fun to think about.
The last idea looks pretty promising to me. I will take a look at the scopes module and give it some more thought. Thanks!
OK, so this should have the behaviour you suggested:
class RefModeDependentFacet(object):
def __init__(self, doc=None, facetnames=[None]*3):
self.__doc__ = doc
self.facetnames = facetnames
def __get__(self, obj, objtype):
if obj.ref_mode == 0:
return getattr(obj, self.facetnames[0])
else:
return self.DualModeContainer(obj, self.facetnames)
def __set__(self, obj, value):
if obj.ref_mode == 0:
setattr(obj, self.facetnames[0], value)
else:
return self.DualModeContainer(obj, self.facetnames)
class DualModeContainer():
def __init__(self, obj, facetnames):
self.obj = obj
self.facetnames = facetnames
def __getitem__(self, key):
if key == 1:
return getattr(self.obj, self.facetnames[1])
elif key == 2:
return getattr(self.obj, self.facetnames[2])
else:
raise KeyError('Invalid key')
def __setitem__(self, key, value):
if key == 1:
return setattr(self.obj, self.facetnames[1], value)
elif key == 2:
return setattr(self.obj, self.facetnames[2], value)
else:
raise KeyError('Invalid key')
class SR7265(Lockin, VisaMixin):
mag = RefModeDependentFacet(
facetnames=['_mag', '_mag1', '_mag2'])
_mag = SCPI_Facet(
'MAG.',
...
)
_mag1 = SCPI_Facet(
'MAG1.',
...
)
_mag2 = SCPI_Facet(
'MAG2.',
...
)
In the end though, I think what I am trying to do is purely esthetic. The only benefit of this approach is that it hides three private facets behind one public one in the attribute list when tab-completing. The user will still need to be aware of the current REFMODE, and how to properly interact with this new Facet wrapper. There is also the added overhead of having to check the REFMODE on each get/set of a RefModeDependentFacet. One could cache the REFMODE value, but then there is the issue of out-of-band changes.
I am going to stick with the ugly straight-forward approach (that is also easier to maintain).
I am having a hard time deciding how to deal with a lock-in amplifier (Signal Recovery 7265) that effectively can be in one of two states, depending on the value of a parameter "REFMODE".
![image](https://user-images.githubusercontent.com/8950513/103465495-3c4d2f80-4d3c-11eb-95f1-7643fae9cbed.png)
So in the single reference state I would like to have class with this one property:
and in a dual reference state I would like to have a class with these two properties:
I have thought about dynamically removing and adding properties on a change of REFMODE, but it does not look like you can do that at an instance level -- you would have to do it at a class level, which would not be acceptable with multiple instruments.
I guess the most straight forward approach is to just have all three properties in the class, but it seems a shame to clutter the namespace with properties that don't really exist.
Any ideas?