labscript-suite / labscript-utils

Shared modules used by the 𝘭𝘢𝘣𝘴𝘤𝘳𝘪𝘱𝘵 𝘴𝘶𝘪𝘵𝘦. Includes a graphical exception handler, debug tools, configuration management, cross platform filepath conversions, unit conversions and custom GUI widgets.
http://labscriptsuite.org
Other
2 stars 47 forks source link

Add enum control widget #26

Open philipstarkey opened 5 years ago

philipstarkey commented 5 years ago

Original report (archived issue) by David Meyer (Bitbucket: dihm, GitHub: dihm).


When programming devices with physical front panels, it is often the case that a control I’d like to manipulate with labscript (at a static level) is best described as an enum (looking at you SRS). I propose we add a basic combobox based widget (like AnalogOutput or DigitalOutput) that can be stuffed with a dictionary of labels and programming values at runtime from the device blacs tab. These controls do not always have an associated output or input class associated with them, rather being a device level setting that influences general operation.

I’m happy to work on this one since we have a current need, but I’d like a bit of guidance on how to integrate with the rest of the BLACS auto-creation of widgets magic. If I have understood correctly, the current paradigm for an AnalogOutput widget is to have the device_tab call a widget auto-populating function which creates AnalogOutput widgets which in turn links to the labscript AO class. This ensures settings from the connection table and blacs tab can configure each output correctly. What is the best way to modify this paradigm?

My initial thought is to drop auto-populating in the blacs_tab in favor writing something akin to ddsoutput.py for any (often conglomerate) control that would be device specific and kept in the device folder. I’m a little less clear on how to handle enum settings at the AO class level. Should I create a commensurate class that behaves as a StaticAO with discrete values set by dictionary?

Anyway, this is starting to get long and likely confusing since I don’t really know what I’m talking about. So I’ll end by describing what our need is and what I would like to see.

We have an RF Signal Generator (SRS SG386) that has modulation controls. The controllable options include: Enable (on/off), Type (AM/FM/PM/Sweep), Function (Sine, Triangle, Square, External), Deviation (float), Depth (float), and External Coupling (AC/DC). Since all of these controls are inter-related and operate on the same function, it would be nice to create a monolithic control widget that groups them together in the BLACS tab and allows user control while enforcing allowable settings. Slightly beyond the scope of this discussion, when writing an experiment script; having corresponding SG386.mod(Enable) and/or SG386.mod.Depth(1MHz) commands would be great. Getting started, StaticAO/DO covers the boolean and float options just fine. But I need an enum for everything else.

philipstarkey commented 5 years ago

Original comment by Philip Starkey (Bitbucket: philipstarkey, GitHub: philipstarkey).


Hi David,

I think in general adding a enum output type makes sense (but not sure if it is what you want for this device or not)

However, if these are just configuration settings, they probably shouldn’t be integrated with the output types of BLACS and labscript. In such a case I would probably suggest just adding additional widgets to your device’s BLACS tab as, for example, the PulseBlaster does for status widgets.

But, assuming it makes sense to go down the route of integrating it as an “output type”, here is the information you need to know!

BLACS
The fundamental level is the AO/DO/DDS/Image output classes. Instances of these we call “output objects” and are ultimately what store the front panel value, interact with saving/restoring settings and trigger events to reprogram the device via the statemachine and worker process. Think of these as a datastore (one per output channel). They are created by the DeviceTab.create_XX_outputs methods.

Widgets can then be created and linked to the output object. We provide the DeviceTab.auto_create_widgets and AO.create_widget (and equivalent for the other classes) methods to streamline this, but actually any appropriate widget can be connected in to the output object (datastore), see the add_widget method of each output class. A BLACS tab can use whatever method it likes to create the widget, it just needs to call appropriate methods to link the widget to the output object. Each output object can have an arbitrary number of widgets connected to it, and they’ll all update together when one changes.

We then have a method to automatically place widgets in collapsible groups (DeviceTab.auto_place_widgets). This is completely optional as you can place widgets in the tab however you like (or do both like the PulseBlaster).

So for your situation, you probably want to create an Enum output class in output_classes.py, update DeviceTab to handle the new output type. You can then opt to create widgets manually, or using the newly created automatic methods, and place them in a custom layout of your choosing for a particular device by bypassing auto_place_widgets.

labscript
On the labscript side you will want to subclass Output and create an EnumOut and StaticEnumOut classes. These would be similar to DigitalOut I guess, but have more states.

To be honest I’m not sure if it makes sense to treat them as output types or not…it almost seems like not (they seem more like labscript device instantiation arguments?), but in that case BLACS has no machinery for allowing device configuration to be modified in manual mode. It all has to be implemented separately in each device tab, which doesn’t seem ideal.

philipstarkey commented 5 years ago

Original comment by David Meyer (Bitbucket: dihm, GitHub: dihm).


To be honest I’m not sure if it makes sense to treat them as output types or not…it almost seems like not (they seem more like labscript device instantiation arguments?), but in that case BLACS has no machinery for allowing device configuration to be modified in manual mode. It all has to be implemented separately in each device tab, which doesn’t seem ideal.

This is exactly the succinct way to say what I’m looking for. I don’t really need an output, I just want manual control of of some settings without needing to recompile the connectiontable or running a complete shot nor writing everything from scratch. I also like the idea of separating a little from typical instantiation arguments since it makes smart programming those settings a bit more consistent with typical outputs. I’ve already done all this once (an SRS lock-in amp) and it is really tedious with plenty of boiler-plate that should be recycled.

Might there be room to make a new type of labscript class that is controlled like an output in the BLACS tab but links directly to “device instantiation objects” (not “output objects”) as far as shots and h5files are concerned? I’m not entirely sure how to think about structure or its implementation but if I’m going to write something from scratch anyway, might as well try to write what I need instead of a kludging on to the output system.

philipstarkey commented 5 years ago

Original comment by Philip Starkey (Bitbucket: philipstarkey, GitHub: philipstarkey).


Cool, so I’ve had a bit of a think about what we could do…this may not be well thought through so please adjust as it makes sense!

We currently have two categories of device properties:

The connection table properties should be those that cannot be configured shot-to-shot. This is why a recompile of the connection table is needed, along with a restart of BLACS. Usually they are device initialisation settings (hence the need to restart BLACS).

The device properties are those that can be changed shot-to-shot, for example data acquisition rates.

It seems to me that the type of properties you want to change on the front panel are going to match the device properties.

I thus propose that we implement something in BLACS device tabs to:

This will require labscript device code to define meta-data about each property (so that we can render appropriate widgets in BLACS and do appropriate type checking in labscript). Off the top of my head I think this would be:

We (a long time ago - when we first created the labscript_devices library from code that existed in BLACS/labscript) planned to have a class for each device that just held device specifications. For example, clock frequencies or update rates, number of output channels, etc. Basically the labscript class attributes and the dictionaries in the BLACS GUI class that contain channel configuration information were going to live in a single class so that we could reuse then in labscript/ruviewer/BLACS without redefining quantities multiple times (and without having to import the classes meant for other components). We didn’t end up doing this because we (a) focused on other more important things and (b) had to import all the classes in every program anyway due to the file structure. The latter is of course now fixed!

So I propose:

A further extension of this could be to solve labscript issue #49 (labscript-suite/labscript#49) and extend the above proposal to allow properties to be associated with device channels rather than just devices. That’s probably beyond the scope of this though (just thought I’d mention it) and would require a tonne more work I think.

Thoughts @dihm and @chrisjbillington ?

philipstarkey commented 5 years ago

Original comment by David Meyer (Bitbucket: dihm, GitHub: dihm).


Finally back from vacation and pushed out a paper so I can think on this again.

Overall I think this is a decent framework to start with. A few specific notes to begin:

that the meta-data about device/connection table properties be placed in a new class, along with any other device specification information collected from the device classes,

While initially worried about changing the structure of labscript devices, I’m coming around. It would be nice to have a central location for putting in device information instead of having to spread it over all the files.

remove the need to explicitly specify the allowed kwargs to the __init__ method of the labscript class

Is there a way to do this while maintaining syntax helps for what kwargs are expected? It might get tricky to know what options are allowed/mandatory if their definition is hidden in another class that isn’t directly interacted with.

integrate (or supersede?) the @set_passed_properties decorator

I would personally love to use this as an opportunity to streamline the process of getting connectiontable_properties through to the BLACS worker. The current set_passed_properties → get_properties → worker_init_kwargs method feels a bit clunky and is now particularly annoying since it requires syncing parameter names across three files by hand. It would be nice to mark a property as ‘saved’ and automatically have all saved properties sent to the BLACS worker.

working out how best to display this information in a tab by default, while also allowing subclasses to override how it is displayed (much like they can with output widgets).

Would a pop-up window be a crazy idea here? I don’t really like it but I’m also worried about widget bloat with some devices that can have truly prodigious numbers of device properties. I’m going to guess you’ll say I should just learn to actually use the collapsible groups feature of my tabs?

Anyway, I like this approach. My only concern is that I’m not really qualified/have sufficient time to implement it all. If I’m going to have any hope of progress I’ll need some way to break this down into somewhat smaller chunks. My initial thoughts are:

  1. Need to pick a device to prototype with. Ideally it is mainlined with mock functionality so it can be well tested even without physical hardware. I believe that leaves the DummyPseudoclock and the IMAQdxCamera. It would be nice to get something like this going for the cameras (restarting BLACS to change exposure times isn’t really tenable) but it is also a pretty unique implementation and I’m wary to start there. I guess I could do the dummy clock but then there isn’t any physical hardware to readily test with.
  2. Device picked, I would want to start on the meta-data class. It seems the easiest place to start with the fewest changes needed to labscript itself. Will need to decide conventions like meta-class naming and data structure.
  3. Next is the underlying code for handling connection table and device properties. Particularly need to figure out how to attach some kind of output objects. Will likely need to add/modify output objects for the purpose. I’m pretty fuzzy on how this part will actually work in practice, particularly the desired syntax in a shot script for setting a device property. Should it remain in the init method or will the settings be controlled more like static outputs?
  4. Create extra property widgets. Get some framework together for the BLACS tab to connect to the output property objects for display.
  5. Add the BLACS methods for programming the properties. Assuming everything else is working this should be relatively simple.

Just getting this written down has me worried this is going to turn into a pretty time sink for everyone. While I’m always a fan of complete, long-term solutions is this also a moment to reconsider something shorter term instead?