Technical Reference:App Connector

From BCI2000 Wiki
Jump to: navigation, search

NOTE

The AppConnector interface has been superseded by a variety of BCI2000 scripting and remote control options. It is being kept for backward compatibility.

As a functional equivalent to the ConnectorOutput filter, execute the following scripting code at startup, e.g., from a batch file:

ADD WATCH State1 State2 State3 ... StateN AT localhost:12345

As a result, the values of the state variables listed will be sent to the named UDP port whenever one of them changes. Multiple ADD WATCH commands may be used to send change notifications for different states (or expressions) to different UDP ports:

ADD WATCH TargetCode AT localhost:2006
ADD WATCH ResultCode AT localhost:2007

As a functional equivalent to the ConnectorInput filter, you may establish a TCP connection to localhost:3999 and issue a SET STATE command, e.g.

SET STATE TargetCode 3

in order to set TargetCode to 3.

For more information about controlling BCI2000 from other applications, see

User Reference:Operator Module Scripting#Commands operating on States

User Reference:Operator Module Scripting#Commands operating on Events

User Reference:Operator Module Scripting#Commands operating on Watches

Programming Reference:BCI2000Remote Class

User Reference:Module Command Line Options#Operator Module

Introduction

The BCI2000 external application interface provides a bi-directional link to exchange information with external processes running on the same machine, or on a different machine over a local network. Via the external application interface, read/write access to BCI2000 state vector information and to the control signal is possible. An external application may read the ResultCode state to access the classification result, set the TargetCode state to control the user's task, or get access to the control signal that is calculated by SignalProcessing so as to control an external output device (such as a robotic arm or a web browser). Multiple instances of BCI2000 running on separate machines may share sequencing and control signal information, allowing for interactive applications such as games.

Scope

The scope of this interface is to provide access to internal BCI2000 information for cases in which the generation of a full-fledged BCI2000 module is impractical. Such a case might be the control of external applications that practically do not allow full incorporation into the BCI2000 framework (such as the Dasher system for efficient low-bandwidth spelling).

This interface is not intended to replace the existing BCI2000 framework for BCI2000 communication. The advantages of writing modules that are fully integrated into the BCI2000 framework are that their configuration is achieved through the same interface as other BCI2000 configuration, that this configuration is stored in the data file along with all other system parameters, and that the state of the module at any given time is encoded in event markers that are also stored in the data file. In contrast, control of an external device using the External Application Interface implies that the configuration of the external device has to be done outside of BCI2000, that this corresponding configuration is not stored along with the data file, and that the internal state of the output device is not automatically saved together with the brain signals (although it is possible to introduce your own state variables for this purpose using the operator module's INSERT STATE scripting command). Having no configuration and state information present in the data file will make it more difficult to reconstruct what exactly was going on during an experimental session. It is thus important to keep this in mind when using this possibility.

Design

The design of the external application interface aims at simplicity, and at minimal interference with the timing of the signal flow through the BCI2000 system. With this in mind, a connection-less, UDP based transmission protocol was chosen rather than one based on TCP. This comes at the cost of a possible loss, or reordering of protocol messages. To keep the probability for such losses as low as possible, and their consequences as local as possible, messages have been designed to be short, self-contained, and redundantly encoded in a human readable fashion.

The connectionless nature of UDP implies that there is no server or client in the asymmetric sense that applies for TCP connections. Rather, processes write to local or remote UDP ports, and read from local UDP ports, whenever applicable. Thus, for bi-directional communication between machine A running BCI2000 and machine B running the external application, there will be two UDP ports involved:

  • a port on machine B into which BCI2000 writes out its messages to the external application, and
  • a port on machine A into which the external application writes its messages to BCI2000.

In most cases, both BCI2000 and the external application will run on the same machine, i.e., A and B will refer to the same machine, and both ports will be local. Still, they are distinct ports.

For communication involving a large number of network nodes, or unreliable connections, we suggest using local UDP communication, in conjunction with locally executed TCP/IP server processes that forward messages to a TCP connection between the two remote machines.

Description

For each block of data processed by the BCI2000 system, two types of information are sent out and may be received from the external application interface:

Sending data occurs immediately after the task filter of the application module processes the data; receiving occurs immediately before the task filter. This ensures that changes resulting from user choices are sent out immediately, and that received information will immediately be available to the task filter. IP addresses and ports used are user-configurable. Sending and receiving may not use the same address and port.

Protocol

Messages consist of a name and a value, separated by white space and terminated with a single newline ('\n'==0x0a) character. Names may identify

  • BCI2000 states by name -- then followed by an integer value in decimal ASCII representation;
  • Signal elements in the form Signal(<channel>,<element>) -- then followed by a float value in decimal ASCII representation. Channel and element indices are given in zero-based form.

Examples

Running 0\n
ResultCode 2\n
Signal(1,0) 1e-8\n

Note that the first example will switch BCI2000 into a suspended state. While the system is in that state, no communication is possible over the application protocol.

The meaning of control signal indices depends on the application module used. For the control signal in a typical cursor task, there are up to three channels holding a single element each, with channel indices 0, 1, and 2 corresponding to movement in X, Y, and Z direction, such that

Signal(1,0) 1e-2\n

would indicate a value of 1e-2 for the control signal representing movement in Y direction.

Parameterization from within BCI2000

BCI2000 reads data from a local IP socket specified by the ConnectorInputAddress parameter, and writes data out into the socket specified by the ConnectorOutputAddress parameter. Sockets are specified by an address/port combination. Addresses may be host names, or numerical IP addresses. Address and port are separated by a colon as in

localhost:5000
134.2.103.151:20321

For incoming values, messages are filtered by name using a list of allowed names present in the ConnectorInputFilter parameter. To allow signal messages, allowed signal elements must be specified including their indices. To allow all names, enter an asterisk (*) as the only list entry.

Examples

BCI2000 example code

#include <iostream>
#include "SockStream.h"

using namespace std;

int main( int argc, char** argv )
{
  const char* address = "localhost:5000";
  if( argc > 1 )
    address = argv[ 1 ];

  receiving_udpsocket socket( address );
  sockstream connection( socket );
  string line;
  // Print each line of BCI2000 input to stdout.
  while( getline( connection, line ) )
    cout << line << endl;

  return 0;
}
  • Note: The above example program use BCI2000 socket stream utility classes contained in src/shared/utils/SockStream.cpp. You will need to add this file to your project to build the example program.

An external application reading information from BCI2000, running locally

  • Set the ConnectorOutputAddress parameter to a local address above 1024, such as localhost:5000.
  • In the external application, create a UDP socket and bind it to BCI2000's output port, i.e. localhost:5000.
  • Read from that socket as you would from a TCP socket.

An external application reading information from BCI2000, running on a remote machine

  • Set the ConnectorOutputAddress parameter to a remote address with a port above 1024, such as 134.2.102.151:20321.
  • In the external program, create a UDP socket, and bind it to the remote machine's external address, i.e. 134.2.102.151:20321 rather than localhost:20321.
  • Read from that socket as you would from a TCP socket.

An external application sending information to BCI2000, running locally

  • Set the ConnectorInputAddress parameter to a local address with a port above 1024, such as localhost:5001.
  • Set the ConnectorInputFilter to * (a single asterisk).
  • In the external application, create a UDP socket and bind it to BCI2000's input port, i.e. localhost:5001.
  • Write to that socket whenever appropriate, without waiting for a connection to be established.

An external application sending information to BCI2000, running on a remote machine

  • Set the ConnectorInputAddress parameter to the local machine's external address, and a port above 1024, such as bci2000machine.yourdomain.org:20320.
  • In the external program, create a UDP socket and bind it to the BCI2000 machine's external address, i.e. bci2000machine.yourdomain.org:20320.
  • Write to that socket whenever appropriate, without waiting for a connection to be established.

See also

Technical Reference:System Design, Technical Reference:State Definition