High Level Overview

Separation of Concerns

This software is split into the Backend and Frontend processes. The Backend is a computation process which does all the heavy lifting, whereas the Frontend process, either the GUI or the CLI, is simply the user interface.

The Backend runs on a given IP:Port and simply listens for a connection from a Frontend Process. Note that a Backend can only connect to one Frontend at a time.

The Frontend, either the GUI or CLI (not both), attempts to connect to the default IP:PORT specified in the python arguments, but also allows the user to disconnect and reconnect from and to different Backends on different IP:PORT combinations. Note that a Frontend can only connect to one Backend at a time.

Note that there is also a Plotter process, which is started if the --plot argument is provided to main.py. This process receives raw data when available from the Backend over a Pipe and graphs it. It is important to realize that the Plotter has not been updated to use a socket, so currently the Plotter can only run in parallel with the Backend on the same machine. This is usually not an issue since the Plotter is usually used when recording data locally, in which case the Backend should be running locally as well.

Code Layout

The Backend source code is contained in the aptly named python/src/backend/ directory, with the GUI in python/src/gui and the CLI in python/src/cli. For each of these, the main process is defined in app.py as a class overriding the Process. In these classes, __init__() initializes attributes that can be pickled and sent to the process when it is started, and run() is the function that is run once the process is created.

Cython utilities are contained in python/src/cython_utils, and as noted in [[Building]], these files must be compiled & built before we can run the software.

Common python classes and definitions are contained in python/src/common/, these are used by both the Frontends and Backend.

python/src/bin/ contains resources used by the project, such as images and audio files.

python/src/peripherals/ contains other software used by the project, such as OpenBCI software for communicating with the OpenBCI EMG & EEG Boards, and Mujoco, which is the physics simulation software we use to simulate prosthetic limbs.

Lastly, python/src/plotter/ contains the plotter process, which is fairly simple - only requiring one file app.py.

Process Communication

Between Frontend & Backend

A Frontend and Backend communicate over one simple socket at the specified IP:PORT. The Frontend and Backend both send and receive messages through their corresponding Threads which are running socket_send_thread_func() and socket_receive_thread_func().

Message Types sent by the Frontend:

  • Command: Specifies the command and specified arguments for the Backend to run. These commands run synchronously; i.e. if a command is sent while the Backend is still running a previous command, the new command will be queued.

  • AsyncCommand: Specifies an asynchronous command (and arguments) for the Backend to run. These commands are run immediately by the Backend’s socket_recv_thread and therefore will run even if the Backend is already running a separate Command. These commands should not computationally heavy.

Message Types sent by the Backend:

  • CommandResponse: Signifies completion of the previously issued Command, either due to success or error.

  • AsyncCommandResponse: A response to a previous AsyncCommand. Not all AyncCommands require a response. An AsyncCommandResponse is tied to a specific AsyncCommand via a UUID generated during creation of the original AsyncCommand.

  • str: A message to print to the console.

  • dict: An status update for the GUI. This includes values like FPS, Sample Rate, and Data Status, among others.

Between Backend and Plotter

The Backend communicates with the Plotter using a multiprocessing Pipe. The Backend sends PlotCommand objects to the Plotter from the Backend’s plot_thread. Any data to plot or update the graphs is contained in PlotCommand.kwargs.