IODevice API comment

Hi Chris and Dave,

After banging on the driver for the new USB device (Labjack) I have a better idea of what we would like in an IODevice API.

  1. Two functions that get called on experiment load/unload (these can be constructor and destructor, or can be explicit)
  2. 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()
  3. 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.

Seems clear that (1) is needed. Use case for (2): there are occasional hiccups in our hardware that require resetting the device. It would be nice to force a reset every time the experiment is started. Use case for (3): JHRM would like to resync the ITC18 clock on every trial to prevent drift.

However you guys feel is the best way to implement this is fine. Thanks. Jon - would these take care of all your needs for the Eyelink driver?

thank you,
Mark

Hi Dave,

Thanks for the reply.

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.

Perfect. The name is fine too. We can do setup in initialize().
FYI we are not now using any channel capabilities either in the LabJack plugin or Eyelink plugin.

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”?

My use case is this: I leave the client and server running for days. When I run a new subject (2x per day), I load a new variable set without loading a new experiment, and when I click start I’d like the IO device to be reset then.
startDeviceIO() and stopDeviceIO() could also be used, although for USB devices init can take tens of milliseconds. Which is why I wanted to avoid doing it every trial.

You might ask - why not unload and reload the experiment xml for each subject? I could, but the reason I’m not doing it now is that the client plugin windows lose their state, so I also have to reopen. Not a huge deal but a pain.

I now think that you’re right, I’ll just do this in start/stopDeviceIO each trial.

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.

Maybe worth having these in the API even if we resolve my issue differently.

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.

Right, I agree, no manual reset. Either it should work for a whole session or you should do it in start/stopDeviceIO.

Thanks for the other information.
Finally, I’m having some trouble with tearing down the IODevice on experiment unload. It seems to never get destructed if you have loaded expt and run at least one trial. If you just load and unload it works correctly. Something somewhere is probably holding on to a pointer to it. I will look into this, I don’t have any wellformed questions now.

best,
Mark

[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

FYI, I pushed the streamlined IODevice base class to github a couple days ago. To see the new, greatly-simplified interface, check out Core/IODevices/IODevice.h.

Chris