From 2e246b5e5f5abfc9e5c8a495fd03eed4c45e0f39 Mon Sep 17 00:00:00 2001 From: bparks13 Date: Fri, 20 Jun 2025 15:42:25 -0400 Subject: [PATCH 01/10] UI pattern updates --- Source/NeuropixelsComponents.h | 9 --------- Source/OnixSourceCanvas.cpp | 1 + Source/UI/AnalogIOInterface.cpp | 2 ++ Source/UI/AnalogIOInterface.h | 8 ++++---- Source/UI/Bno055Interface.cpp | 2 ++ Source/UI/Bno055Interface.h | 8 ++++---- Source/UI/DigitalIOInterface.cpp | 2 ++ Source/UI/DigitalIOInterface.h | 8 ++++---- Source/UI/HarpSyncInputInterface.cpp | 2 ++ Source/UI/HarpSyncInputInterface.h | 8 ++++---- Source/UI/InterfaceList.h | 10 +++++----- Source/UI/NeuropixelsV1Interface.cpp | 27 ++++++++++++++------------- Source/UI/NeuropixelsV1Interface.h | 6 +++--- Source/UI/NeuropixelsV2eInterface.cpp | 1 - Source/UI/OutputClockInterface.cpp | 2 ++ Source/UI/OutputClockInterface.h | 8 ++++---- Source/UI/PolledBno055Interface.cpp | 2 ++ Source/UI/PolledBno055Interface.h | 8 ++++---- Source/UI/ProbeBrowser.h | 11 +++++------ Source/UI/SettingsInterface.h | 11 +++++++++-- 20 files changed, 73 insertions(+), 63 deletions(-) diff --git a/Source/NeuropixelsComponents.h b/Source/NeuropixelsComponents.h index 8601580..b2f950b 100644 --- a/Source/NeuropixelsComponents.h +++ b/Source/NeuropixelsComponents.h @@ -33,15 +33,6 @@ namespace OnixSourcePlugin { - enum class VisualizationMode - { - ENABLE_VIEW, - AP_GAIN_VIEW, - LFP_GAIN_VIEW, - REFERENCE_VIEW, - ACTIVITY_VIEW - }; - enum class ProbeType { NONE = 1, diff --git a/Source/OnixSourceCanvas.cpp b/Source/OnixSourceCanvas.cpp index 9cddf1a..f14f004 100644 --- a/Source/OnixSourceCanvas.cpp +++ b/Source/OnixSourceCanvas.cpp @@ -23,6 +23,7 @@ #include "OnixSourceCanvas.h" #include "OnixSource.h" #include "OnixSourceEditor.h" +#include "UI/SettingsInterface.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/AnalogIOInterface.cpp b/Source/UI/AnalogIOInterface.cpp index bfd5805..5585594 100644 --- a/Source/UI/AnalogIOInterface.cpp +++ b/Source/UI/AnalogIOInterface.cpp @@ -21,6 +21,8 @@ */ #include "AnalogIOInterface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/AnalogIOInterface.h b/Source/UI/AnalogIOInterface.h index fda2113..85c6d6f 100644 --- a/Source/UI/AnalogIOInterface.h +++ b/Source/UI/AnalogIOInterface.h @@ -23,14 +23,14 @@ #pragma once #include - -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - +#include "SettingsInterface.h" #include "../Devices/AnalogIO.h" namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + class AnalogIOInterface : public SettingsInterface, public Button::Listener, public ComboBox::Listener diff --git a/Source/UI/Bno055Interface.cpp b/Source/UI/Bno055Interface.cpp index 52f31c9..e03aa06 100644 --- a/Source/UI/Bno055Interface.cpp +++ b/Source/UI/Bno055Interface.cpp @@ -21,6 +21,8 @@ */ #include "Bno055Interface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/Bno055Interface.h b/Source/UI/Bno055Interface.h index 1e0e82e..fa4dca1 100644 --- a/Source/UI/Bno055Interface.h +++ b/Source/UI/Bno055Interface.h @@ -23,14 +23,14 @@ #pragma once #include - -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - +#include "SettingsInterface.h" #include "../Devices/Bno055.h" namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + class Bno055Interface : public SettingsInterface, public Button::Listener { diff --git a/Source/UI/DigitalIOInterface.cpp b/Source/UI/DigitalIOInterface.cpp index fca29d4..6504ec0 100644 --- a/Source/UI/DigitalIOInterface.cpp +++ b/Source/UI/DigitalIOInterface.cpp @@ -21,6 +21,8 @@ */ #include "DigitalIOInterface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/DigitalIOInterface.h b/Source/UI/DigitalIOInterface.h index 6f13a3f..cf90de4 100644 --- a/Source/UI/DigitalIOInterface.h +++ b/Source/UI/DigitalIOInterface.h @@ -23,14 +23,14 @@ #pragma once #include - -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - +#include "SettingsInterface.h" #include "../Devices/DigitalIO.h" namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + class DigitalIOInterface : public SettingsInterface, public Button::Listener { diff --git a/Source/UI/HarpSyncInputInterface.cpp b/Source/UI/HarpSyncInputInterface.cpp index a53ee97..1f74406 100644 --- a/Source/UI/HarpSyncInputInterface.cpp +++ b/Source/UI/HarpSyncInputInterface.cpp @@ -21,6 +21,8 @@ */ #include "HarpSyncInputInterface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/HarpSyncInputInterface.h b/Source/UI/HarpSyncInputInterface.h index 888e653..8e9527e 100644 --- a/Source/UI/HarpSyncInputInterface.h +++ b/Source/UI/HarpSyncInputInterface.h @@ -23,14 +23,14 @@ #pragma once #include - -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - +#include "SettingsInterface.h" #include "../Devices/HarpSyncInput.h" namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + class HarpSyncInputInterface : public SettingsInterface, public Button::Listener { diff --git a/Source/UI/InterfaceList.h b/Source/UI/InterfaceList.h index ff7c625..6d0d3cc 100644 --- a/Source/UI/InterfaceList.h +++ b/Source/UI/InterfaceList.h @@ -20,12 +20,12 @@ */ +#include "AnalogIOInterface.h" +#include "Bno055Interface.h" #include "CustomViewport.h" +#include "DigitalIOInterface.h" +#include "HarpSyncInputInterface.h" #include "NeuropixelsV1Interface.h" #include "NeuropixelsV2eInterface.h" -#include "Bno055Interface.h" -#include "PolledBno055Interface.h" #include "OutputClockInterface.h" -#include "HarpSyncInputInterface.h" -#include "AnalogIOInterface.h" -#include "DigitalIOInterface.h" +#include "PolledBno055Interface.h" diff --git a/Source/UI/NeuropixelsV1Interface.cpp b/Source/UI/NeuropixelsV1Interface.cpp index ab34889..a2d55df 100644 --- a/Source/UI/NeuropixelsV1Interface.cpp +++ b/Source/UI/NeuropixelsV1Interface.cpp @@ -21,7 +21,8 @@ */ #include "NeuropixelsV1Interface.h" - +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" #include "../Formats/ProbeInterface.h" using namespace OnixSourcePlugin; @@ -43,7 +44,7 @@ NeuropixelsV1Interface::NeuropixelsV1Interface(std::shared_ptr d, { type = device->getDeviceType() == OnixDeviceType::NEUROPIXELSV1E ? SettingsInterface::Type::NEUROPIXELS1E_SETTINGS_INTERFACE : SettingsInterface::Type::NEUROPIXELS1F_SETTINGS_INTERFACE; - mode = VisualizationMode::ENABLE_VIEW; + mode = SettingsInterface::VisualizationMode::ENABLE_VIEW; probeBrowser = std::make_unique(this, 0); probeBrowser->setBounds(0, 0, 600, 600); @@ -708,35 +709,35 @@ void NeuropixelsV1Interface::buttonClicked(Button* button) } else if (button == enableViewButton.get()) { - mode = VisualizationMode::ENABLE_VIEW; + mode = SettingsInterface::VisualizationMode::ENABLE_VIEW; probeBrowser->stopTimer(); drawLegend(); repaint(); } else if (button == apGainViewButton.get()) { - mode = VisualizationMode::AP_GAIN_VIEW; + mode = SettingsInterface::VisualizationMode::AP_GAIN_VIEW; probeBrowser->stopTimer(); drawLegend(); repaint(); } else if (button == lfpGainViewButton.get()) { - mode = VisualizationMode::LFP_GAIN_VIEW; + mode = SettingsInterface::VisualizationMode::LFP_GAIN_VIEW; probeBrowser->stopTimer(); drawLegend(); repaint(); } else if (button == referenceViewButton.get()) { - mode = VisualizationMode::REFERENCE_VIEW; + mode = SettingsInterface::VisualizationMode::REFERENCE_VIEW; probeBrowser->stopTimer(); drawLegend(); repaint(); } else if (button == activityViewButton.get()) { - mode = VisualizationMode::ACTIVITY_VIEW; + mode = SettingsInterface::VisualizationMode::ACTIVITY_VIEW; if (acquisitionIsActive) probeBrowser->startTimer(100); @@ -889,7 +890,7 @@ void NeuropixelsV1Interface::startAcquisition() setInterfaceEnabledState(false); - if (mode == VisualizationMode::ACTIVITY_VIEW) + if (mode == SettingsInterface::VisualizationMode::ACTIVITY_VIEW) probeBrowser->startTimer(100); } @@ -910,19 +911,19 @@ void NeuropixelsV1Interface::drawLegend() switch (mode) { - case VisualizationMode::ENABLE_VIEW: + case SettingsInterface::VisualizationMode::ENABLE_VIEW: enableViewComponent->setVisible(true); break; - case VisualizationMode::AP_GAIN_VIEW: + case SettingsInterface::VisualizationMode::AP_GAIN_VIEW: apGainViewComponent->setVisible(true); break; - case VisualizationMode::LFP_GAIN_VIEW: + case SettingsInterface::VisualizationMode::LFP_GAIN_VIEW: lfpGainViewComponent->setVisible(true); break; - case VisualizationMode::REFERENCE_VIEW: + case SettingsInterface::VisualizationMode::REFERENCE_VIEW: referenceViewComponent->setVisible(true); break; - case VisualizationMode::ACTIVITY_VIEW: + case SettingsInterface::VisualizationMode::ACTIVITY_VIEW: activityViewComponent->setVisible(true); break; default: diff --git a/Source/UI/NeuropixelsV1Interface.h b/Source/UI/NeuropixelsV1Interface.h index 99d7377..9ad50d7 100644 --- a/Source/UI/NeuropixelsV1Interface.h +++ b/Source/UI/NeuropixelsV1Interface.h @@ -29,11 +29,11 @@ #include "NeuropixelsV1ProbeBrowser.h" #include "../Devices/Neuropixels1.h" -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + /** Extended graphical interface for updating probe settings diff --git a/Source/UI/NeuropixelsV2eInterface.cpp b/Source/UI/NeuropixelsV2eInterface.cpp index 60f9442..5a5214c 100644 --- a/Source/UI/NeuropixelsV2eInterface.cpp +++ b/Source/UI/NeuropixelsV2eInterface.cpp @@ -21,7 +21,6 @@ */ #include "NeuropixelsV2eInterface.h" - #include "../OnixSourceEditor.h" #include "../OnixSourceCanvas.h" diff --git a/Source/UI/OutputClockInterface.cpp b/Source/UI/OutputClockInterface.cpp index f314522..9326af5 100644 --- a/Source/UI/OutputClockInterface.cpp +++ b/Source/UI/OutputClockInterface.cpp @@ -21,6 +21,8 @@ */ #include "OutputClockInterface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/OutputClockInterface.h b/Source/UI/OutputClockInterface.h index 69ebf66..ae8291d 100644 --- a/Source/UI/OutputClockInterface.h +++ b/Source/UI/OutputClockInterface.h @@ -23,14 +23,14 @@ #pragma once #include - -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - +#include "SettingsInterface.h" #include "../Devices/OutputClock.h" namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + class OutputClockInterface : public SettingsInterface, public Label::Listener, public Button::Listener diff --git a/Source/UI/PolledBno055Interface.cpp b/Source/UI/PolledBno055Interface.cpp index 4941c05..36719a1 100644 --- a/Source/UI/PolledBno055Interface.cpp +++ b/Source/UI/PolledBno055Interface.cpp @@ -21,6 +21,8 @@ */ #include "PolledBno055Interface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" using namespace OnixSourcePlugin; diff --git a/Source/UI/PolledBno055Interface.h b/Source/UI/PolledBno055Interface.h index 8b224b0..e225070 100644 --- a/Source/UI/PolledBno055Interface.h +++ b/Source/UI/PolledBno055Interface.h @@ -23,14 +23,14 @@ #pragma once #include - -#include "../OnixSourceEditor.h" -#include "../OnixSourceCanvas.h" - +#include "SettingsInterface.h" #include "../Devices/PolledBno055.h" namespace OnixSourcePlugin { + class OnixSourceEditor; + class OnixSourceCanvas; + class PolledBno055Interface : public SettingsInterface, public Button::Listener { diff --git a/Source/UI/ProbeBrowser.h b/Source/UI/ProbeBrowser.h index e789697..f7c0d5b 100644 --- a/Source/UI/ProbeBrowser.h +++ b/Source/UI/ProbeBrowser.h @@ -23,7 +23,6 @@ #pragma once #include - #include "SettingsInterface.h" namespace OnixSourcePlugin @@ -634,19 +633,19 @@ namespace OnixSourcePlugin return Colours::black; else { - if (mode == VisualizationMode::ENABLE_VIEW) // ENABLED STATE + if (mode == SettingsInterface::VisualizationMode::ENABLE_VIEW) // ENABLED STATE { return Colours::yellow; } - else if (mode == VisualizationMode::AP_GAIN_VIEW) // AP GAIN + else if (mode == SettingsInterface::VisualizationMode::AP_GAIN_VIEW) // AP GAIN { return Colour(25 * settings->apGainIndex, 25 * settings->apGainIndex, 50); } - else if (mode == VisualizationMode::LFP_GAIN_VIEW) // LFP GAIN + else if (mode == SettingsInterface::VisualizationMode::LFP_GAIN_VIEW) // LFP GAIN { return Colour(66, 25 * settings->lfpGainIndex, 35 * settings->lfpGainIndex); } - else if (mode == VisualizationMode::REFERENCE_VIEW) + else if (mode == SettingsInterface::VisualizationMode::REFERENCE_VIEW) { auto ref = parent->getReferenceText(); @@ -657,7 +656,7 @@ namespace OnixSourcePlugin else return Colours::black; } - else if (mode == VisualizationMode::ACTIVITY_VIEW) + else if (mode == SettingsInterface::VisualizationMode::ACTIVITY_VIEW) { if (settings->electrodeMetadata[i].status == ElectrodeStatus::CONNECTED) { diff --git a/Source/UI/SettingsInterface.h b/Source/UI/SettingsInterface.h index 8b3d80e..da0ddb9 100644 --- a/Source/UI/SettingsInterface.h +++ b/Source/UI/SettingsInterface.h @@ -24,7 +24,6 @@ #include -#include "../NeuropixelsComponents.h" #include "../OnixDevice.h" namespace OnixSourcePlugin @@ -58,6 +57,15 @@ namespace OnixSourcePlugin UNKNOWN_SETTINGS_INTERFACE }; + enum class VisualizationMode + { + ENABLE_VIEW, + AP_GAIN_VIEW, + LFP_GAIN_VIEW, + REFERENCE_VIEW, + ACTIVITY_VIEW + }; + /** Constructor */ SettingsInterface(std::shared_ptr device_, OnixSourceEditor* editor_, OnixSourceCanvas* canvas_) { @@ -114,6 +122,5 @@ namespace OnixSourcePlugin /** Enables or disables all UI elements that should not be changed during acquisition */ virtual void setInterfaceEnabledState(bool newState) = 0; - }; } From 4884d17e1925e923183c6efce65ccbff382eaaef Mon Sep 17 00:00:00 2001 From: bparks13 Date: Tue, 24 Jun 2025 12:22:15 -0400 Subject: [PATCH 02/10] Add CompositeDevice class --- Source/FrameReader.cpp | 2 +- Source/OnixDevice.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++ Source/OnixDevice.h | 65 +++++++++++++++++++++++-------- 3 files changed, 138 insertions(+), 17 deletions(-) diff --git a/Source/FrameReader.cpp b/Source/FrameReader.cpp index 16500fd..e66faba 100644 --- a/Source/FrameReader.cpp +++ b/Source/FrameReader.cpp @@ -50,7 +50,7 @@ void FrameReader::run() for (const auto& source : sources) { - if (frame->dev_idx == source->getDeviceIdx(true)) + if (source->compareIndex(frame->dev_idx)) { source->addFrame(frame); destroyFrame = false; diff --git a/Source/OnixDevice.cpp b/Source/OnixDevice.cpp index 867bef6..b59177a 100644 --- a/Source/OnixDevice.cpp +++ b/Source/OnixDevice.cpp @@ -215,3 +215,91 @@ Array OnixDevice::getUniquePortsFromIndices(std::vector indices) return ports; } + +bool OnixDevice::compareIndex(uint32_t index) +{ + return index == deviceIdx; +} + +CompositeDevice::CompositeDevice(std::string name_, std::string hubName, CompositeDeviceType type_, OnixDeviceVector devices_, std::shared_ptr oni_ctx) + : OnixDevice(name_, hubName, OnixDeviceType::COMPOSITE, devices_.at(0)->getDeviceIdx(), oni_ctx) +{ + compositeType = type_; + devices = devices_; +} + +CompositeDeviceType CompositeDevice::getCompositeDeviceType() const +{ + return compositeType; +} + +bool CompositeDevice::compareIndex(uint32_t index) +{ + for (const auto& device : devices) + { + if (device->getDeviceIdx() == index) + return true; + } + + return false; +} + +int CompositeDevice::configureDevice() +{ + int result = ONI_ESUCCESS; + + for (const auto& device : devices) + { + result |= device->configureDevice(); + } + + return result; +} + +bool CompositeDevice::updateSettings() +{ + bool result = true; + + for (const auto& device : devices) + { + result &= device->updateSettings(); + } + + return result; +} + +void CompositeDevice::startAcquisition() +{ + for (const auto& device : devices) + { + device->startAcquisition(); + } +} + +void CompositeDevice::stopAcquisition() +{ + for (const auto& device : devices) + { + device->stopAcquisition(); + } +} + +void CompositeDevice::addSourceBuffers(OwnedArray& sourceBuffers) +{ + for (const auto& device : devices) + { + device->addSourceBuffers(sourceBuffers); + } +} + +void CompositeDevice::addFrame(oni_frame_t* frame) +{ + for (const auto& device : devices) + { + if (device->compareIndex(frame->dev_idx)) + { + device->addFrame(frame); + return; + } + } +} diff --git a/Source/OnixDevice.h b/Source/OnixDevice.h index e6f5238..07a1da0 100644 --- a/Source/OnixDevice.h +++ b/Source/OnixDevice.h @@ -55,6 +55,7 @@ namespace OnixSourcePlugin HARPSYNCINPUT, ANALOGIO, DIGITALIO, + COMPOSITE, }; struct StreamInfo { @@ -157,29 +158,19 @@ namespace OnixSourcePlugin /** Constructor */ OnixDevice(std::string name_, std::string hubName, OnixDeviceType type_, const oni_dev_idx_t, std::shared_ptr oni_ctx, bool passthrough = false); - /** Destructor */ - ~OnixDevice() { } - virtual void addFrame(oni_frame_t*) {}; - virtual void processFrames() {}; - - const std::string getName() { return name; } - - bool isEnabled() const { return enabled; } - - void setEnabled(bool newState) { enabled = newState; } - virtual int configureDevice() { return -1; }; - virtual bool updateSettings() { return false; }; - virtual void startAcquisition() {}; - virtual void stopAcquisition() {}; - - /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */ virtual void addSourceBuffers(OwnedArray& sourceBuffers) {}; + virtual bool compareIndex(uint32_t index); + + const std::string getName() { return name; } + + bool isEnabled() const { return enabled; } + void setEnabled(bool newState) { enabled = newState; } oni_dev_idx_t getDeviceIdx(bool getPassthroughIndex = false); @@ -241,4 +232,46 @@ namespace OnixSourcePlugin }; using OnixDeviceVector = std::vector>; + + /* + Abstract device that contains two or more devices + */ + class CompositeDevice : public OnixDevice + { + public: + + CompositeDevice(std::string name_, std::string hubName, CompositeDeviceType type_, OnixDeviceVector devices_, std::shared_ptr oni_ctx); + + CompositeDeviceType getCompositeDeviceType() const; + + bool compareIndex(uint32_t index) override; + int configureDevice() override; + bool updateSettings() override; + void startAcquisition() override; + void stopAcquisition() override; + void addSourceBuffers(OwnedArray& sourceBuffers) override; + void addFrame(oni_frame_t*) override; + + template + std::shared_ptr getDevice(OnixDeviceType deviceType) + { + for (const auto& device : devices) + { + if (device->getDeviceType() == deviceType) + return std::static_pointer_cast(device); + } + + return nullptr; + } + + protected: + + OnixDeviceVector devices; + + CompositeDeviceType compositeType; + + private: + + JUCE_LEAK_DETECTOR(CompositeDevice); + }; } From 551aff9f9592e69f5b0512df4e089b80b537db21 Mon Sep 17 00:00:00 2001 From: bparks13 Date: Mon, 23 Jun 2025 16:01:18 -0400 Subject: [PATCH 03/10] Add AuxiliaryIO device to manage AnalogIO and DigitalIO - Introduced AuxiliaryIO class to encapsulate both AnalogIO and DigitalIO devices. - Modified DigitalIO to configure sample period based on AnalogIO's sample rate. - Implemented AuxiliaryIOInterface for UI management of both devices. - Refactored OnixSource to initialize AuxiliaryIO instead of individual AnalogIO and DigitalIO. - Adjusted UI components for proper layout and interaction with the new AuxiliaryIO. - Cleaned up unused code and improved overall structure for better maintainability. --- Source/Devices/AnalogIO.cpp | 163 ++++++++++++++++++++++------- Source/Devices/AnalogIO.h | 102 ++++-------------- Source/Devices/AuxiliaryIO.cpp | 108 +++++++++++++++++++ Source/Devices/AuxiliaryIO.h | 57 ++++++++++ Source/Devices/DeviceList.h | 13 +-- Source/Devices/DigitalIO.cpp | 28 ++++- Source/Devices/DigitalIO.h | 29 ++--- Source/Devices/MemoryMonitor.cpp | 4 - Source/Devices/MemoryMonitor.h | 8 -- Source/OnixDevice.h | 4 + Source/OnixSource.cpp | 81 ++++++++------ Source/OnixSourceCanvas.cpp | 58 ++++++---- Source/OnixSourceEditor.cpp | 1 + Source/OnixSourceEditor.h | 4 +- Source/UI/AnalogIOInterface.cpp | 2 +- Source/UI/AnalogIOInterface.h | 7 +- Source/UI/AuxiliaryIOInterface.cpp | 91 ++++++++++++++++ Source/UI/AuxiliaryIOInterface.h | 65 ++++++++++++ Source/UI/DigitalIOInterface.cpp | 2 +- Source/UI/DigitalIOInterface.h | 7 +- Source/UI/InterfaceList.h | 1 + Source/UI/SettingsInterface.h | 15 +-- 22 files changed, 616 insertions(+), 234 deletions(-) create mode 100644 Source/Devices/AuxiliaryIO.cpp create mode 100644 Source/Devices/AuxiliaryIO.h create mode 100644 Source/UI/AuxiliaryIOInterface.cpp create mode 100644 Source/UI/AuxiliaryIOInterface.h diff --git a/Source/Devices/AnalogIO.cpp b/Source/Devices/AnalogIO.cpp index 25db21c..979e018 100644 --- a/Source/Devices/AnalogIO.cpp +++ b/Source/Devices/AnalogIO.cpp @@ -32,7 +32,7 @@ AnalogIO::AnalogIO(std::string name, std::string hubName, const oni_dev_idx_t de "Analog Input data", getStreamIdentifier(), getNumChannels(), - std::floor(AnalogIOFrequencyHz / framesToAverage), + getSampleRate(), "AnalogInput", ContinuousChannel::Type::ADC, getVoltsPerDivision(AnalogIOVoltageRange::TenVolts), // NB: +/- 10 Volts @@ -53,6 +53,11 @@ AnalogIO::AnalogIO(std::string name, std::string hubName, const oni_dev_idx_t de dataType = AnalogIODataType::Volts; } +int AnalogIO::getSampleRate() +{ + return std::floor(AnalogIOFrequencyHz / framesToAverage); +} + OnixDeviceType AnalogIO::getDeviceType() { return OnixDeviceType::ANALOGIO; @@ -109,6 +114,78 @@ float AnalogIO::getVoltsPerDivision(AnalogIOVoltageRange voltageRange) } } +AnalogIODirection AnalogIO::getChannelDirection(int channelNumber) +{ + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); + return AnalogIODirection::Input; + } + + return channelDirection[channelNumber]; +} + +std::string AnalogIO::getChannelDirection(AnalogIODirection direction) +{ + switch (direction) + { + case AnalogIODirection::Input: + return "Input"; + case AnalogIODirection::Output: + return "Output"; + default: + return ""; + } +} + +void AnalogIO::setChannelDirection(int channelNumber, AnalogIODirection direction) +{ + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); + return; + } + + channelDirection[channelNumber] = direction; +} + +AnalogIOVoltageRange AnalogIO::getChannelVoltageRange(int channelNumber) +{ + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); + return AnalogIOVoltageRange::FiveVolts; + } + + return channelVoltageRange[channelNumber]; +} + +void AnalogIO::setChannelVoltageRange(int channelNumber, AnalogIOVoltageRange direction) +{ + if (channelNumber > numChannels || channelNumber < 0) + { + LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); + return; + } + + channelVoltageRange[channelNumber] = direction; +} + +AnalogIODataType AnalogIO::getDataType() const +{ + return dataType; +} + +void AnalogIO::setDataType(AnalogIODataType type) +{ + dataType = type; +} + +int AnalogIO::getNumChannels() +{ + return numChannels; +} + void AnalogIO::startAcquisition() { currentFrame = 0; @@ -133,62 +210,74 @@ void AnalogIO::addFrame(oni_frame_t* frame) frameArray.add(frame); } +int AnalogIO::getNumberOfFrames() +{ + const GenericScopedLock frameLock(frameArray.getLock()); + return frameArray.size(); +} + void AnalogIO::addSourceBuffers(OwnedArray& sourceBuffers) { sourceBuffers.add(new DataBuffer(streamInfos.getFirst().getNumChannels(), (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); analogInputBuffer = sourceBuffers.getLast(); } -void AnalogIO::processFrames() +void AnalogIO::processFrame(uint64_t eventWord) { - while (!frameArray.isEmpty()) - { - const GenericScopedLock frameLock(frameArray.getLock()); - oni_frame_t* frame = frameArray.removeAndReturn(0); + const GenericScopedLock frameLock(frameArray.getLock()); + oni_frame_t* frame = frameArray.removeAndReturn(0); - int16_t* dataPtr = (int16_t*)frame->data; + int16_t* dataPtr = (int16_t*)frame->data; + + int dataOffset = 4; + + for (size_t i = 0; i < numChannels; i++) + { + if (dataType == AnalogIODataType::S16) + analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i); + else + analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i) * voltsPerDivision[i]; + } - int dataOffset = 4; + currentAverageFrame++; + if (currentAverageFrame >= framesToAverage) + { for (size_t i = 0; i < numChannels; i++) { - if (dataType == AnalogIODataType::S16) - analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i); - else - analogInputSamples[currentFrame + i * numFrames] += *(dataPtr + dataOffset + i) * voltsPerDivision[i]; + analogInputSamples[currentFrame + i * numFrames] /= framesToAverage; } - currentAverageFrame++; - - if (currentAverageFrame >= framesToAverage) - { - for (size_t i = 0; i < numChannels; i++) - { - analogInputSamples[currentFrame + i * numFrames] /= framesToAverage; - } + currentAverageFrame = 0; - currentAverageFrame = 0; + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); + sampleNumbers[currentFrame] = sampleNumber++; + eventCodes[currentFrame] = eventWord; - timestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); - sampleNumbers[currentFrame] = sampleNumber++; + currentFrame++; + } - currentFrame++; - } + oni_destroy_frame(frame); - oni_destroy_frame(frame); + if (currentFrame >= numFrames) + { + shouldAddToBuffer = true; + currentFrame = 0; + } - if (currentFrame >= numFrames) - { - shouldAddToBuffer = true; - currentFrame = 0; - } + if (shouldAddToBuffer) + { + shouldAddToBuffer = false; + analogInputBuffer->addToBuffer(analogInputSamples.data(), sampleNumbers, timestamps, eventCodes, numFrames); - if (shouldAddToBuffer) - { - shouldAddToBuffer = false; - analogInputBuffer->addToBuffer(analogInputSamples.data(), sampleNumbers, timestamps, eventCodes, numFrames); + analogInputSamples.fill(0); + } +} - analogInputSamples.fill(0); - } +void AnalogIO::processFrames() +{ + while (!frameArray.isEmpty()) + { + processFrame(); } } diff --git a/Source/Devices/AnalogIO.h b/Source/Devices/AnalogIO.h index 8bd5c03..6f674a7 100644 --- a/Source/Devices/AnalogIO.h +++ b/Source/Devices/AnalogIO.h @@ -71,102 +71,46 @@ namespace OnixSourcePlugin public: AnalogIO(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - /** Configures the device so that it is ready to stream with default settings */ int configureDevice() override; - - /** Update the settings of the device */ bool updateSettings() override; - - /** Starts probe data streaming */ void startAcquisition() override; - - /** Stops probe data streaming*/ void stopAcquisition() override; - - /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */ void addSourceBuffers(OwnedArray& sourceBuffers) override; - void addFrame(oni_frame_t*) override; - void processFrames() override; - AnalogIODirection getChannelDirection(int channelNumber) - { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return AnalogIODirection::Input; - } - - return channelDirection[channelNumber]; - } - - static std::string getChannelDirection(AnalogIODirection direction) - { - switch (direction) - { - case AnalogIODirection::Input: - return "Input"; - case AnalogIODirection::Output: - return "Output"; - default: - return ""; - } - } - - void setChannelDirection(int channelNumber, AnalogIODirection direction) - { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return; - } - - channelDirection[channelNumber] = direction; - } - - AnalogIOVoltageRange getChannelVoltageRange(int channelNumber) - { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return AnalogIOVoltageRange::FiveVolts; - } - - return channelVoltageRange[channelNumber]; - } - - void setChannelVoltageRange(int channelNumber, AnalogIOVoltageRange direction) - { - if (channelNumber > numChannels || channelNumber < 0) - { - LOGE("Channel number must be between 0 and " + std::to_string(channelNumber)); - return; - } - - channelVoltageRange[channelNumber] = direction; - } - - AnalogIODataType getDataType() const { return dataType; } - - void setDataType(AnalogIODataType type) { dataType = type; } - - int getNumChannels() { return numChannels; } + void processFrame(uint64_t eventWord = 0); + + AnalogIODirection getChannelDirection(int channelNumber); + static std::string getChannelDirection(AnalogIODirection direction); + void setChannelDirection(int channelNumber, AnalogIODirection direction); + + AnalogIOVoltageRange getChannelVoltageRange(int channelNumber); + void setChannelVoltageRange(int channelNumber, AnalogIOVoltageRange direction); + + AnalogIODataType getDataType() const; + void setDataType(AnalogIODataType type); + + int getNumChannels(); static OnixDeviceType getDeviceType(); + static constexpr int framesToAverage = 4; // NB: Downsampling from 100 kHz to 25 kHz + static int getSampleRate(); + + int getNumberOfFrames(); + private: DataBuffer* analogInputBuffer = nullptr; - static const int AnalogIOFrequencyHz = 100000; + static constexpr int AnalogIOFrequencyHz = 100000; - static const int numFrames = 25; - static const int framesToAverage = 4; // NB: Downsampling from 100 kHz to 25 kHz - static const int numChannels = 12; + static constexpr int numFrames = 25; + static constexpr int numChannels = 12; - static const int numberOfDivisions = 1 << 16; - const int dacMidScale = 1 << 15; + static constexpr int numberOfDivisions = 1 << 16; + static constexpr int dacMidScale = 1 << 15; std::array channelDirection; std::array channelVoltageRange; diff --git a/Source/Devices/AuxiliaryIO.cpp b/Source/Devices/AuxiliaryIO.cpp new file mode 100644 index 0000000..88771c3 --- /dev/null +++ b/Source/Devices/AuxiliaryIO.cpp @@ -0,0 +1,108 @@ +/* + ------------------------------------------------------------------ + + Copyright (C) Open Ephys + + ------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "AuxiliaryIO.h" +#include "AnalogIO.h" +#include "DigitalIO.h" + +using namespace OnixSourcePlugin; + +AuxiliaryIO::AuxiliaryIO(std::string name, std::string hubName, const oni_dev_idx_t analogIndex, const oni_dev_idx_t digitalIndex, std::shared_ptr oni_ctx) + : CompositeDevice(name, hubName, AuxiliaryIO::getCompositeDeviceType(), createAuxiliaryIODevices(hubName, analogIndex, digitalIndex, oni_ctx), oni_ctx) +{ +} + +// NB: This constructor assumes that the digitalIO device is located at one index above the analogIndex +AuxiliaryIO::AuxiliaryIO(std::string name, std::string hubName, const oni_dev_idx_t analogIndex, std::shared_ptr oni_ctx) + : AuxiliaryIO(name, hubName, analogIndex, analogIndex + 1, oni_ctx) +{ +} + +OnixDeviceVector AuxiliaryIO::createAuxiliaryIODevices(std::string hubName, const oni_dev_idx_t analogIndex, const oni_dev_idx_t digitalIndex, std::shared_ptr oni_ctx) +{ + OnixDeviceVector devices; + + devices.emplace_back(std::make_shared("AnalogIO", hubName, analogIndex, oni_ctx)); + devices.emplace_back(std::make_shared("DigitalIO", hubName, digitalIndex, oni_ctx)); + + return devices; +} + +void AuxiliaryIO::processFrames() +{ + auto analogIO = getAnalogIO(); + auto digitalIO = getDigitalIO(); + + if (!digitalIO->isEnabled() && !analogIO->isEnabled()) + { + return; + } + else if (!digitalIO->isEnabled()) + { + analogIO->processFrames(); + return; + } + else if (!analogIO->isEnabled()) + { + digitalIO->processFrames(); + while (digitalIO->hasEventWord()) + { + digitalIO->getEventWord(); + } + + return; + } + + digitalIO->processFrames(); + + while (analogIO->getNumberOfFrames() >= AnalogIO::framesToAverage && digitalIO->getNumberOfWords() >= 1) + { + auto eventWord = digitalIO->getEventWord(); + + for (int i = 0; i < AnalogIO::framesToAverage; i++) + { + analogIO->processFrame(eventWord); + } + + digitalIO->processFrames(); + } +} + +OnixDeviceType AuxiliaryIO::getDeviceType() +{ + return OnixDeviceType::COMPOSITE; +} + +CompositeDeviceType AuxiliaryIO::getCompositeDeviceType() +{ + return CompositeDeviceType::AUXILIARYIO; +} + +std::shared_ptr AuxiliaryIO::getAnalogIO() +{ + return getDevice(OnixDeviceType::ANALOGIO); +} + +std::shared_ptr AuxiliaryIO::getDigitalIO() +{ + return getDevice(OnixDeviceType::DIGITALIO); +} diff --git a/Source/Devices/AuxiliaryIO.h b/Source/Devices/AuxiliaryIO.h new file mode 100644 index 0000000..23e8595 --- /dev/null +++ b/Source/Devices/AuxiliaryIO.h @@ -0,0 +1,57 @@ +/* + ------------------------------------------------------------------ + + Copyright (C) Open Ephys + + ------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#pragma once + +#include "../OnixDevice.h" + +namespace OnixSourcePlugin +{ + class AnalogIO; + class DigitalIO; + + /* + Abstract device that configures and streams data from both an AnalogIO device and a DigitalIO device on a Breakout Board + */ + class AuxiliaryIO : public CompositeDevice + { + public: + + AuxiliaryIO(std::string name, std::string hubName, const oni_dev_idx_t analogIndex, std::shared_ptr oni_ctx); + + AuxiliaryIO(std::string name, std::string hubName, const oni_dev_idx_t analogIndex, const oni_dev_idx_t digitalIndex, std::shared_ptr oni_ctx); + + static OnixDeviceType getDeviceType(); + static CompositeDeviceType getCompositeDeviceType(); + + std::shared_ptr getAnalogIO(); + std::shared_ptr getDigitalIO(); + + void processFrames() override; + + private: + + static OnixDeviceVector createAuxiliaryIODevices(std::string hubName, const oni_dev_idx_t analogIndex, const oni_dev_idx_t digitalIndex, std::shared_ptr oni_ctx); + + JUCE_LEAK_DETECTOR(AuxiliaryIO); + }; +} diff --git a/Source/Devices/DeviceList.h b/Source/Devices/DeviceList.h index c3f9969..03d2249 100644 --- a/Source/Devices/DeviceList.h +++ b/Source/Devices/DeviceList.h @@ -20,17 +20,18 @@ */ +#include "AuxiliaryIO.h" +#include "AnalogIO.h" #include "Bno055.h" +#include "DigitalIO.h" #include "DS90UB9x.h" +#include "HarpSyncInput.h" #include "HeadStageEEPROM.h" +#include "Heartbeat.h" +#include "MemoryMonitor.h" #include "Neuropixels1f.h" #include "Neuropixels2e.h" -#include "MemoryMonitor.h" #include "OutputClock.h" -#include "Heartbeat.h" #include "PersistentHeartbeat.h" -#include "HarpSyncInput.h" -#include "AnalogIO.h" -#include "DigitalIO.h" -#include "PortController.h" #include "PolledBno055.h" +#include "PortController.h" diff --git a/Source/Devices/DigitalIO.cpp b/Source/Devices/DigitalIO.cpp index 34457fe..8117d13 100644 --- a/Source/Devices/DigitalIO.cpp +++ b/Source/Devices/DigitalIO.cpp @@ -21,6 +21,7 @@ */ #include "DigitalIO.h" +#include "AnalogIO.h" using namespace OnixSourcePlugin; @@ -39,7 +40,21 @@ int DigitalIO::configureDevice() if (deviceContext == nullptr || !deviceContext->isInitialized()) throw error_str("Device context is not initialized properly for " + getName()); - return deviceContext->writeRegister(deviceIdx, (uint32_t)DigitalIORegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0)); + int rc = deviceContext->writeRegister(deviceIdx, (uint32_t)DigitalIORegisters::ENABLE, (oni_reg_val_t)(isEnabled() ? 1 : 0)); + if (rc != ONI_ESUCCESS) + throw error_str("Failed to enable the DigitalIO device."); + + oni_reg_val_t baseFreqHz; + rc = deviceContext->readRegister(deviceIdx, (uint32_t)DigitalIORegisters::BASE_FREQ_HZ, &baseFreqHz); + if (rc != ONI_ESUCCESS) + throw error_str("Could not read the base frequency register on the DigitalIO device."); + + uint32_t periodTicks = baseFreqHz / (uint32_t)AnalogIO::getSampleRate(); + rc = deviceContext->writeRegister(deviceIdx, (uint32_t)DigitalIORegisters::SAMPLE_PERIOD, periodTicks); + if (rc != ONI_ESUCCESS) + throw error_str("Could not write the sample rate for polling to the DigitalIO device."); + + return rc; } bool DigitalIO::updateSettings() @@ -60,15 +75,14 @@ void DigitalIO::stopAcquisition() } } -EventChannel::Settings DigitalIO::getEventChannelSettings() +EventChannel::Settings DigitalIO::getEventChannelSettings(DataStream* stream) { - // NB: The stream must be assigned before adding the channel EventChannel::Settings settings{ EventChannel::Type::TTL, OnixDevice::createStreamName({getHubName(), getName(), "Events"}), "Digital inputs and breakout button states coming from a DigitalIO device", getStreamIdentifier() + ".event.digital", - nullptr, + stream, numButtons + numDigitalInputs }; @@ -81,6 +95,12 @@ void DigitalIO::addFrame(oni_frame_t* frame) frameArray.add(frame); } +int DigitalIO::getNumberOfWords() +{ + const GenericScopedLock frameLock(eventWords.getLock()); + return eventWords.size(); +} + void DigitalIO::processFrames() { while (!frameArray.isEmpty()) diff --git a/Source/Devices/DigitalIO.h b/Source/Devices/DigitalIO.h index 57849f8..920a07e 100644 --- a/Source/Devices/DigitalIO.h +++ b/Source/Devices/DigitalIO.h @@ -28,7 +28,10 @@ namespace OnixSourcePlugin { enum class DigitalIORegisters : uint32_t { - ENABLE = 0 + ENABLE = 0x0, + BASE_FREQ_HZ = 0x5, + DEAD_TICKS = 0x6, + SAMPLE_PERIOD = 0x7, }; enum class DigitalPortState : uint16_t @@ -45,6 +48,7 @@ namespace OnixSourcePlugin enum class BreakoutButtonState : uint16_t { + None = 0x0, Moon = 0x1, Triangle = 0x2, X = 0x4, @@ -60,44 +64,33 @@ namespace OnixSourcePlugin }; /* - Configures and streams data from an AnalogIO device on a Breakout Board + Configures and streams data from a DigitalIO device on a Breakout Board */ class DigitalIO : public OnixDevice { public: DigitalIO(std::string name, std::string hubName, const oni_dev_idx_t, std::shared_ptr oni_ctx); - /** Configures the device so that it is ready to stream with default settings */ int configureDevice() override; - - /** Update the settings of the device */ bool updateSettings() override; - - /** Starts probe data streaming */ void startAcquisition() override; - - /** Stops probe data streaming*/ void stopAcquisition() override; - - /** Given the sourceBuffers from OnixSource, add all streams for the current device to the array */ void addSourceBuffers(OwnedArray& sourceBuffers) override {}; - - EventChannel::Settings getEventChannelSettings(); - void addFrame(oni_frame_t*) override; - void processFrames() override; - uint64_t getEventWord(); + EventChannel::Settings getEventChannelSettings(DataStream* stream); + int getNumberOfWords(); + uint64_t getEventWord(); bool hasEventWord(); static OnixDeviceType getDeviceType(); private: - static const int numDigitalInputs = 8; - static const int numButtons = 6; + static constexpr int numDigitalInputs = 8; + static constexpr int numButtons = 6; Array frameArray; Array eventWords; diff --git a/Source/Devices/MemoryMonitor.cpp b/Source/Devices/MemoryMonitor.cpp index 2dd26ec..8d566ad 100644 --- a/Source/Devices/MemoryMonitor.cpp +++ b/Source/Devices/MemoryMonitor.cpp @@ -167,10 +167,6 @@ void MemoryMonitor::processFrames() sampleNumbers[currentFrame] = sampleNumber++; - prevWord = m_digitalIO != nullptr && m_digitalIO->hasEventWord() ? m_digitalIO->getEventWord() : prevWord; - - eventCodes[currentFrame] = prevWord; - currentFrame++; if (currentFrame >= numFrames) diff --git a/Source/Devices/MemoryMonitor.h b/Source/Devices/MemoryMonitor.h index 04281f4..ca9b7de 100644 --- a/Source/Devices/MemoryMonitor.h +++ b/Source/Devices/MemoryMonitor.h @@ -27,8 +27,6 @@ namespace OnixSourcePlugin { - class DigitalIO; - enum class MemoryMonitorRegisters : uint32_t { ENABLE = 0, @@ -66,18 +64,12 @@ namespace OnixSourcePlugin float getLastPercentUsedValue(); - void setDigitalIO(std::shared_ptr digitalIO) { m_digitalIO = digitalIO; } - static OnixDeviceType getDeviceType(); private: DataBuffer* percentUsedBuffer; - std::shared_ptr m_digitalIO; - - uint64_t prevWord = 0; - static const int numFrames = 10; Array frameArray; diff --git a/Source/OnixDevice.h b/Source/OnixDevice.h index 07a1da0..935c1d0 100644 --- a/Source/OnixDevice.h +++ b/Source/OnixDevice.h @@ -231,6 +231,10 @@ namespace OnixSourcePlugin JUCE_LEAK_DETECTOR(OnixDevice); }; + enum class CompositeDeviceType { + AUXILIARYIO = 0 + }; + using OnixDeviceVector = std::vector>; /* diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index ffc62f7..fc403dd 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -310,14 +310,8 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn return false; } - devicesFound = configureDevice(sources, canvas, "Analog IO", BREAKOUT_BOARD_NAME, AnalogIO::getDeviceType(), hubIndex + 6, context); - if (!devicesFound) - { - sources.clear(); - return false; - } - - devicesFound = configureDevice(sources, canvas, "Digital IO", BREAKOUT_BOARD_NAME, DigitalIO::getDeviceType(), hubIndex + 7, context); + // NB: Configures AnalogIO and DigitalIO + devicesFound = configureDevice(sources, canvas, "Auxiliary IO", BREAKOUT_BOARD_NAME, AuxiliaryIO::getDeviceType(), hubIndex + 6, context); if (!devicesFound) { sources.clear(); @@ -412,7 +406,7 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn hubNames.insert({ PortController::getOffsetFromIndex(polledBno->getDeviceIdx()), NEUROPIXELSV2E_HEADSTAGE_NAME }); } - else if (hsid == 0xFFFFFFFF || hsid == ONIX_HUB_HSNP1ET || hsid == ONIX_HUB_HSNP1EH) + else if (hsid == 0xFFFFFFFF || hsid == ONIX_HUB_HSNP1ET || hsid == ONIX_HUB_HSNP1EH) // TODO: Remove the 0xFFFFFFFF before publishing { auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); @@ -744,8 +738,6 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel updateSourceBuffers(); - std::shared_ptr digitalIO = std::static_pointer_cast(getDevice(OnixDeviceType::DIGITALIO, BREAKOUT_BOARD_OFFSET)); - if (devicesFound) { for (const auto& source : sources) @@ -814,29 +806,6 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel deviceInfos->add(new DeviceInfo(deviceSettings)); addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); - - if (digitalIO != nullptr && digitalIO->isEnabled()) - { - auto ttlChannelSettings = digitalIO->getEventChannelSettings(); - ttlChannelSettings.stream = dataStreams->getLast(); - eventChannels->add(new EventChannel(ttlChannelSettings)); - - std::static_pointer_cast(source)->setDigitalIO(digitalIO); - } - } - else if (source->getDeviceType() == OnixDeviceType::ANALOGIO) - { - DeviceInfo::Settings deviceSettings{ - source->getName(), - "Analog IO", - "analogio", - "0000000", - "" - }; - - deviceInfos->add(new DeviceInfo(deviceSettings)); - - addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } else if (source->getDeviceType() == OnixDeviceType::HARPSYNCINPUT) { @@ -866,6 +835,50 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } + else if (source->getDeviceType() == OnixDeviceType::HEARTBEAT || + source->getDeviceType() == OnixDeviceType::PERSISTENTHEARTBEAT || + source->getDeviceType() == OnixDeviceType::OUTPUTCLOCK) + { + continue; + } + else if (source->getDeviceType() == OnixDeviceType::COMPOSITE) + { + auto compositeDevice = std::static_pointer_cast(source); + + if (compositeDevice->getCompositeDeviceType() == CompositeDeviceType::AUXILIARYIO) + { + DeviceInfo::Settings deviceSettings{ + source->getName(), + "Auxiliary device containing analog and digital IO data", + "auxiliaryio", + "0000000", + "" + }; + + deviceInfos->add(new DeviceInfo(deviceSettings)); + + auto auxiliaryIO = std::static_pointer_cast(compositeDevice); + + addIndividualStreams(auxiliaryIO->getAnalogIO()->streamInfos, dataStreams, deviceInfos, continuousChannels); + + auto eventChannelSettings = auxiliaryIO->getDigitalIO()->getEventChannelSettings(dataStreams->getLast()); + eventChannels->add(new EventChannel(eventChannelSettings)); + } + else + { + Onix1::showWarningMessageBoxAsync( + "Unknown Composite Source", + "Found an unknown composite source (" + source->getName() + ") on hub " + source->getHubName() + + " at address " + std::to_string(source->getDeviceIdx())); + } + } + else + { + Onix1::showWarningMessageBoxAsync( + "Unknown Source", + "Found an unknown source (" + source->getName() + ") on hub " + source->getHubName() + + " at address " + std::to_string(source->getDeviceIdx())); + } } } } diff --git a/Source/OnixSourceCanvas.cpp b/Source/OnixSourceCanvas.cpp index f14f004..9062e2e 100644 --- a/Source/OnixSourceCanvas.cpp +++ b/Source/OnixSourceCanvas.cpp @@ -55,6 +55,13 @@ void OnixSourceCanvas::addHub(std::string hubName, int offset) CustomTabComponent* tab = nullptr; OnixDeviceVector devices; PortName port = PortController::getPortFromIndex(offset); + auto context = source->getContext(); + + if (context == nullptr || !context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync("Invalid Context", "Unable to find an initialized context when adding hubs to the canvas."); + return; + } if (hubName == NEUROPIXELSV1E_HEADSTAGE_NAME) { @@ -62,25 +69,24 @@ void OnixSourceCanvas::addHub(std::string hubName, int offset) const int passthroughIndex = (offset >> 8) + 7; - devices.emplace_back(std::make_shared("Probe", hubName, passthroughIndex, source->getContext())); - devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, source->getContext())); + devices.emplace_back(std::make_shared("Probe", hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, context)); } else if (hubName == NEUROPIXELSV1F_HEADSTAGE_NAME) { tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - devices.emplace_back(std::make_shared("Probe0", hubName, offset, source->getContext())); - devices.emplace_back(std::make_shared("Probe1", hubName, offset + 1, source->getContext())); - devices.emplace_back(std::make_shared("BNO055", hubName, offset + 2, source->getContext())); + devices.emplace_back(std::make_shared("Probe0", hubName, offset, context)); + devices.emplace_back(std::make_shared("Probe1", hubName, offset + 1, context)); + devices.emplace_back(std::make_shared("BNO055", hubName, offset + 2, context)); } else if (hubName == BREAKOUT_BOARD_NAME) { tab = addTopLevelTab(hubName, 0); - devices.emplace_back(std::make_shared("Output Clock", hubName, 5, source->getContext())); - devices.emplace_back(std::make_shared("Analog IO", hubName, 6, source->getContext())); - devices.emplace_back(std::make_shared("Digital IO", hubName, 7, source->getContext())); - devices.emplace_back(std::make_shared("Harp Sync Input", hubName, 12, source->getContext())); + devices.emplace_back(std::make_shared("Output Clock", hubName, 5, context)); + devices.emplace_back(std::make_shared("Auxiliary IO", hubName, 6, 7, context)); + devices.emplace_back(std::make_shared("Harp Sync Input", hubName, 12, context)); } else if (hubName == NEUROPIXELSV2E_HEADSTAGE_NAME) { @@ -88,8 +94,8 @@ void OnixSourceCanvas::addHub(std::string hubName, int offset) tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - devices.emplace_back(std::make_shared("", hubName, passthroughIndex, source->getContext())); - devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, source->getContext())); + devices.emplace_back(std::make_shared("", hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, context)); } if (tab != nullptr && devices.size() > 0) @@ -144,16 +150,6 @@ void OnixSourceCanvas::populateSourceTabs(CustomTabComponent* tab, OnixDeviceVec auto harpSyncInputInterface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, harpSyncInputInterface); } - else if (device->getDeviceType() == OnixDeviceType::ANALOGIO) - { - auto analogIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, analogIOInterface); - } - else if (device->getDeviceType() == OnixDeviceType::DIGITALIO) - { - auto digitalIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); - addInterfaceToTab(device->getName(), tab, digitalIOInterface); - } else if (device->getDeviceType() == OnixDeviceType::NEUROPIXELSV2E) { auto npxv2eInterface = std::make_shared(std::static_pointer_cast(device), editor, this); @@ -166,6 +162,26 @@ void OnixSourceCanvas::populateSourceTabs(CustomTabComponent* tab, OnixDeviceVec auto polledBnoInterface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, polledBnoInterface); } + else if (device->getDeviceType() == OnixDeviceType::COMPOSITE) + { + auto compositeDevice = std::static_pointer_cast(device); + + if (compositeDevice->getCompositeDeviceType() == CompositeDeviceType::AUXILIARYIO) + { + auto auxiliaryIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); + addInterfaceToTab(device->getName(), tab, auxiliaryIOInterface); + } + else + { + Onix1::showWarningMessageBoxAsync("Composite Device Type Not Found", "Could not find a valid composite device type when adding devices to the canvas."); + return; + } + } + else + { + Onix1::showWarningMessageBoxAsync("Device Type Not Found", "Could not find a valid device type when adding devices to the canvas."); + return; + } } } diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index 5df5f9e..7669f88 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -23,6 +23,7 @@ #include "OnixSourceEditor.h" #include "OnixSource.h" #include "OnixSourceCanvas.h" +#include "Devices/MemoryMonitor.h" using namespace OnixSourcePlugin; diff --git a/Source/OnixSourceEditor.h b/Source/OnixSourceEditor.h index 6b2874b..a80c686 100644 --- a/Source/OnixSourceEditor.h +++ b/Source/OnixSourceEditor.h @@ -23,13 +23,13 @@ #pragma once #include - -#include "Devices/MemoryMonitor.h" +#include "OnixDevice.h" namespace OnixSourcePlugin { class OnixSource; class OnixSourceCanvas; + class MemoryMonitorUsage; /** diff --git a/Source/UI/AnalogIOInterface.cpp b/Source/UI/AnalogIOInterface.cpp index 5585594..ef68a4c 100644 --- a/Source/UI/AnalogIOInterface.cpp +++ b/Source/UI/AnalogIOInterface.cpp @@ -36,7 +36,7 @@ AnalogIOInterface::AnalogIOInterface(std::shared_ptr d, OnixSourceEdit deviceEnableButton = std::make_unique(enabledButtonText); deviceEnableButton->setFont(font); deviceEnableButton->setRadius(3.0f); - deviceEnableButton->setBounds(50, 40, 100, 22); + deviceEnableButton->setBounds(40, 20, 100, 22); deviceEnableButton->setClickingTogglesState(true); deviceEnableButton->setTooltip("If disabled, AnalogIO will not stream or receive data during acquisition"); deviceEnableButton->setToggleState(true, dontSendNotification); diff --git a/Source/UI/AnalogIOInterface.h b/Source/UI/AnalogIOInterface.h index 85c6d6f..23400c3 100644 --- a/Source/UI/AnalogIOInterface.h +++ b/Source/UI/AnalogIOInterface.h @@ -39,20 +39,15 @@ namespace OnixSourcePlugin AnalogIOInterface(std::shared_ptr d, OnixSourceEditor* e, OnixSourceCanvas* c); void saveParameters(XmlElement* xml) override; - void loadParameters(XmlElement* xml) override; - void updateInfoString() override {}; - void updateSettings() override; - void buttonClicked(Button*) override; void comboBoxChanged(ComboBox* cb) override; + void setInterfaceEnabledState(bool newState) override; private: - void setInterfaceEnabledState(bool newState) override; - static const int numChannels = 12; std::array, numChannels> channelDirectionLabels; diff --git a/Source/UI/AuxiliaryIOInterface.cpp b/Source/UI/AuxiliaryIOInterface.cpp new file mode 100644 index 0000000..f6f85ce --- /dev/null +++ b/Source/UI/AuxiliaryIOInterface.cpp @@ -0,0 +1,91 @@ +/* + ------------------------------------------------------------------ + + Copyright (C) Open Ephys + + ------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "AuxiliaryIOInterface.h" +#include "../OnixSourceEditor.h" +#include "../OnixSourceCanvas.h" +#include "AnalogIOInterface.h" +#include "DigitalIOInterface.h" + +using namespace OnixSourcePlugin; + +AuxiliaryIOInterface::AuxiliaryIOInterface(std::shared_ptr d, OnixSourceEditor* e, OnixSourceCanvas* c) : + SettingsInterface(d, e, c) +{ + if (device != nullptr) + { + auto auxiliaryIO = std::static_pointer_cast(device); + + static int offset = 55; + FontOptions font = FontOptions("Fira Code", "Bold", 22.0f); + + analogLabel = std::make_unique