From Bcontrol
Revision as of 07:13, 13 August 2007 by CarlosBrody (talk | contribs) (The action string and its 5 possible values)

Overview of Dispatcher

Dispatcher is an interface between protocol code and the Real-Time Linux State Machine (RTLSM). It provides opening and closing of protocols, automatic segmentation of data from the RTLSM into trials, parsing of trials, and an automatic keeping track of trial and state machine history over trials. Dispatcher also takes care of the regular polling of the RTLSM.

One of the principal advantages of using Dispatcher is that it is aware of the trial structure, and thus knows precisely which state machine definition corresponded to which trial and exactly when state machine definitions switched, and can therefore precisely interpret events from the RTLSM and parse them for you. (Parsing is described here; as a brief example, if your state machine has states named "woohee" and "dinkle", Dispatcher will automatically provide you with a variable called "parsed_events" that is a structure with elements states.woohee and state.dinkle; the entries in those elements will tell you the time stamps of entering and exiting from those states.)

Dispatcher is written using Solo, but to use the functionality that Dispatcher provides, you don't need to know Solo at all.

Once again, here is documentation on the result of parsing -- i.e., this is documentation on the format in which information about state machine events is returned to you by dispatcher.

Currently (14:07, 1 June 2007 (EDT)) there are only three protocols that work with dispatcher. They can be used as templates for you to make your own protocol. The protocols are PokesplotDemo, Minimal, and CenterPokeFollower. The last is planned as the answer key to one of the excercises, so-- don't look at it unless you have to!

Opening/closing Dispatcher

To start dispatcher, make sure you've called newstartup (See Startup Guide.). The command in the startup process that starts dispatcher is:

  >> dispatcher('init')

To rescan the existing protocols that dispatcher can start,

  >> dispatcher('rescan_protocols')

To close dispatcher,

  >> dispatcher('close')

Template code to write a dispatcher protocol

To write a dispatcher protocol, make a directory in Protocols, "Protocols/@mynewprotocol". Within that directory, place a constructor file, "Protocols/@mynewprotocol/mynewprotocol.m". (You should replace "mynewprotocol" with the name of your choice, of course.) The .m file must contain, at a minimum, what is in this example dispatcher template. With that plus the information below, you are good to go!

You might want to look at existing template protocols, like PokesplotDemo, or Minimal. You might also want to look at the Plugins page, to see what pre-existing code goodies are available for you to pick up.

The action string and its 5 possible values

Dispatcher interacts with your protocol by calling the constructor of your protocol code always with a single argument, a string usually called the "action." (E.g., if your code is in @yourprotocol/yourprotocol.m, it'll be yourprotocol.m that gets called with a single string as an argument.) This single argument can have one of 5 values:

This is the cue for initialization of the protocol: start any windows or GUIs that you may want to have, initialize variables, etc. Send the state machine definition for the first trial to dispatcher, to be forwarded to the RTLSM.
The protocol is to be closed. Delete your figure windows, delete your variables, etc.
Dispatcher has detected that the RTLSM has entered one of the "prepare_next_trial" states. (See trial structure.) Your protocol should prepare the next trial's state machine definition, and send it to dispatcher to be forwarded to the RTLSM. You can send any state machine definition that you like. (But you should make sure that your state machine is guaranteed to hit "check_next_trial_ready" at some point, otherwise the following trial may never start!) At the same time, you should indicate which states in your definition form the "prepare_next_trial" states. Note that this is also called when the experiment starts - at the beginning of the first trial.
An 'update' call (see below) is guaranteed to have been made immediately before the 'prepare_next_trial' call to your protocol. That is, you don't need to process the latest events in your 'prepare_next_trial' code, leave latest event processing to your 'update' code.
(Note: Since the state machine definition can be different from trial to trial, so can the set of trials that compose the "prepare_next_trial" set.)
Dispatcher has detected a state_0 boundary, meaning that a trial has been completed and the next trial is about to start. (See trial structure.)
We are in the middle of a trial. Dispatcher has polled the RTLSM to ask for the latest events, and is reporting them to you.

Variables instantiated by Dispatcher

Any Dispatcher protocol should have, near the top of its code, the line "GetSoloFunctionArgs(obj);", where "obj" is an object of your protocol's class. (This line is already part of the default code in the "do not modify" section of the template.) As a consequence, in addition to calling your protocol code with the above 5 different possible actions strings, Dispatcher will instantiate certain variables within your protocol, so that you have the content of these variables available to you. They will be instantiated as regular local Matlab variables (not SoloParamHandles). In any method of your protocol (i.e., m-files within your @yourprotocol/ directory), the line "GetSoloFunctionArgs(obj);" will instantiate the same variables. The variables are:

The number of trials that have reached one of their "prepare_next_trial" states.
The number of times the RTLSM has gone through state_0.
In between the initial state_0 in a trial, and entering the first of the "prepare_next_trial" states in that trial, n_started_trials=1+n_done_trials. In between entering the first of the "prepare_next_trial" states and the final state_0 in a trial, n_started_trials=n_done_trials.
This is always 1 less than n_started_trials. A trial is completed when the jump to state 0, to start the next trial, occurs.
A structure (see parsing), the result of running disassemble.m with the 'parsed_structure' flag set to 1 on all the events that have occurred from the initial state_0 in the current trial until now.
parsed_events.states.state_0 is guaranteed to have at least one row, and that first row will be of the form [NaN t0] where t0 is the time of the start of the current trial. parsed_events.states.state_0 is guaranteed to have at most two rows. If the second row exists, the trial has been completed, and the second row is guaranteed to be of the form [t1 NaN] where t1 is the time of the end of the current trial.
A cell that is n_completed_trials-by-1 in size. Each element is the parsed_events structure for a completed trial. Thus, parsed_events_history{1} is the parsed_events structure for the 1st trial; parsed_events_history{4} is the parsed_events structure for the 4th trial; and so on.
A structure (see parsing), the result of running disassemble.m with the 'parsed_structure' flag set to 1 on all the events that have occurred from the last time the RTLSM was polled until now. If no events have occurred, all the elements of this structure will be empty. latest_parsed_events is a useful variable when you want to focus just on updating based on the most recent events.
A @StateMachineAssembler object, as was used for the current trial.
This is the object that is used to disassemble events into parsed_events and latest_parsed_events.
A cell that is n_completed_trials-by-1 in size. Each element is the current_assembler for a completed trial. Thus, current_assembler_history{1} is the current_assembler for the 1st trial; current_assembler_history{4} is the current_assembler for the 4th trial; and so on.
A numeric matrix containing all the events from the start of the current trial until now, as obtained directly from the RTLSM.
parsed_events = disassemble(current_assembler, raw_events, 'parsed_structure', 1);
The first row of raw_events is guaranteed to be the transition out of state 0 that initiated the current trial. If the trial is completed, the last row of raw_events is guaranteed to be the transition into state 0.
A cell that is n_completed_trials-by-1 in size. Each element is the raw_events for a completed trial. Thus, raw_events_history{1} is the raw_events for the 1st trial; raw_events_history{4} is the raw_events for the 4th trial; and so on.
parsed_events_history{n} = disassemble(current_events_history{n}, raw_events_history{n}, 'parsed_structure', 1);

Runrats, A dispatcher frontend

Runrats is a simplified GUI to streamline the process of setting up a rig for a behavioral session. In a lab, it would generally be started by a startup script when launching MATLAB. It can be run by typing


at the matlab command line. It will read the directory structure in the Main_Data_Directory settings preference (or if this is not set, it will assume that your settings are in ../SoloData/Settings) and populate a list of experimenters. The technician or researcher running rats then follows the simple steps:

  1. Pick an experimenter
  2. Pick a rat
  3. Press LOAD
  4. When ready, press RUN
  5. When the experiment is over, press END
  6. Goto step 1.

Behind the scenes, the runrats GUI finds the latest settings file for that rat, loads the appropriate protocol and settings. At the end of the experiment, it stops the State Machine, calls the end_session action of the protocol and saves the settings and data.