diff --git a/Source/Devices/AnalogIO.cpp b/Source/Devices/AnalogIO.cpp index 09cc101..c59d7e0 100644 --- a/Source/Devices/AnalogIO.cpp +++ b/Source/Devices/AnalogIO.cpp @@ -31,7 +31,7 @@ AnalogIO::AnalogIO(std::string name, std::string hubName, const oni_dev_idx_t de OnixDevice::createStreamName({ getHubName(), name, "AnalogInput" }), "Analog Input data", getStreamIdentifier(), - getNumChannels(), + numChannels, getSampleRate(), "AnalogInput", ContinuousChannel::Type::ADC, @@ -181,11 +181,6 @@ void AnalogIO::setDataType(AnalogIODataType type) dataType = type; } -int AnalogIO::getNumChannels() -{ - return numChannels; -} - void AnalogIO::startAcquisition() { currentFrame = 0; @@ -247,22 +242,16 @@ void AnalogIO::processFrame(uint64_t eventWord) if (currentFrame >= numFrames) { - shouldAddToBuffer = true; - currentFrame = 0; - } - - if (shouldAddToBuffer) - { - shouldAddToBuffer = false; analogInputBuffer->addToBuffer(analogInputSamples.data(), sampleNumbers, timestamps, eventCodes, numFrames); analogInputSamples.fill(0); + currentFrame = 0; } } void AnalogIO::processFrames() { - while (frameQueue.peek() != nullptr ) + while (frameQueue.peek() != nullptr) { processFrame(); } diff --git a/Source/Devices/AnalogIO.h b/Source/Devices/AnalogIO.h index 45a2d5b..b395a0b 100644 --- a/Source/Devices/AnalogIO.h +++ b/Source/Devices/AnalogIO.h @@ -89,11 +89,8 @@ namespace OnixSourcePlugin 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(); @@ -103,6 +100,7 @@ namespace OnixSourcePlugin DataBuffer* analogInputBuffer = nullptr; static constexpr int AnalogIOFrequencyHz = 100000; + static constexpr int framesToAverage = 4; // NB: Downsampling from 100 kHz to 25 kHz static constexpr int numFrames = 25; static constexpr int numChannels = 12; @@ -119,8 +117,6 @@ namespace OnixSourcePlugin unsigned short currentAverageFrame = 0; int sampleNumber = 0; - bool shouldAddToBuffer = false; - std::array analogInputSamples; double timestamps[numFrames]; diff --git a/Source/Devices/AuxiliaryIO.cpp b/Source/Devices/AuxiliaryIO.cpp deleted file mode 100644 index c72f705..0000000 --- a/Source/Devices/AuxiliaryIO.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - ------------------------------------------------------------------ - - 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) -{ - analogIO = getDevice(OnixDeviceType::ANALOGIO); - digitalIO = getDevice(OnixDeviceType::DIGITALIO); -} - -// 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; -} - -bool AuxiliaryIO::isEnabled() const -{ - return analogIO->isEnabled(); -} - -void AuxiliaryIO::processFrames() -{ - digitalIO->processFrames(); - - while (digitalIO->hasEventWord() && analogIO->getNumberOfFrames() >= AnalogIO::framesToAverage) - { - 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 analogIO; -} - -std::shared_ptr AuxiliaryIO::getDigitalIO() -{ - return digitalIO; -} diff --git a/Source/Devices/AuxiliaryIO.h b/Source/Devices/AuxiliaryIO.h deleted file mode 100644 index 794418f..0000000 --- a/Source/Devices/AuxiliaryIO.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - ------------------------------------------------------------------ - - 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); - - bool isEnabled() const override; - - 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); - - std::shared_ptr analogIO; - std::shared_ptr digitalIO; - - JUCE_LEAK_DETECTOR(AuxiliaryIO); - }; -} diff --git a/Source/Devices/DeviceList.h b/Source/Devices/DeviceList.h index 2e697e2..0cf5851 100644 --- a/Source/Devices/DeviceList.h +++ b/Source/Devices/DeviceList.h @@ -20,7 +20,6 @@ */ -#include "AuxiliaryIO.h" #include "AnalogIO.h" #include "Bno055.h" #include "DigitalIO.h" diff --git a/Source/Devices/DigitalIO.cpp b/Source/Devices/DigitalIO.cpp index 30060f3..1d6eae9 100644 --- a/Source/Devices/DigitalIO.cpp +++ b/Source/Devices/DigitalIO.cpp @@ -26,8 +26,37 @@ using namespace OnixSourcePlugin; DigitalIO::DigitalIO(std::string name, std::string hubName, const oni_dev_idx_t deviceIdx_, std::shared_ptr oni_ctx) - : OnixDevice(name, hubName, DigitalIO::getDeviceType(), deviceIdx_, oni_ctx), eventWords(64) + : OnixDevice(name, hubName, DigitalIO::getDeviceType(), deviceIdx_, oni_ctx) { + StreamInfo digitalInputStream = StreamInfo( + OnixDevice::createStreamName({ getHubName(), name, "DigitalInputs" }), + "Digital Inputs data", + getStreamIdentifier(), + NumDigitalInputs, + AnalogIO::getSampleRate(), + "CH", + ContinuousChannel::Type::AUX, + 1.0, + "u", // NB: Digital data is unitless by definition + {}, + { "input" }); + streamInfos.add(digitalInputStream); + + StreamInfo digitalButtonStream = StreamInfo( + OnixDevice::createStreamName({ getHubName(), name, "DigitalButtons" }), + "Digital Buttons data", + getStreamIdentifier(), + NumButtons, + AnalogIO::getSampleRate(), + "", + ContinuousChannel::Type::AUX, + 1.0, + "u", // NB: Digital data is unitless by definition + { "Moon", "Triangle", "X", "Check", "Circle", "Square" }, + { "input" }); + streamInfos.add(digitalButtonStream); + + eventCodes.fill(0); } OnixDeviceType DigitalIO::getDeviceType() @@ -49,7 +78,9 @@ int DigitalIO::configureDevice() 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(); + // NB: Two states are not accounted for when comparing clock ticks on the hardware, + // therefore the periodTicks variable must be decreased by 2 to get the correct sample rate. + uint32_t periodTicks = (baseFreqHz / (uint32_t)AnalogIO::getSampleRate()) - 2u; 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."); @@ -64,10 +95,16 @@ bool DigitalIO::updateSettings() void DigitalIO::startAcquisition() { + currentFrame = 0; + sampleNumber = 0; + + digitalSamples.fill(0); } void DigitalIO::addSourceBuffers(OwnedArray& sourceBuffers) { + sourceBuffers.add(new DataBuffer(NumChannels, (int)streamInfos.getFirst().getSampleRate() * bufferSizeInSeconds)); + digitalBuffer = sourceBuffers.getLast(); } EventChannel::Settings DigitalIO::getEventChannelSettings(DataStream* stream) @@ -78,48 +115,54 @@ EventChannel::Settings DigitalIO::getEventChannelSettings(DataStream* stream) "Digital inputs and breakout button states coming from a DigitalIO device", getStreamIdentifier() + ".event.digital", stream, - numButtons + numDigitalInputs + NumChannels }; return settings; } -int DigitalIO::getNumberOfWords() +float DigitalIO::getChannelState(uint8_t state, int channel) { - return eventWords.size_approx(); -} + return (state & (1 << channel)) >> channel; // NB: Return the state of the specified channel +}; void DigitalIO::processFrames() { oni_frame_t* frame; while (frameQueue.try_dequeue(frame)) { + size_t offset = 0; uint16_t* dataPtr = (uint16_t*)frame->data; - uint64_t timestamp = deviceContext->convertTimestampToSeconds(frame->time); - int dataOffset = 4; + timestamps[currentFrame] = deviceContext->convertTimestampToSeconds(frame->time); + sampleNumbers[currentFrame] = sampleNumber++; - uint64_t portState = *(dataPtr + dataOffset); - uint64_t buttonState = *(dataPtr + dataOffset + 1); + constexpr int inputDataOffset = 4; + constexpr int buttonDataOffset = inputDataOffset + 1; - uint64_t ttlEventWord = (buttonState & 0x3F) << 8 | (portState & 0xFF); - eventWords.enqueue(ttlEventWord); + uint64_t inputState = *(dataPtr + inputDataOffset); + uint64_t buttonState = *(dataPtr + buttonDataOffset); - oni_destroy_frame(frame); - } -} + for (int i = 0; i < NumDigitalInputs; i++) + { + digitalSamples[currentFrame + offset++ * NumFrames] = getChannelState(inputState, i); + } -uint64_t DigitalIO::getEventWord() -{ - uint64_t eventWord; - if (eventWords.try_dequeue(eventWord)) - return eventWord; + for (int i = 0; i < NumButtons; i++) + { + digitalSamples[currentFrame + offset++ * NumFrames] = getChannelState(buttonState, i); + } - return 0; -} + eventCodes[currentFrame] = (buttonState & 0x3F) << 8 | (inputState & 0xFF); -bool DigitalIO::hasEventWord() -{ - return eventWords.peek() != nullptr; + oni_destroy_frame(frame); + + if (++currentFrame >= NumFrames) + { + digitalBuffer->addToBuffer(digitalSamples.data(), sampleNumbers.data(), timestamps.data(), eventCodes.data(), NumFrames); + + currentFrame = 0; + } + } } diff --git a/Source/Devices/DigitalIO.h b/Source/Devices/DigitalIO.h index 0a8756d..6589f1f 100644 --- a/Source/Devices/DigitalIO.h +++ b/Source/Devices/DigitalIO.h @@ -79,18 +79,30 @@ namespace OnixSourcePlugin EventChannel::Settings getEventChannelSettings(DataStream* stream); - int getNumberOfWords(); - uint64_t getEventWord(); - bool hasEventWord(); - static OnixDeviceType getDeviceType(); private: - static constexpr int numDigitalInputs = 8; - static constexpr int numButtons = 6; + DataBuffer* digitalBuffer = nullptr; + + static float getChannelState(uint8_t state, int channel); + + static constexpr int NumFrames = 25; + + static constexpr int NumDigitalInputs = 8; + static constexpr int NumButtons = 6; + static constexpr int NumChannels = NumDigitalInputs + NumButtons; + + static constexpr int NumSamples = NumFrames * NumChannels; + + unsigned short currentFrame = 0; + int64_t sampleNumber = 0; + + std::array digitalSamples; - ReaderWriterQueue eventWords; + std::array timestamps; + std::array sampleNumbers; + std::array eventCodes; JUCE_LEAK_DETECTOR(DigitalIO); }; diff --git a/Source/OnixDevice.cpp b/Source/OnixDevice.cpp index cc0601c..33537b8 100644 --- a/Source/OnixDevice.cpp +++ b/Source/OnixDevice.cpp @@ -24,6 +24,20 @@ using namespace OnixSourcePlugin; +const std::map OnixDevice::TypeString = { + {OnixDeviceType::BNO, "BNO055"}, + {OnixDeviceType::POLLEDBNO, "BNO055"}, + {OnixDeviceType::NEUROPIXELSV1E, "Neuropixels 1.0e"}, + {OnixDeviceType::NEUROPIXELSV1F, "Neuropixels 1.0f"}, + {OnixDeviceType::NEUROPIXELSV2E, "Neuropixels 2.0"}, + {OnixDeviceType::PORT_CONTROL, "Port Control"}, + {OnixDeviceType::MEMORYMONITOR, "Memory Monitor"}, + {OnixDeviceType::OUTPUTCLOCK, "Output Clock"}, + {OnixDeviceType::HARPSYNCINPUT, "Harp Sync Input"}, + {OnixDeviceType::ANALOGIO, "Analog IO"}, + {OnixDeviceType::DIGITALIO, "Digital IO"} +}; + OnixDevice::OnixDevice(std::string name_, std::string hubName, OnixDeviceType type_, const oni_dev_idx_t deviceIdx_, std::shared_ptr ctx, bool passthrough) : type(type_), deviceIdx(deviceIdx_), frameQueue(32) { @@ -57,22 +71,40 @@ std::string OnixDevice::createStreamName(std::vector names) return streamName; } +bool OnixDevice::isValidPassthroughIndex(oni_dev_idx_t passthroughIndex) +{ + return passthroughIndex == (uint32_t)PassthroughIndex::A || passthroughIndex == (uint32_t)PassthroughIndex::B; +} + oni_dev_idx_t OnixDevice::getHubIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex) { - if (passthroughIndex != (uint32_t)PassthroughIndex::A && passthroughIndex != (uint32_t)PassthroughIndex::B) + if (!isValidPassthroughIndex(passthroughIndex)) { - LOGE("Invalid passthrough index given. Value was ", passthroughIndex); + Onix1::showWarningMessageBoxAsync("Invalid Index", "Invalid passthrough index given. Value was " + std::to_string(passthroughIndex)); return 0; } return (passthroughIndex - 7) << 8; } +oni_dev_idx_t OnixDevice::getPassthroughIndexFromHubIndex(oni_dev_idx_t hubIndex) +{ + auto index = (hubIndex >> 8) + 7; + + if (!isValidPassthroughIndex(index)) + { + Onix1::showWarningMessageBoxAsync("Invalid Index", "Invalid hub index given. Value was " + std::to_string(hubIndex)); + return 0; + } + + return index; +} + oni_dev_idx_t OnixDevice::getDeviceIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex) const { - if (passthroughIndex != (uint32_t)PassthroughIndex::A && passthroughIndex != (uint32_t)PassthroughIndex::B) + if (!isValidPassthroughIndex(passthroughIndex)) { - LOGE("Invalid passthrough index given. Value was ", passthroughIndex); + Onix1::showWarningMessageBoxAsync("Invalid Index", "Invalid passthrough index given. Value was " + std::to_string(passthroughIndex)); return 0; } @@ -203,8 +235,8 @@ std::vector OnixDevice::getUniqueOffsets(OnixDeviceMap devices, bool ignore { std::vector indices; - for (const auto& [key, _] : devices) - { + for (const auto& [key, _] : devices) + { indices.emplace_back(key); } @@ -241,141 +273,3 @@ void OnixDevice::stopAcquisition() oni_destroy_frame(frame); } } - -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; -} - -bool CompositeDevice::isEnabled() const -{ - bool enabled = true; - - for (const auto& device : devices) - { - enabled &= device->isEnabled(); - } - - return enabled; -} - -bool CompositeDevice::isEnabled(uint32_t index) -{ - for (const auto& device : devices) - { - if (device->compareIndex(index)) - return device->isEnabled(); - } - - Onix1::showWarningMessageBoxAsync( - "Unknown Index", - "Could not get the enabled status of a device at index " + std::to_string(index) + ", it was not found in " + getName() - ); - - return false; -} - -void CompositeDevice::setEnabled(bool newState) -{ - for (const auto& device : devices) - { - device->setEnabled(newState); - } -} - -void CompositeDevice::setEnabled(uint32_t index, bool newState) -{ - for (const auto& device : devices) - { - if (device->compareIndex(index)) - { - device->setEnabled(newState); - return; - } - } - - Onix1::showWarningMessageBoxAsync( - "Unknown Index", - "Could not set the enabled status of a device at index " + std::to_string(index) + ", it was not found in " + getName() - ); - - return; -} - -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 4a79568..fe52a32 100644 --- a/Source/OnixDevice.h +++ b/Source/OnixDevice.h @@ -53,9 +53,10 @@ namespace OnixSourcePlugin HARPSYNCINPUT, ANALOGIO, DIGITALIO, - COMPOSITE, }; + static const std::string ProbeString = "Probe"; + struct StreamInfo { public: StreamInfo() {} @@ -201,11 +202,14 @@ namespace OnixSourcePlugin std::string getStreamIdentifier(); static oni_dev_idx_t getHubIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex); + static oni_dev_idx_t getPassthroughIndexFromHubIndex(oni_dev_idx_t hubIndex); + + static const std::map TypeString; protected: oni_dev_idx_t getDeviceIndexFromPassthroughIndex(oni_dev_idx_t passthroughIndex) const; - + ReaderWriterQueue frameQueue; const oni_dev_idx_t deviceIdx; std::shared_ptr deviceContext; @@ -227,58 +231,10 @@ namespace OnixSourcePlugin B = 9 }; - JUCE_LEAK_DETECTOR(OnixDevice); - }; + static bool isValidPassthroughIndex(oni_dev_idx_t); - enum class CompositeDeviceType { - AUXILIARYIO = 0 + JUCE_LEAK_DETECTOR(OnixDevice); }; - + 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; - bool isEnabled() const override; - bool isEnabled(uint32_t index); - void setEnabled(bool newState) override; - void setEnabled(uint32_t index, bool newState); - 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); - }; -} +} \ No newline at end of file diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index bb62c20..2dc5dad 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -335,29 +335,37 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn { hubNames.insert({ hubIndex, BREAKOUT_BOARD_NAME }); - devicesFound = configureDevice(sources, editor, "Output Clock", BREAKOUT_BOARD_NAME, OutputClock::getDeviceType(), hubIndex + 5, context); + static constexpr int OutputClockOffset = 5, AnalogIOOffset = 6, DigitalIOOffset = 7, MemoryMonitorOffset = 10, HarpSyncInputOffset = 12; + + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(OutputClock::getDeviceType()), BREAKOUT_BOARD_NAME, OutputClock::getDeviceType(), hubIndex + OutputClockOffset, context); + if (!devicesFound) + { + sources.clear(); + return false; + } + + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(DigitalIO::getDeviceType()), BREAKOUT_BOARD_NAME, DigitalIO::getDeviceType(), hubIndex + DigitalIOOffset, context); if (!devicesFound) { sources.clear(); return false; } - // NB: Configures AnalogIO and DigitalIO - devicesFound = configureDevice(sources, editor, "Auxiliary IO", BREAKOUT_BOARD_NAME, AuxiliaryIO::getDeviceType(), hubIndex + 6, context); + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(AnalogIO::getDeviceType()), BREAKOUT_BOARD_NAME, AnalogIO::getDeviceType(), hubIndex + AnalogIOOffset, context); if (!devicesFound) { sources.clear(); return false; } - devicesFound = configureDevice(sources, editor, "Memory Monitor", BREAKOUT_BOARD_NAME, MemoryMonitor::getDeviceType(), hubIndex + 10, context); + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(MemoryMonitor::getDeviceType()), BREAKOUT_BOARD_NAME, MemoryMonitor::getDeviceType(), hubIndex + MemoryMonitorOffset, context); if (!devicesFound) { sources.clear(); return false; } - devicesFound = configureDevice(sources, editor, "Harp Sync Input", BREAKOUT_BOARD_NAME, HarpSyncInput::getDeviceType(), hubIndex + 12, context); + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(HarpSyncInput::getDeviceType()), BREAKOUT_BOARD_NAME, HarpSyncInput::getDeviceType(), hubIndex + HarpSyncInputOffset, context); if (!devicesFound) { sources.clear(); @@ -370,7 +378,7 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn for (int i = 0; i < 2; i++) { - devicesFound = configureDevice(sources, editor, "Probe" + std::to_string(i), NEUROPIXELSV1F_HEADSTAGE_NAME, Neuropixels1f::getDeviceType(), hubIndex + i, context); + devicesFound = configureDevice(sources, editor, ProbeString + std::to_string(i), NEUROPIXELSV1F_HEADSTAGE_NAME, Neuropixels1f::getDeviceType(), hubIndex + i, context); if (!devicesFound) { sources.clear(); @@ -378,7 +386,7 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn } } - devicesFound = configureDevice(sources, editor, "BNO055", NEUROPIXELSV1F_HEADSTAGE_NAME, Bno055::getDeviceType(), hubIndex + 2, context); + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(Bno055::getDeviceType()), NEUROPIXELSV1F_HEADSTAGE_NAME, Bno055::getDeviceType(), hubIndex + 2, context); if (!devicesFound) { sources.clear(); @@ -416,14 +424,14 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn { auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); - devicesFound = configureDevice(sources, editor, "Neuropixels 2.0", NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); + devicesFound = configureDevice(sources, editor, ProbeString, NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); if (!devicesFound) { sources.clear(); return false; } - devicesFound = configureDevice(sources, editor, "BNO055", NEUROPIXELSV2E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(PolledBno055::getDeviceType()), NEUROPIXELSV2E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); if (!devicesFound) { sources.clear(); @@ -449,14 +457,14 @@ bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamIn { auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); - devicesFound = configureDevice(sources, editor, "Probe", NEUROPIXELSV1E_HEADSTAGE_NAME, Neuropixels1e::getDeviceType(), hubIndex, context); + devicesFound = configureDevice(sources, editor, ProbeString, NEUROPIXELSV1E_HEADSTAGE_NAME, Neuropixels1e::getDeviceType(), hubIndex, context); if (!devicesFound) { sources.clear(); return false; } - devicesFound = configureDevice(sources, editor, "BNO055", NEUROPIXELSV1E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); + devicesFound = configureDevice(sources, editor, OnixDevice::TypeString.at(PolledBno055::getDeviceType()), NEUROPIXELSV1E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); if (!devicesFound) { sources.clear(); @@ -790,7 +798,9 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel { if (!source->isEnabled()) continue; - if (source->getDeviceType() == OnixDeviceType::NEUROPIXELSV1F) + auto type = source->getDeviceType(); + + if (type == OnixDeviceType::NEUROPIXELSV1F) { DeviceInfo::Settings deviceSettings{ source->getName(), @@ -804,7 +814,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } - else if (source->getDeviceType() == OnixDeviceType::BNO || source->getDeviceType() == OnixDeviceType::POLLEDBNO) + else if (type == OnixDeviceType::BNO || type == OnixDeviceType::POLLEDBNO) { DeviceInfo::Settings deviceSettings{ source->getName(), @@ -826,7 +836,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); } - else if (source->getDeviceType() == OnixDeviceType::NEUROPIXELSV2E) + else if (type == OnixDeviceType::NEUROPIXELSV2E) { DeviceInfo::Settings deviceSettings{ source->getName(), @@ -840,7 +850,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } - else if (source->getDeviceType() == OnixDeviceType::MEMORYMONITOR) + else if (type == OnixDeviceType::MEMORYMONITOR) { DeviceInfo::Settings deviceSettings{ source->getName(), @@ -854,7 +864,7 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } - else if (source->getDeviceType() == OnixDeviceType::HARPSYNCINPUT) + else if (type == OnixDeviceType::HARPSYNCINPUT) { DeviceInfo::Settings deviceSettings{ source->getName(), @@ -868,7 +878,46 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } - else if (source->getDeviceType() == OnixDeviceType::NEUROPIXELSV1E) + else if (type == OnixDeviceType::DIGITALIO) + { + DeviceInfo::Settings digitalIODeviceSettings{ + source->getName(), + "DigitalIO", + "digitalio", + "0000000", + "" + }; + + deviceInfos->add(new DeviceInfo(digitalIODeviceSettings)); + + DataStream::Settings dataStreamSettings{ + OnixDevice::createStreamName({source->getHubName(), source->getName()}), + "Digital inputs and buttons", + source->getStreamIdentifier(), + source->streamInfos[0].getSampleRate(), + true + }; + + addCombinedStreams(dataStreamSettings, source->streamInfos, dataStreams, deviceInfos, continuousChannels); + + auto eventChannelSettings = std::static_pointer_cast(source)->getEventChannelSettings(dataStreams->getLast()); + eventChannels->add(new EventChannel(eventChannelSettings)); + } + else if (type == OnixDeviceType::ANALOGIO) + { + DeviceInfo::Settings analogIODeviceSettings{ + source->getName(), + "AnalogIO", + "analogio", + "0000000", + "" + }; + + deviceInfos->add(new DeviceInfo(analogIODeviceSettings)); + + addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); + } + else if (type == OnixDeviceType::NEUROPIXELSV1E) { DeviceInfo::Settings deviceSettings{ source->getName(), @@ -882,41 +931,10 @@ void OnixSource::updateSettings(OwnedArray* continuousChannel addIndividualStreams(source->streamInfos, dataStreams, deviceInfos, continuousChannels); } - else if (source->getDeviceType() == OnixDeviceType::OUTPUTCLOCK) + else if (type == 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( @@ -947,7 +965,12 @@ void OnixSource::addCombinedStreams(DataStream::Settings dataStreamSettings, auto prefix = streamInfo.getChannelPrefix(); if (suffixes[chan] != "") - prefix += "-" + suffixes[chan]; + { + if (prefix != "") + prefix += "-" + suffixes[chan]; + else + prefix = suffixes[chan]; + } ContinuousChannel::Settings channelSettings{ streamInfo.getChannelType(), diff --git a/Source/OnixSourceCanvas.cpp b/Source/OnixSourceCanvas.cpp index 71cf762..6293aaf 100644 --- a/Source/OnixSourceCanvas.cpp +++ b/Source/OnixSourceCanvas.cpp @@ -67,35 +67,40 @@ void OnixSourceCanvas::addHub(std::string hubName, int offset) { tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - const int passthroughIndex = (offset >> 8) + 7; + const int passthroughIndex = OnixDevice::getPassthroughIndexFromHubIndex(offset); - devices.emplace_back(std::make_shared("Probe", hubName, passthroughIndex, context)); - devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared(ProbeString, hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(PolledBno055::getDeviceType()), 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, context)); - devices.emplace_back(std::make_shared("Probe1", hubName, offset + 1, context)); - devices.emplace_back(std::make_shared("BNO055", hubName, offset + 2, context)); + static constexpr int Probe0Offset = 0, Probe1Offset = 1, BnoOffset = 2; + + devices.emplace_back(std::make_shared(ProbeString + "0", hubName, offset + Probe0Offset, context)); + devices.emplace_back(std::make_shared(ProbeString + "1", hubName, offset + Probe1Offset, context)); + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(Bno055::getDeviceType()), hubName, offset + BnoOffset, context)); } else if (hubName == BREAKOUT_BOARD_NAME) { tab = addTopLevelTab(hubName, 0); - devices.emplace_back(std::make_shared("Auxiliary IO", hubName, 6, 7, context)); - devices.emplace_back(std::make_shared("Harp Sync Input", hubName, 12, context)); - devices.emplace_back(std::make_shared("Output Clock", hubName, 5, context)); + static constexpr int OutputClockOffset = 5, AnalogIOOffset = 6, DigitalIOOffset = 7, HarpSyncInputOffset = 12; + + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(DigitalIO::getDeviceType()), hubName, DigitalIOOffset, context)); + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(AnalogIO::getDeviceType()), hubName, AnalogIOOffset, context)); + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(HarpSyncInput::getDeviceType()), hubName, HarpSyncInputOffset, context)); + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(OutputClock::getDeviceType()), hubName, OutputClockOffset, context)); } else if (hubName == NEUROPIXELSV2E_HEADSTAGE_NAME) { - const int passthroughIndex = (offset >> 8) + 7; + const int passthroughIndex = OnixDevice::getPassthroughIndexFromHubIndex(offset); tab = addTopLevelTab(getTopLevelTabName(port, hubName), (int)port); - devices.emplace_back(std::make_shared("", hubName, passthroughIndex, context)); - devices.emplace_back(std::make_shared("BNO055", hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared(ProbeString, hubName, passthroughIndex, context)); + devices.emplace_back(std::make_shared(OnixDevice::TypeString.at(OnixDeviceType::POLLEDBNO), hubName, passthroughIndex, context)); } if (tab != nullptr && devices.size() > 0) @@ -125,61 +130,58 @@ void OnixSourceCanvas::populateSourceTabs(CustomTabComponent* tab, OnixDeviceVec for (const auto& device : devices) { - if (device->getDeviceType() == OnixDeviceType::NEUROPIXELSV1F) + auto type = device->getDeviceType(); + + if (type == OnixDeviceType::NEUROPIXELSV1F) { auto neuropixInterface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, neuropixInterface); } - else if (device->getDeviceType() == OnixDeviceType::NEUROPIXELSV1E) + else if (type == OnixDeviceType::NEUROPIXELSV1E) { auto neuropixInterface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, neuropixInterface); } - else if (device->getDeviceType() == OnixDeviceType::BNO) + else if (type == OnixDeviceType::BNO) { auto bno055Interface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, bno055Interface); } - else if (device->getDeviceType() == OnixDeviceType::OUTPUTCLOCK) + else if (type == OnixDeviceType::OUTPUTCLOCK) { auto outputClockInterface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, outputClockInterface); } - else if (device->getDeviceType() == OnixDeviceType::HARPSYNCINPUT) + else if (type == OnixDeviceType::HARPSYNCINPUT) { auto harpSyncInputInterface = std::make_shared(std::static_pointer_cast(device), editor, this); addInterfaceToTab(device->getName(), tab, harpSyncInputInterface); } - else if (device->getDeviceType() == OnixDeviceType::NEUROPIXELSV2E) + else if (type == OnixDeviceType::ANALOGIO) + { + auto analogIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); + addInterfaceToTab(device->getName(), tab, analogIOInterface); + } + else if (type == OnixDeviceType::DIGITALIO) + { + auto digitalIOInterface = std::make_shared(std::static_pointer_cast(device), editor, this); + addInterfaceToTab(device->getName(), tab, digitalIOInterface); + } + else if (type == OnixDeviceType::NEUROPIXELSV2E) { auto npxv2eInterface = std::make_shared(std::static_pointer_cast(device), editor, this); std::string substring = " Headstage"; std::string hubName = device->getHubName(); addInterfaceToTab(hubName.erase(hubName.find(substring), substring.size()), tab, npxv2eInterface); } - else if (device->getDeviceType() == OnixDeviceType::POLLEDBNO) + else if (type == OnixDeviceType::POLLEDBNO) { 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."); + Onix1::showWarningMessageBoxAsync("Device Type Not Found", "The device type " + OnixDevice::TypeString.at(type) + " is not known."); return; } } diff --git a/Source/OpenEphysLib.cpp b/Source/OpenEphysLib.cpp index 5694750..e8a0a70 100644 --- a/Source/OpenEphysLib.cpp +++ b/Source/OpenEphysLib.cpp @@ -47,7 +47,7 @@ extern "C" EXPORT void getLibInfo(Plugin::LibraryInfo* info) info->name = "ONIX Source"; //Version of the library, used only for information - info->libVersion = "0.1.0"; + info->libVersion = "0.2.0"; info->numPlugins = NUM_PLUGINS; } diff --git a/Source/UI/AnalogIOInterface.cpp b/Source/UI/AnalogIOInterface.cpp index 53b19df..d9628f4 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(40, 20, 100, 22); + deviceEnableButton->setBounds(50, 40, 100, 22); deviceEnableButton->setClickingTogglesState(true); deviceEnableButton->setTooltip("If disabled, AnalogIO will not stream or receive data during acquisition"); deviceEnableButton->setToggleState(true, dontSendNotification); @@ -129,6 +129,11 @@ void AnalogIOInterface::updateSettings() } } +void AnalogIOInterface::hideEnableButton() +{ + deviceEnableButton->setVisible(false); +} + void AnalogIOInterface::buttonClicked(Button* button) { if (button == deviceEnableButton.get()) diff --git a/Source/UI/AnalogIOInterface.h b/Source/UI/AnalogIOInterface.h index 4d3f7ef..b5f95f2 100644 --- a/Source/UI/AnalogIOInterface.h +++ b/Source/UI/AnalogIOInterface.h @@ -46,6 +46,8 @@ namespace OnixSourcePlugin void comboBoxChanged(ComboBox* cb) override; void setInterfaceEnabledState(bool newState) override; + void hideEnableButton(); + private: static const int numChannels = 12; diff --git a/Source/UI/AuxiliaryIOInterface.cpp b/Source/UI/AuxiliaryIOInterface.cpp deleted file mode 100644 index 83d54b1..0000000 --- a/Source/UI/AuxiliaryIOInterface.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - ------------------------------------------------------------------ - - 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); - - analogDigitalLabel = std::make_unique