ioio.lib.api
Interface Sequencer

All Superinterfaces:
Closeable

public interface Sequencer
extends Closeable

A waveform sequencer.

The Sequencer interface enables generation of very precisely-timed digital waveforms, primarily targeted for different kinds of actuators, such as different kinds of motors, solenoids and LEDs. The output comprises one or more channels, each of which can have one of several different types. The output channels type and their assignments to pins are determined when the Sequencer instance is created and cannot be changed during its lifetime. A Sequencer instance is obtained via IOIO#openSequencer(ChannelConfig[]).

Each channel is configured with one of the Sequencer.ChannelConfig subclasses. For example, Sequencer.ChannelCueBinary specified a binary output, useful for controlling an LED or a solenoid. This configuration includes, among other things, the pin specification for this channel. See the individual subclasses documentation for more information.

Once instantiated, the Sequencer gets fed with a stream of <cue, duration> pairs. A cue is a set of instructions telling each of the channels what to do, or more precisely, one Sequencer.ChannelCue per channel. The concrete type of the Sequencer.ChannelCue depends on the channel type, for example, a channel that has been configured with Sequencer.ChannelConfigBinary, expects a Sequencer.ChannelCueBinary for its cue. In this example, the cue will specify whether this output should be high or low. When a cue is combined with a duration, it has the semantics of "for this much time, generate this waveform". A stream of such pairs can represent a complex multi-channel waveform.

The above <cue, duration> pairs are pushed into the Sequencer via the push(ChannelCue[], int) method. They will not yet be executed, but rather queue up. There is a limited number of cues that can be queued. Attempting to push more will block until a cue has been executed. In order to avoid blocking (especially during the initial fill of the queue - see below), the available() method returns the number of pushes that can be done without blocking. Once a few of them have been queued up, execution may start by calling start() . During execution, more cues can be pushed, and ideally fast enough so that execution never needs to stall. During execution, pause() will suspend execution as soon as the currently executed cue is done, without clearing the queue of pending cues. Operation can then be resumed by calling start() again. Calling stop() will immediately stop execution and clear the queue. Execution progress can be tracked at any time by calling #numCuesStarted(), which will increment every time actual execution of a cue has begun. It will be reset back to 0 following a stop().

Pre-fill

In order to avoid stalls immediately after start(), it is recommended to pre-fill the cue FIFO priorly. The recommended sequence of operations is:
 // Open the sequencer.
 Sequencer s = ioio_.openSequencer(...);
 // At this point, the FIFO might still be at zero capacity, wait until opening is complete.
 s.waitEventType(Sequencer.Event.Type.STOPPED);
 // Now our FIFO is empty and at full capacity. Fill it entirely.
 while (s.available() > 0) {
   s.push(...);
 }
 // Now we can start!
 s.start();
 

Manual Operation

In some cases it is useful to be able to execute some cues while the Sequencer is paused or stopped without having to clear the queue. For this purpose, the manualStart(ChannelCue[]) command can be used. Calling it will immediately begin execution of a given cue, without changing the queue previously given. Unlike cues scheduled via push(ChannelCue[], int), these cues have no duration, and will keep executing until manualStop() is called. After that, normal operation can be resumed by calling start() again.

States

This table summarizes the different states the Sequencer can be in, and the methods which can be called to change it:
Current State Method Next State
Idle start() Running
manualStart(ChannelCue[]) Manual
stop() Idle (and clear the queue)
manualStop() Idle (no-op)
Running pause() Idle
stop() Idle (and clear the queue)
Manual manualStop() Idle
stop() Idle (and clear the queue)
#manualStart() Manual (new cue)

Clocking

All the timing information provided to a single channel is based on a clock which implies a time-base. For some channel types, the clock is determined upon instantiation; for others it can be set on a per-cue basis. Once a clock rate is set, all timing values used for that channel are in units of this time-base. Using a faster clock rate enables faster waveforms to be generated as well as have better timing resolution for everything. However, since most timing values used in this API have a limited range, using a faster clock would also limit the maximum duration of some signals. As a rule of thumb, choosing the highest possible clock rate that can satisfy the duration requirements is the right choice.

Stalls

It is possible that the client does not push cues fast enough to keep up with execution. In that case, the queue will drain and execution will stall. All channel types have well-defined behaviors in case of a stall, for example, a binary channel allows the user to explicitly determine whether it should be go back to its initial value or retain the current value when stalled. Once stalled, as soon as a new cue gets pushed, operation will resume immediately without having to call start().

Keeping track of execution

Since execution of queued events is asynchronous, it is sometimes required to track their execution progress, for example, for keeping a user interface synchronized with the actual state of a multi-axis machine.

The client can poll for the last event that occured via getLastEvent(), or block until the next one arrives using waitEvent(). The latter features a limited-size queue, so events are not lost as long as the client reads continuously. The queue is initially 32-events long, but could be changed using setEventQueueSize(int). The sequencer will report the following kinds of events, via the Sequencer.Event type:

STOPPED
The sequencer has been stopped (and the cue FIFO is now empty). This is also the event that is returned when calling getLastEvent() before any event has arrived.
CUE_STARTED
A new cue has just started execution.
PAUSED
A cue has just finished execution and progress has been paused as result of an explicit pause request.
STALLED
A cue has just finished execution and progress has been paused as result of the queue running empty. In this case, the state of the sequencer does not change (i.e. it is still Running), and pushing addition events will immediately resume execution.
CLOSED
The sequencer has been closed. This is mostly intended for gracefully exiting a thread which is constantly blocking on waitEvent()


Nested Class Summary
static interface Sequencer.ChannelConfig
          A marker interface for channel configurations.
static class Sequencer.ChannelConfigBinary
          Configuration for a binary channel.
static class Sequencer.ChannelConfigFmSpeed
          Configuration for a channel of type FM speed.
static class Sequencer.ChannelConfigPwmPosition
          Configuration for a channel of type PWM Position.
static class Sequencer.ChannelConfigPwmSpeed
          Configuration for a channel of type PWM speed.
static class Sequencer.ChannelConfigSteps
          Configuration for a channel of type steps.
static interface Sequencer.ChannelCue
          A marker interface for channel cues.
static class Sequencer.ChannelCueBinary
          A cue for a binary channel.
static class Sequencer.ChannelCueFmSpeed
          A cue for a FM speed channel.
static class Sequencer.ChannelCuePwmPosition
          A cue for a PWM position channel.
static class Sequencer.ChannelCuePwmSpeed
          A cue for a PWM speed channel.
static class Sequencer.ChannelCueSteps
          A cue for a steps channel.
static class Sequencer.Clock
          A clock rate selection, which implies a time-base.
static class Sequencer.Event
          A sequencer event.
 
Method Summary
 int available()
          Get the number of cues which can be pushed without blocking.
 Sequencer.Event getLastEvent()
          Get the most recent execution event.
 void manualStart(Sequencer.ChannelCue[] cues)
          Execute a cue until further notice.
 void manualStop()
          Stop a manual cue currently running.
 void pause()
          Pause execution of the sequence.
 void push(Sequencer.ChannelCue[] cues, int duration)
          Push a timed cue to the sequencer.
 void setEventQueueSize(int size)
          Sets a new size for the incoming event queue.
 void start()
          Start execution of the sequence.
 void stop()
          Stop execution of the sequence.
 Sequencer.Event waitEvent()
          Waits until an execution event occurs and returns it.
 void waitEventType(Sequencer.Event.Type type)
          A convenience method for blocking until an event of a certain type appears on the event queue.
 
Methods inherited from interface ioio.lib.api.Closeable
close
 

Method Detail

push

void push(Sequencer.ChannelCue[] cues,
          int duration)
          throws ConnectionLostException,
                 java.lang.InterruptedException
Push a timed cue to the sequencer.

This method will block until there is at least one free space in the FIFO (which may be forever if the sequencer is not running -- use Sequencer.available() first in this case). Then, it will queue the cue for execution.

Parameters:
cues - An array of channel cues. Has to be the exact same length as the Sequencer.ChannelConfig array that was used to configure the sequencer. Each element's type should be the counterpart of the corresponding configuration type. For example, it element number 5 in the configuration array was of type Sequencer.ChannleConfigBinary, then cues[5] needs to be of type Sequencer.ChannelCueBinary
duration - The time duration for which this cue is to be executed, before moving to the next cue (or stalling). The units are 16 microseconds. For example, passing 10 here would mean a duration of 160 microseconds. Valid values are [2..65536] (approx. 1.05 seconds).
Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.
java.lang.InterruptedException - The operation was interrupted before completion.

manualStart

void manualStart(Sequencer.ChannelCue[] cues)
                 throws ConnectionLostException
Execute a cue until further notice.

This method may only be called when the sequencer is not in the Running state. It will not affect the queue of pending timed-cues previously filled via push(ChannelCue[], int) calls. The cue will be executed until explicitly stopped via manualStop(). A subsequent call to manualStart(ChannelCue[]) can be used to immediately have a new cue take into effect.

Parameters:
cues - An array of channel cues to execute. See the description of the same argument in push(ChannelCue[], int) for details.
Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.

manualStop

void manualStop()
                throws ConnectionLostException
Stop a manual cue currently running.

This may be called only when a the sequencer is not in the Running state, typically in the Manual state, as result of a previous manualStart(ChannelCue[]). This causes the execution to stop immediately and the sequencer is now back in paused mode, ready for another manual cue or for resuming execution of its previously queued sequence. Calling while in the Idle state is legal, but does nothing.

Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.

start

void start()
           throws ConnectionLostException
Start execution of the sequence.

This method will cause any previously queued cues (via push(ChannelCue[], int)) to start execution in order of pushing, according to their specified timings. The sequencer must be paused before calling this method.

Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.

pause

void pause()
           throws ConnectionLostException
Pause execution of the sequence.

This method can be called when the sequencer is running, as result of previous invocation of start(). It will cause execution to suspend as soon as the currently executing cue is done. The queue of pending cues will not be affected, and operation can be resumed seamlessly by calling start() again.

Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.

stop

void stop()
          throws ConnectionLostException
Stop execution of the sequence.

This will cause the sequence execution to stop immediately (without waiting for the current cue to complete). All previously queued cues will be discarded. The sequence will then go to paused state.

Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.

available

int available()
              throws ConnectionLostException
Get the number of cues which can be pushed without blocking.

This is useful for pre-filling the queue before starting the sequencer, or if the client wants to do other operations on the same thread that pushes cues. The value returned will indicate how many calls to push(ChannelCue[], int) can be completed without blocking.

Returns:
The number of available slots in the cue FIFO.
Throws:
ConnectionLostException

getLastEvent

Sequencer.Event getLastEvent()
                             throws ConnectionLostException
Get the most recent execution event.

This includes the event type and the number of cues that started executing, since opening the sequencer or the last call to stop(). Immediately after opening the sequencer, the event type may be Event.Type.CLOSED, and as soon as the sequencer finished opening an Event.Type.STOPPED will be sent.

Returns:
The last event.
Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.

waitEvent

Sequencer.Event waitEvent()
                          throws ConnectionLostException,
                                 java.lang.InterruptedException
Waits until an execution event occurs and returns it.

In case the client is not reading fast enough, older events will be discarded as new once arrive, so that the queue always stores the most recent events.

Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.
java.lang.InterruptedException - The operation was interrupted before completion.

waitEventType

void waitEventType(Sequencer.Event.Type type)
                   throws ConnectionLostException,
                          java.lang.InterruptedException
A convenience method for blocking until an event of a certain type appears on the event queue. All events proceeding this event type, including the event of the requested type will be removed from the queue and discarded.

Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.
java.lang.InterruptedException - The operation was interrupted before completion.

setEventQueueSize

void setEventQueueSize(int size)
                       throws ConnectionLostException
Sets a new size for the incoming event queue.

Initially the size of the queue is 32, which should suffice for most purposes. If, however, a client is not able to read frequently enough to not miss events, increasing the size is an option.

Any pending events will be discarded. It is recommended to call this method only once, immediately after opening the sequencer.

Parameters:
size - The new queue size.
Throws:
ConnectionLostException - Connection to the IOIO was lost before or during this operation.