Skip to content

Commit e652786

Browse files
committed
Add emulator support
- Add Emulator object that emulates bpod hardware behavior and reference it in the BpodBase class - Modify bpod classes to accommodate emulator functionality
1 parent 44dc099 commit e652786

8 files changed

Lines changed: 1056 additions & 103 deletions

File tree

pybpodapi/bpod/bpod_base.py

Lines changed: 220 additions & 52 deletions
Large diffs are not rendered by default.

pybpodapi/bpod/bpod_com_protocol.py

Lines changed: 4 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ class BpodCOMProtocol(BpodBase):
3030
3131
"""
3232

33-
def __init__(self, serial_port=None, sync_channel=None, sync_mode=None):
34-
super(BpodCOMProtocol, self).__init__(serial_port, sync_channel, sync_mode)
33+
def __init__(self, serial_port=None, sync_channel=None, sync_mode=None, emulator_mode=False):
34+
super(BpodCOMProtocol, self).__init__(serial_port, sync_channel, sync_mode, emulator_mode=emulator_mode)
3535

3636
self._arcom = None # type: ArCOM
3737
self.bpod_com_ready = False
3838

3939
# used to keep the list of msg ids sent using the load_serial_message function
4040
self.msg_id_list = [False for i in range(255)]
4141

42-
if self.serial_port:
42+
if self.serial_port and not emulator_mode:
4343
self.open()
4444

4545
def open(self):
@@ -52,39 +52,6 @@ def close(self):
5252
self._arcom.close()
5353
self.bpod_com_ready = False
5454

55-
def manual_override(self, channel_type, channel_name, channel_number, value):
56-
"""
57-
Manually override a Bpod channel
58-
59-
:param ChannelType channel_type: channel type input or output
60-
:param ChannelName channel_name: channel name like PWM, Valve, etc.
61-
:param channel_number:
62-
:param int value: value to write on channel
63-
"""
64-
if channel_type == ChannelType.INPUT:
65-
input_channel_name = channel_name + str(channel_number)
66-
channel_number = self.hardware.channels.input_channel_names.index(input_channel_name)
67-
try:
68-
self._bpodcom_override_input_state(channel_number, value)
69-
except:
70-
raise BpodErrorException(
71-
'Error using manual_override: {name} is not a valid channel name.'.format(name=channel_name))
72-
73-
elif channel_type == ChannelType.OUTPUT:
74-
if channel_name == 'Serial':
75-
self._bpodcom_send_byte_to_hardware_serial(channel_number, value)
76-
77-
else:
78-
try:
79-
output_channel_name = channel_name + str(channel_number)
80-
channel_number = self.hardware.channels.output_channel_names.index(output_channel_name)
81-
self._bpodcom_override_digital_hardware_state(channel_number, value)
82-
except:
83-
raise BpodErrorException('Error using manual_override: {name} is not a valid channel name.'.format(
84-
name=output_channel_name))
85-
else:
86-
raise BpodErrorException('Error using manualOverride: first argument must be "Input" or "Output".')
87-
8855
def _bpodcom_connect(self, serial_port, baudrate=115200, timeout=1):
8956
"""
9057
Connect to Bpod using serial connection
@@ -250,18 +217,7 @@ def _bpodcom_enable_ports(self, hardware):
250217
:rtype: bool
251218
"""
252219

253-
###### set inputs enabled or disabled #######################################################
254-
hardware.inputs_enabled = [0] * len(hardware.inputs)
255-
256-
for j, i in enumerate(hardware.bnc_inputports_indexes):
257-
hardware.inputs_enabled[i] = settings.BPOD_BNC_PORTS_ENABLED[j]
258-
259-
for j, i in enumerate(hardware.wired_inputports_indexes):
260-
hardware.inputs_enabled[i] = settings.BPOD_WIRED_PORTS_ENABLED[j]
261-
262-
for j, i in enumerate(hardware.behavior_inputports_indexes):
263-
hardware.inputs_enabled[i] = settings.BPOD_BEHAVIOR_PORTS_ENABLED[j]
264-
#############################################################################################
220+
hardware.configure_inputs()
265221

266222
logger.debug("Requesting ports enabling (%s)", SendMessageHeader.ENABLE_PORTS)
267223
logger.debug("Inputs enabled (%s): %s", len(hardware.inputs_enabled), hardware.inputs_enabled)

pybpodapi/bpod/bpod_io.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@ class BpodIO(BpodCOMProtocolModules):
2020
"""
2121
Bpod I/O logic.
2222
"""
23-
def __init__(self, serial_port=None, workspace_path=None, session_name=None, sync_channel=None, sync_mode=None):
23+
def __init__(self, serial_port=None, workspace_path=None, session_name=None, sync_channel=None, sync_mode=None, emulator_mode=False):
2424
self.workspace_path = workspace_path if workspace_path is not None else settings.PYBPOD_SESSION_PATH
2525
self.session_name = session_name if session_name is not None else settings.PYBPOD_SESSION
26-
27-
super(BpodIO, self).__init__(serial_port, sync_channel, sync_mode)
26+
super(BpodIO, self).__init__(serial_port, sync_channel, sync_mode, emulator_mode)
2827

2928
self.session += SessionInfo("This is a PYBPOD file. Find more info at http://pybpod.readthedocs.io")
3029
self.session += SessionInfo(Session.INFO_BPODAPI_VERSION, pybpodapi.__version__)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .emulator import Emulator
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
The following values must be set according to Bpod firmware specification:
3+
https://github.com/sanworks/Bpod_StateMachine_Firmware/blob/v22/Dev/StateMachineFirmware/StateMachineFirmware.ino
4+
5+
0 = USB (default), 1 = Ethernet (w/ Bpod Ethernet Module)
6+
IMPORTANT: PC via Ethernet requires State Machine v2.0 or newer.
7+
"""
8+
__author__ = "Chris Karageorgiou Kaneen"
9+
10+
from confapp import conf as settings
11+
12+
#################
13+
# from settings #
14+
#################
15+
16+
FIRMWARE_VERSION = int(settings.TARGET_BPOD_FIRMWARE_VERSION)
17+
MACHINE_TYPE = int(settings.TARGET_BPOD_MACHINE_TYPE)
18+
19+
20+
class MACHINE_TYPE_ENUM(object):
21+
ONE = 1
22+
TWO = 2
23+
THREE = 3
24+
25+
26+
class SM_FEATURE_PROFILE_ENUM(object):
27+
ZERO = 0
28+
ONE = 1
29+
TWO = 2
30+
THREE = 3
31+
32+
33+
class ETHERNET_COM_ENUM(object):
34+
ON = 1
35+
OFF = 0
36+
37+
38+
ETHERNET_COM = ETHERNET_COM_ENUM.OFF
39+
LIVE_TIMESTAMPS = 0
40+
TIMER_PERIOD = 100
41+
SM_FEATURE_PROFILE = {
42+
MACHINE_TYPE_ENUM.ONE: SM_FEATURE_PROFILE_ENUM.ZERO,
43+
MACHINE_TYPE_ENUM.TWO: SM_FEATURE_PROFILE_ENUM.ZERO,
44+
MACHINE_TYPE_ENUM.THREE: SM_FEATURE_PROFILE_ENUM.ONE,
45+
}
46+
INPUT_HW = {
47+
MACHINE_TYPE_ENUM.ONE: 'UUXBBWWWWPPPPPPPP',
48+
MACHINE_TYPE_ENUM.TWO: 'UUUXBBWWPPPPPPPP',
49+
MACHINE_TYPE_ENUM.THREE: {
50+
ETHERNET_COM_ENUM.OFF: 'UUUUUXBBPPPP',
51+
ETHERNET_COM_ENUM.ON: 'UUUUXBBPPPP',
52+
},
53+
}
54+
OUTPUT_HW = {
55+
MACHINE_TYPE_ENUM.ONE: 'UUXBBWWWWPPPPPPPPVVVVVVVV',
56+
MACHINE_TYPE_ENUM.TWO: 'UUUXBBWWWPPPPPPPPVVVVVVVV',
57+
MACHINE_TYPE_ENUM.THREE: {
58+
ETHERNET_COM_ENUM.OFF: 'UUUUUXBBPPPPVVVV',
59+
ETHERNET_COM_ENUM.ON: 'UUUUXBBPPPPVVVV',
60+
},
61+
}
62+
HARDWARE_DESCRIPTION = {
63+
'MAX_STATES': {
64+
MACHINE_TYPE_ENUM.ONE: 128,
65+
MACHINE_TYPE_ENUM.TWO: 256,
66+
MACHINE_TYPE_ENUM.THREE: 256,
67+
},
68+
'TIMER_PERIOD': TIMER_PERIOD,
69+
'MAX_SERIAL_EVENTS': {
70+
MACHINE_TYPE_ENUM.ONE: 30,
71+
MACHINE_TYPE_ENUM.TWO: 60,
72+
MACHINE_TYPE_ENUM.THREE: {
73+
ETHERNET_COM_ENUM.OFF: 90,
74+
ETHERNET_COM_ENUM.ON: 75,
75+
},
76+
},
77+
'MAX_GLOBAL_TIMERS': {
78+
SM_FEATURE_PROFILE_ENUM.ZERO: 5,
79+
SM_FEATURE_PROFILE_ENUM.ONE: 16,
80+
SM_FEATURE_PROFILE_ENUM.TWO: 8,
81+
SM_FEATURE_PROFILE_ENUM.THREE: 20,
82+
},
83+
'MAX_GLOBAL_COUNTERS': {
84+
SM_FEATURE_PROFILE_ENUM.ZERO: 5,
85+
SM_FEATURE_PROFILE_ENUM.ONE: 8,
86+
SM_FEATURE_PROFILE_ENUM.TWO: 2,
87+
SM_FEATURE_PROFILE_ENUM.THREE: 2,
88+
},
89+
'MAX_CONDITIONS': {
90+
SM_FEATURE_PROFILE_ENUM.ZERO: 5,
91+
SM_FEATURE_PROFILE_ENUM.ONE: 16,
92+
SM_FEATURE_PROFILE_ENUM.TWO: 8,
93+
SM_FEATURE_PROFILE_ENUM.THREE: 20,
94+
},
95+
'INPUT_HW': INPUT_HW,
96+
'OUTPUT_HW': OUTPUT_HW,
97+
}

0 commit comments

Comments
 (0)