diff --git a/pybehave/Components/DigitalWaveformStim.py b/pybehave/Components/DigitalWaveformStim.py new file mode 100644 index 0000000..f9e5fc1 --- /dev/null +++ b/pybehave/Components/DigitalWaveformStim.py @@ -0,0 +1,9 @@ +from pybehave.Components.Component import Component +from pybehave.Components.WaveformStim import WaveformStim + + +class DigitalWaveformStim(WaveformStim): + + @staticmethod + def get_type() -> Component.Type: + return Component.Type.DIGITAL_OUTPUT diff --git a/pybehave/Events/OENetworkLogger.py b/pybehave/Events/OENetworkLogger.py index c132e93..bf9d2e5 100644 --- a/pybehave/Events/OENetworkLogger.py +++ b/pybehave/Events/OENetworkLogger.py @@ -73,7 +73,7 @@ def receive(self) -> None: def log_events(self, events: collections.deque[LoggerEvent]) -> None: for event in events: self.event_count += 1 - if isinstance(event.event, self.OEEvent): + if isinstance(event.event, OEEvent): if event.event.event_type == 'startAcquisition': self.send_string("startAcquisition") elif event.event.event_type == 'stopAcquisition': diff --git a/pybehave/Events/OEWidget.py b/pybehave/Events/OEWidget.py index f01c751..d1f083f 100644 --- a/pybehave/Events/OEWidget.py +++ b/pybehave/Events/OEWidget.py @@ -46,6 +46,8 @@ def __init__(self, name: str, address: str, port: str): oe_text_layout = QHBoxLayout(self.oe_group) oe_control_layout = QVBoxLayout(self.oe_group) + icons_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'Workstation/icons')) + # Widget corresponding to the path for the output folder for OpenEphys rec_dir_box = QGroupBox('RecDir') rec_dir_layout = QHBoxLayout(self.oe_group) @@ -54,7 +56,7 @@ def __init__(self, name: str, address: str, port: str): self.rec_dir.setReadOnly(True) rec_dir_layout.addWidget(self.rec_dir) self.rec_dir_browse = QPushButton() - self.rec_dir_browse.setIcon(QIcon('Workstation/icons/folder.svg')) + self.rec_dir_browse.setIcon(QIcon(os.path.join(icons_dir, 'folder.svg'))) self.rec_dir_browse.setFixedWidth(30) self.rec_dir_browse.clicked.connect(lambda: self.get_file_path()) rec_dir_layout.addWidget(self.rec_dir_browse) @@ -76,11 +78,11 @@ def __init__(self, name: str, address: str, port: str): oe_rec_layout.addLayout(oe_text_layout) oe_group_layout.addLayout(oe_rec_layout) - self.acq_button = IconButton('Workstation/icons/play.svg', 'Workstation/icons/play_hover.svg') + self.acq_button = IconButton(os.path.join(icons_dir, 'play.svg'), os.path.join(icons_dir, 'play_hover.svg')) self.acq_button.setFixedWidth(30) self.acq_button.clicked.connect(self.acquisition) oe_control_layout.addWidget(self.acq_button) - self.rec_button = IconButton('Workstation/icons/record.svg', 'Workstation/icons/record_hover.svg') + self.rec_button = IconButton(os.path.join(icons_dir, 'record.svg'), os.path.join(icons_dir, 'record_hover.svg')) self.rec_button.setFixedWidth(30) self.rec_button.clicked.connect(self.record) oe_control_layout.addWidget(self.rec_button) @@ -114,20 +116,21 @@ def set_chamber(self, cw: ChamberWidget) -> None: "%m-%d-%Y"))) def acquisition(self) -> None: + icons_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'Workstation/icons')) if not self.acq: - self.acq_button.icon = 'Workstation/icons/stop_play.svg' - self.acq_button.hover_icon = 'Workstation/icons/stop_play_hover.svg' + self.acq_button.icon = os.path.join(icons_dir, 'stop_play.svg') + self.acq_button.hover_icon = os.path.join(icons_dir, 'stop_play_hover.svg') self.acq_button.setIcon(QIcon(self.acq_button.icon)) self.send_string("startAcquisition") # self.queue.put_nowait(PybEvents.OEEvent(self.cw.task, "startAcquisition").format()) self.acq = True elif self.acq: - self.acq_button.icon = 'Workstation/icons/play.svg' - self.acq_button.hover_icon = 'Workstation/icons/play_hover.svg' + self.acq_button.icon = os.path.join(icons_dir, 'play.svg') + self.acq_button.hover_icon = os.path.join(icons_dir, 'play_hover.svg') self.acq_button.setIcon(QIcon(self.acq_button.icon)) if self.rec: - self.rec_button.icon = 'Workstation/icons/record.svg' - self.rec_button.hover_icon = 'Workstation/icons/record_hover.svg' + self.rec_button.icon = os.path.join(icons_dir, 'record.svg') + self.rec_button.hover_icon = os.path.join(icons_dir, 'record_hover.svg') self.rec_button.setIcon(QIcon(self.rec_button.icon)) self.rec = False self.send_string("stopAcquisition") @@ -135,12 +138,13 @@ def acquisition(self) -> None: self.acq = False def record(self) -> None: + icons_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'Workstation/icons')) if not self.rec: - self.acq_button.icon = 'Workstation/icons/stop_play.svg' - self.acq_button.hover_icon = 'Workstation/icons/stop_play_hover.svg' + self.acq_button.icon = os.path.join(icons_dir, 'stop_play.svg') + self.acq_button.hover_icon = os.path.join(icons_dir, 'stop_play_hover.svg') self.acq_button.setIcon(QIcon(self.acq_button.icon)) - self.rec_button.icon = 'Workstation/icons/stop_record.svg' - self.rec_button.hover_icon = 'Workstation/icons/stop_record_hover.svg' + self.rec_button.icon = os.path.join(icons_dir, 'stop_record.svg') + self.rec_button.hover_icon = os.path.join(icons_dir, 'stop_record_hover.svg') self.rec_button.setIcon(QIcon(self.rec_button.icon)) self.send_string("startRecord RecDir={} prependText={} appendText={}".format(self.rec_dir.text(), self.pre.text(), @@ -149,8 +153,8 @@ def record(self) -> None: self.acq = True self.rec = True elif self.rec: - self.rec_button.icon = 'Workstation/icons/record.svg' - self.rec_button.hover_icon = 'Workstation/icons/record_hover.svg' + self.rec_button.icon = os.path.join(icons_dir, 'record.svg') + self.rec_button.hover_icon = os.path.join(icons_dir, 'record_hover.svg') self.rec_button.setIcon(QIcon(self.rec_button.icon)) self.send_string("stopRecord") # self.queue.put_nowait(PybEvents.OEEvent(self.cw.task, "stopRecord").format()) diff --git a/pybehave/Sources/NIDAQSource.py b/pybehave/Sources/NIDAQSource.py index adadbcc..e46bfeb 100644 --- a/pybehave/Sources/NIDAQSource.py +++ b/pybehave/Sources/NIDAQSource.py @@ -51,7 +51,10 @@ def __init__(self, dev): self.streams = {} self.ao_task = None self.ao_stream = None + self.do_task = None + self.do_stream = None self.ao_inds = {} + self.do_inds = {} def initialize(self): dev_obj = system.Device(self.dev) @@ -59,11 +62,19 @@ def initialize(self): def register_component(self, component, metadata): if component.get_type() == Component.Type.DIGITAL_OUTPUT: - task = nidaqmx.Task() - task.do_channels.add_do_chan(self.dev + component.address, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) - task.start() - self.tasks[component.id] = task + if component.sr is None: + task = nidaqmx.Task() + task.do_channels.add_do_chan(self.dev + component.address, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES) + task.start() + self.tasks[component.id] = task + else: + if self.do_task is None: + self.do_task = nidaqmx.Task() + self.do_task.auto_start = True + self.do_task.do_channels.add_do_chan(self.dev + component.address) + self.do_stream = stream_writers.DigitalMultiChannelWriter(self.do_task.out_stream, auto_start=True) + self.do_inds[component.id] = len(self.do_inds) elif component.get_type() == Component.Type.ANALOG_OUTPUT: if self.ao_task is None: self.ao_task = nidaqmx.Task() @@ -76,6 +87,10 @@ def close_source(self): self.ao_task.close() self.ao_task = None self.ao_stream = None + if self.do_task is not None: + self.do_task.close() + self.do_task = None + self.do_stream = None for task in self.tasks: task.close() self.tasks = {} @@ -88,12 +103,26 @@ def close_component(self, component_id): self.ao_task = None self.ao_stream = None else: + if self.components[component_id].sr is not None: + if self.do_task is not None: + self.do_task.stop() + self.do_task.close() + self.do_task = None + self.do_stream = None self.tasks[component_id].stop() self.tasks[component_id].close() def write_component(self, component_id, msg): if self.components[component_id].get_type() == Component.Type.DIGITAL_OUTPUT: - self.tasks[component_id].write(msg) + if self.components[component_id].sr is not None: + output = np.zeros((len(self.do_inds), msg.shape[1])) + output[self.do_inds[component_id], :] = np.squeeze(msg) + # self.do_task.timing.cfg_samp_clk_timing(self.components[component_id].sr, + # sample_mode=nidaqmx.constants.AcquisitionType.FINITE, + # samps_per_chan=msg.shape[1]) + self.do_stream.write_many_sample_port_uint32(np.uint32(output)) + else: + self.tasks[component_id].write(msg) elif self.components[component_id].get_type() == Component.Type.ANALOG_OUTPUT: output = np.zeros((len(self.ao_inds), msg.shape[1])) output[self.ao_inds[component_id], :] = np.squeeze(msg) @@ -109,5 +138,7 @@ def write_component(self, component_id, msg): def metadata_defaults(comp_type: Component.Type = None) -> Dict: if comp_type == Component.Type.ANALOG_OUTPUT: return {"sr": 30000} + elif comp_type == Component.Type.DIGITAL_OUTPUT: + return {"sr": None} else: return {}