Backend Overview¶
Initialization¶
The Backend’s initialization starts in Backend.__init__()
. This executes when the Backend object is created in python/src/main.py
. Note that the instance variables initialized in __init__()
must be pickleable since the the Backend object is pickled when it becomes a new process. Instance variables can also be initialized at the beginning of Backend.run()
to avoid this issue.
Here’s a brief description of some of the important instance variables in the Backend class in the order they are defined:
current_command
: The currentCommand
that is being processed by the Backend.handlers
: A dict mappingCommand
to Backend handler methods.settings
: TheGeneralSettings
object used to hold some basic settings.output_settings
: TheOutputDataSettings
object holding settings related to decoder output, such as latching.feat_settings
: TheFeatureSettings
object holding settings related to decoder input features, such as the state and feature history.features
: Current list ofFeature
objects used to compute the feature input to the decoder.autoencoder
: The current autoencoder, which is a type of Feature essentially.decoder
: The current movement intent decoder object, which we can train and test, among other functionalities.trainer
: The currentTrainer
object, which specifies settings related to training.collector
: The currentCollector
object, which specifies settings related to collecting raw data.tester
: The currentTester
object, which specifies settings related to testing a decoder.results
: Current list ofResult
objects, which are used to compute test results such as MSE.data
: The currentData
object, which holds the input and output of the decoder.test_results
: The actual test results which were computed using theself.results
list ofResult
objects.
There are still many more instance variables defined, many of which are very important as well.
Run Loop¶
The Backend process’ run loop is contained in Backend.run()
. This function first initializes logging, gpu support, movement detectors, socket-related events and queues, starts all the threads, and then enters the process’ main run loop.
In this run loop, the Backend first gets a command from the self.command_queue
, which is just a standard Python Queue, using a predefined timeout. If this command is Commands.QUIT
, the run loop exits and we disconnect the socket, stop all the threads, and exit the process. Otherwise, we execute the handler function for the given command, which is located in the dictionary self.handlers
, which maps Commands
to Backend member functions. After completing the command, the Backend responds with either CommandResponses.SUCCESS
or CommandResponses.ERROR
, along with any necessary parameters.
Globals¶
In python/src/backend/globals.py
we define a few variables that are used by multiple Backend threads.
Namely, we define raw_data
which holds the raw EMG or EEG data received from the OpenBCI modules.
Editing this object requires holding the raw_data_lock
mutex, preventing the Backend and its multiple threads from editing the raw data simultaneously.
The raw data settings are also stored in the global variable raw_data_settings
. This doesn’t require a mutex lock since only the Backend will edit this object.
The last global variable is the filter
. This Filter
object is used to filter the raw EMG or EEG data. This is stored in a global variable because the Backend’s plot_thread
sends it to the Plotter process so that we can display the filtered raw data. The filter_graphs
variable is the flag that determines whether the Plotter process should filter its data before graphing.
Threads¶
The Backend currently uses 6 threads (not including the logging thread):
Socket Listening Thread: Listens for a new connection from a Frontend (unless already connected)
Socket Recv Thread: Receives
Command
s andAsyncCommand
s from the Frontend.Socket Send Thread: Sends
CommandResponse
s,AsyncCommandResponse
s andstr
console print statements to the Frontend.GUI Update Thread: Sends
dict
updates to the GUI at 2 Hz containing values like FPS.Plot Thread: When active, sends raw data from
globals.raw_data.full_buffer
to the Plotter process for graphing.EMG Thread: When active, receives raw EMG or EEG data from external OpenBCI Board and adds it to
globals.raw_data
.