[Adding Dave’s initial reply to Mark, for the record]
Hey Mark,
Thanks for the input! This is pretty close to what we already have, but Chris and I were on our way to streamlining/paring down the API, and a few of the existing methods don’t match up with what you’ve written in terms of when they get called. We should probably hash this out a bit further:
Two functions that get called on experiment load/unload (these can be constructor and destructor, or can be explicit)
So these are obviously already in place… actually there are two API hooks during parsing: one is the constructor, the other is the method that is currently called “startup” (which we are planning to rename “initialize”). This one gets called after all IO-related objects (including channel definitions, etc.) have all been constructed and attached together. For all intents and purposes this is the one where you’d do all of your initialization, since all of the possible constraints / requirements can’t be known at construction.
Two functions that get called on experiment start/stop (i.e. clicking the green/red buttons in the client). Suggestions for names: startup() and shutdown()
What would these do that would be different from start/stopDeviceIO? Incidentally, the “stop” one already does get called automatically when the experiment goes into an idle state. Is there something substantially different you’d want to happen on a “shutdown”?
The current thinking has been that the onus is on “start” and “stop” to ensure that the hardware is in a good state when enabling IO. Irrespective of what other calls are available in the base IO class, if you’re worried about hiccups, I don’t think you necessarily want to wait until the experiment idles or restarts to take care of the problem (this could be a long time for your IO to be in a bad state).
That said, it wouldn’t be a big deal at all to add an “experimentDidIdle()” (or some such) notification method that gets called whenever the experiment “parks”, and it’s similarly not a big stretch to add in a method to be called at experiment start. If we do add them, I’d prefer that their names be more tightly coupled to the circumstances under which they are called rather than something like “shutdown”, since the majority of devices don’t actually require any shutdown or other kind of baby-sitting when the experiment idles (it also makes it less clear what is meant to happen in each method). Implementing these methods would obviously be optional for IO devices, with the idle one calling stopDeviceIO by default and the experiment start one being a no op.
The other option (not mutually exclusive) would be to add a “reset” method (and associated action), to give you explicit control over when a “reset” takes place. The advantage of this is that you could reset the hardware more frequently if your device is particularly flakey, or your experiment is particularly long. A problem with the experiment start/stop events is that they occur at different/unpredictable intervals for different experiments. In my lab, we often don’t start/stop an experiment except at the beginning/end of a session. In this case, the hardware would never get reset, so this would not be a robust place to put defensive reseting if it were mission critical.
Two functions that get called at the beginning/end of each trial. Right now we are using startDeviceIO and stopDeviceIO for this, and calling them through XML.
Yes, this is what these are for… did you want something different here? Not everyone uses trial objects the same way, so I don’t think there is a reasonable way to make this automatic (and I think you probably also want to retain control over this at arbitrary granularity – block-wise, trial-wise, etc.).
The other thing to bear in mind in all of this is that you can always create a custom action that works on your particular IODevice to do whatever you want (e.g. if you wanted more than one kind of {hard | soft | etc} reset, or there were some other imperative that it made sense to send to your particular device). These actions can be “bundled” in the same plugin as the IO device itself. If you’re interested in doing this, let me know; the process is fairly straightforward. But if it is a common pattern that more than one IO device will want, we should build it into the core.
– Dave