IODevice/IOChannel questions

Christopher Stawarz's Avatar

Christopher Stawarz

21 Apr, 2010 05:20 PM

Hi Dave,

I'm trying to answer some questions from John Maunsell about implementing a core plugin for dynamic visual stimuli. From John:

The main questions I have are on the IODevice and dynamic visual stimuli. I would like to know which methods are required, and which methods MWorks will call at setup and destruction (and in what order). It would also be good if we could get the same information for the IOChannels, and some overview of how those are supposed to work.

I know essentially nothing about how this stuff works, so I'm trying to track down the info that John needs. To that end:

  • Can any of the existing IODevice/IOChannel plugins be considered canonical, meaning they're implemented using the current, recommended approach? (The NE500 plugin seems pretty recent, so thus far I've been using it as my example.)
  • Should the comments in IODevice.{h,cpp} be believed, or are they likely to be out of date?
  • Are there any up-to-date docs about this kind of stuff?

I'd appreciate any insights you can share, as my only other option is to dig through the code and try to piece things together myself :)

Thanks,
Chris

  1. Support Staff 1 Posted by Christopher Sta... on 21 Apr, 2010 05:24 PM

    Christopher Stawarz's Avatar

    Hey Chris (and John),

    History of MW IO Devices

    Originally, MW had an elaborate model for IO devices where a complex negotiation between capabilities and requests could be managed. This was modeled roughly on the USB HID negotiation process. This snowballed out of control and became more general and complex than anyone needed or wanted to deal with. The ITC18 plugin (which Jim wrote) is a good example of why this structure isn't a good idea. In a perfect world, this kind of complex negotiation could make a ton of sense if you wanted to write protocols that could port easily to new setups without necessarily knowing what IO device would be used, or if you wanted to expose as much flexibility in the device as possible. In reality, you usually know exactly what IO device you'll be using and how, and you perhaps just want to target that device as simply as possible.

    Current state of things

    The more "modern" IO devices that I have written (e.g. NE500), technically still descend from the IODevice base class, but they mostly just override and ignore the formalities of the base class. I included NE500 in the public distribution of MW (and not in my private plugin repository) specifically to serve as an example of a simpler, less fussy IO device.

    The current thinking is that since all IODevices are implemented as plugins (which didn't exist when we spec'ed the origin IODevice base class), then you really ought to be able to do whatever configuration you need to do in the ComponentFactory when the object is created (when the XML is parsed). You can still be guaranteed that all of the IODevice base class calls will be called in the same order as before (e.g. attachPhysicalDevice gets called after parsing, but before your paradigm starts), but there is no obligation on your part to use any of these methods, and there is rarely a strong reason to do so. That said, the base class IO device negotiation stuff does work exactly as advertised, and you can certainly use it if you find it helpful.

    At some point, as long as there aren't strong objections from users, I'd like to refactor everything except for the "start" and "stop" calls into a subclass, so general/complex implementations such as the ITC18 won't need to be rewritten (except superficially), while at the same time, allowing simpler IO devices to avoid being burdened with that extra bloat. Maybe 0.4.5 will be a good time to do that.

    Upshot for how I'd recommend you write IO plugins

    Handle the configuration of the IODevice object however you like in the plugin factory object and the object's constructor. Your object should descend from IODevice, but I'd encourage you to "hollow-out" everything except startDeviceIO/stopDeviceIO. Your object should respond to these start/stop methods (these are driven by the stock StartDeviceIO and StopDeviceIO actions), presuming it makes sense for them to do so. This gives the user the opportunity to mark sections where they want IO active, e.g. in case the device clock drifts over time and resyncing is needed, or if there would be too much data saved to disk if the device were running constantly.

    This also implies that you can safely ignore the IOChannel, IOChannelRequest, etc. classes, unless they are specifically useful to you. The addChild() call that gets called during parsing allow you to associate whatever custom channel (or other) objects you'd like with the IO device. NE500 works this way.

    I'd also recommend building something only at the level of generality that you actually need right now (e.g. no need to implement features of the hardware you don't anticipate actually using; you can always implement that later if needed).

    Everything else is up to you. As with all plugins, you can interact with special features of your particular device via custom actions (e.g. "OpenBombBayDoorsAction" or "ActivateLaserAction", whose sole purpose in life would be to call a custom method on your IO device class); these custom actions would also be packaged up in the same plugin as the IO device component. Data passing can happen via Variables / VariableNotifications, just as everywhere else in MW.

    Let me know if any of this doesn't make sense. Hope this helps,
    Dave

  2. Christopher Stawarz closed this discussion on 21 Apr, 2010 05:24 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac

Recent Discussions

17 May, 2022 02:12 PM
16 May, 2022 03:12 PM
04 May, 2022 06:02 PM
03 May, 2022 01:30 PM
02 May, 2022 10:47 PM