Skip to content

Latest commit

 

History

History
333 lines (200 loc) · 11.8 KB

components.md

File metadata and controls

333 lines (200 loc) · 11.8 KB

Creating and using Components

Overview

Components are abstractions that allow tasks to communicate with external objects like levers, lights, or even network events without details of the specific implementation. To develop a new component, the user creates a subclass of the base Component class or preexisting component in the py-behav-box-v2/source/Components folder.

Typically, components do not enforce a particular physical implementation but abstract functionality that could be implemented on a variety of hardware. For example, a Toggle, a component that can be set to be on or off, could be used to represent a variety of hardware like lights, motors, or arbitrary digital outputs. Rather than having a separate component class for each of these equivalent cases, the details of interacting with hardware are instead handled by implementation specific Sources.

Defining the component type

All components have a type that indicates the types of data components either receive or output. Component types are represented as an enum in the base Component class (see the reference section for the full type list). All subclasses of Component must override the get_type method which should return one of the possible indicated types. Using the example of the Toggle component which solely outputs a simple digital on or off signal:

def get_type():
    return Component.Type.DIGITAL_OUTPUT

Component states

All components have states which represent how a component is behaving at any given time. There are no requirements for the data type of a component's state but all components must override the get_state method. In the Toggle component example, the method just returns a boolean indicating if the toggle is current on or off. However, get_state could return arbitrarily complex objects for more flexible components like touch screens or analog inputs.

Component constructor

All components are linked to a particular Source via a component_id and component_address. This linkage is mediated by a Task object task. When Components are used outside of Tasks (for Sources or GUIs) the Task parameter can be None. These three variables are all passed to the component constructor along with metadata and configured using local AddressFiles. All metadata will be processed to create class attributes corresponding to the entries in the dictionary. The component constructor can be overridden if necessary to define variables for the component subclass (if necessary, these variables can be locally configured using metadata in AddressFiles). Depending on the Component, some metadata may be required or option (indicated in the package reference below). The example below shows how the default constructor could be overridden to keep track of a state variable:

def __init__(self, task, component_id, component_address):
    super().__init__(task, component_id, component_address)
    self.state = False

Interacting with sources

To query data from the component's source, use the Component get_state method. To output to the Source use the Component write method with a single input of any type. The type of data returned by get_state or required by write is flexible and can depend on the Source class. Therefore, we recommend that these methods should be wrapped in component-specific versions like the toggle method for Toggle components:

def toggle(self, on):
    self.write(on)
    self.state = on

Rather than calling write or get_state directly, methods like toggle ensure states are properly updated and conventions for particular types of components kept to. This is especially helpful when subclassing components when implementation specific features may be necessary (look at BinaryInput and OEBinaryInput for a more in depth example). When new data is received from a Source, the behavior for updating the state of a Source is handled by the update method. This method should return True if the value of the state was changed and False otherwise.

Package reference

The methods and classes detailed below are contained in the Components package.

Component base class

The Component class in the Component module is the super class for all components.

__init__

__init__(task : Task, component_id : str, component_address : str)

Constructor for a new component connecting to Task task registered with component_id at component_address. Values for task, component_id, component_address should be provided by local AddressFiles. Any attributes (metadata) necessary for the component should be defined here (ex. frame rates for videos).

Type

class Type(Enum)

Type is an enum representing the possible types of components to indicate expected data to a source. The possible component types are shown below:

DIGITAL_INPUT = 0   # The Component solely provides digital input
DIGITAL_OUTPUT = 1  # The Component solely receives digital output
ANALOG_INPUT = 2    # The Component solely provides analog input
ANALOG_OUTPUT = 3   # The Component solely receives analog output
INPUT = 4           # Arbitrary input type
OUTPUT = 5          # Arbitrary output type
BOTH = 6            # The Component both inputs and outputs (arbitrary type)

get_type

get_type() -> None

Returns the component type as one of the symbolic names described in the Type class. This method must be overridden by all subclasses of Component.

Example override:

def get_type(self):
    return Component.Type.DIGITAL_OUTPUT

get_state

get_state() -> Any

Returns the state the component currently is in.

write

write(msg : Any) -> None

Outputs a value via the source. The data type of msg will depend on the source.

Example usage:

self.write(True)

update

update(value: Any) -> bool

Process new information from the Source to update the Component's state.

close

close() -> None

Notifies the source that the component should be closed.

Example usage:

self.close()

Component subclasses

Output

class Output(Component)
Type: OUTPUT

General purpose/non-specific output class.

Attributes:

state : Any Current state value

Input

class Input(Component)
Type: INPUT

General purpose/non-specific input class.

Attributes:

state : Any Current state value

Both

class Both(Component)
Type: BOTH

General purpose/non-specific class that supports inputs and outputs.

Attributes:

state : Any Current state value

Toggle

class Toggle(Output)
Type: DIGITAL_OUTPUT

Toggles typically represent digital outputs like lights and motors and can be set to on or off states.

Methods:

toggle(on : bool) -> None Set the on/off state of the toggle.

Attributes:

state : bool Current state value (active/inactive)

BinaryInput

class BinaryInput(Input)
Type: DIGITAL_INPUT

BinaryInputs represent inputs that can only be on or off like switches, IR sensors, or levers.

Attributes:

state : bool Current state value (active/inactive)

AnalogInput

class AnalogInput(Input)
Type: ANALOG_INPUT

AnalogInputs represent inputs that can take on a continuous range of values like light, pressure, or temperature sensors.

Methods:

check() -> float Queries and returns the current value of the component.

Attributes:

state : float Current state value

AnalogOutput

class AnalogOutput(Output)
Type: ANALOG_OUTPUT

AnalogOutputs represent outputs that can take on a continuous range of values like intensity, frequency, and duration.

Attributes:

state : float Current state value

TimedToggle

class TimedToggle(Toggle)
Type: DIGITAL_OUTPUT

TimedToggles will only remain active for a set amount of time.

Methods:

toggle(on : Union[float, bool]) -> None Set the on/off state of the toggle either as an active duration or a steady on/off value.

Attributes:

count: int Counter for number of instances a timed event has occurred

OEBinaryInput

class OEBinaryInput(BinaryInput)
Type: DIGITAL_INPUT

OEBinaryInputs represent TTL broadcast events originating from OpenEphys. The class overrides the check method to handle the JSON data but has identical outputs to the standard BinaryInput.

Optional Metadata:

rising: bool = True Indicator for whether to respond to rising events

falling: bool = False Indicator for whether to respond to falling events

TouchBinaryInput

class TouchBinaryInput(BinaryInput)
Type: DIGITAL_INPUT

TouchBinaryInputs contain additional information about the location of an input for use with hardware like touchscreens.

Attributes:

definition: Any Application specific description of the touch object

pos: (int, int) Tuple indicating the last location touched

Video

class Video(Both)
Type: BOTH

Video components represent the "barebones" information necessary for recording a video file.

Methods:

start() -> None Starts a video recording

stop() -> None Stops a video recording

Attributes:

state: bool Indicates if the video is currently being recorded

name: str Represents the name for the saved video file. Default implementation uses the current timestamp as the name

Stimmer

class Stimmer(Output)
Type: Output

Stimmer is an abstract class to consolidate the basic functionality required for stimulation.

Methods:

parametrize(pnum : int, outs : list[int], per : int, dur : int, amps : ndarray, durs : list[int]) -> None Defines a pulse train with ID pnum. The type of stimulation for each potential channel is indicated by a list of ints outs. The stimulation has a period of per. The pulse train itself is described by a set of stages with amplitudes amps and durations durs. The number of stages corresponds to the number of columns in amps which is equivalent to the length of durs. The number of rows in amps corresponds to the number of channels. This format was inspired by programming for the StimJim.

start(pnum : int, stype: str) -> None Starts the pulse train with ID pnum

StimJim

class StimJim(Output)
Type: Output

StimJim encapsulates behavior for a component that would accept a list of instructions to generate a stimulation waveform. The default configuration is readily compatible with the StimJim.

Methods:

trigger(ichan : int, pnum : int, falling : int = 0) -> None Configures input ichan on the component to trigger the stimulation pulse train with ID pnum. Defaults to trigger off a rising input but can be configured to falling inputs if falling is changed to 1.

start(pnum : int, stype: str = 'T') -> None Starts the pulse train with ID pnum

WaveformStim

class WaveformStim(Output)
Type: AnalogOutput

WaveformStim defines a stimulation waveform as a matrix given configuration parameters that could be output using a device like a DAQ.

Methods:

parametrize(pnum : int, _, per : int, dur : int, amps : ndarray, durs : list[int]) -> None Defines a pulse train with ID pnum. The stimulation has a period of per. The pulse train itself is described by a set of stages with amplitudes amps and durations durs. The number of stages corresponds to the number of columns in amps which is equivalent to the length of durs. The number of rows in amps corresponds to the number of channels. This format was inspired by programming for the StimJim.

start(pnum : int, stype: str = None) -> None Starts the pulse train with ID pnum