diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp index 7c334a7..df21742 100644 --- a/Source/Devices/PortController.cpp +++ b/Source/Devices/PortController.cpp @@ -32,7 +32,7 @@ PortController::PortController(PortName port_, std::shared_ptr ctx_) : int PortController::configureDevice() { - if (deviceContext == nullptr || !deviceContext->isInitialized()) return -5; + if (deviceContext == nullptr || !deviceContext->isInitialized()) return 1; return deviceContext->writeRegister(deviceIdx, (uint32_t)PortControllerRegister::ENABLE, 1); } diff --git a/Source/Onix1.cpp b/Source/Onix1.cpp index b31166f..b7c53ae 100644 --- a/Source/Onix1.cpp +++ b/Source/Onix1.cpp @@ -49,10 +49,12 @@ Onix1::~Onix1() oni_destroy_ctx(ctx_); } -int Onix1::updateDeviceTable() +int Onix1::getDeviceTable(device_map_t* deviceTable) { - if (deviceTable.size() > 0) - deviceTable.clear(); + if (deviceTable->size() > 0) + deviceTable->clear(); + + issueReset(); oni_size_t numDevices; int rc = getOption(ONI_OPT_NUMDEVICES, &numDevices); @@ -70,13 +72,13 @@ int Onix1::updateDeviceTable() for (const auto& device : devices) { - deviceTable.insert({ device.idx, device }); + deviceTable->insert({ device.idx, device }); } return rc; } -std::map Onix1::getHubIds() +std::map Onix1::getHubIds(device_map_t deviceTable) const { std::map hubIds; @@ -92,7 +94,10 @@ std::map Onix1::getHubIds() oni_reg_val_t hubId = 0; int rc = oni_read_reg(ctx_, offsets[i] + ONIX_HUB_DEV_IDX, (uint32_t)ONIX_HUB_HARDWAREID, &hubId); if (rc != ONI_ESUCCESS) + { LOGE("Unable to read the hub device index for the hub at index ", offsets[i]); + continue; + } hubIds.insert({ offsets[i], hubId }); } diff --git a/Source/Onix1.h b/Source/Onix1.h index 5d4c5bd..f8ebb3f 100644 --- a/Source/Onix1.h +++ b/Source/Onix1.h @@ -98,9 +98,7 @@ namespace OnixSourcePlugin int writeRegister(oni_dev_idx_t, oni_reg_addr_t, oni_reg_val_t) const; - device_map_t getDeviceTable() const noexcept { return deviceTable; } - - int updateDeviceTable(); + int getDeviceTable(device_map_t*); oni_frame_t* readFrame() const; @@ -111,7 +109,7 @@ namespace OnixSourcePlugin double convertTimestampToSeconds(uint32_t timestamp) const { return static_cast(timestamp) / ACQ_CLK_HZ; } /** Gets a map of all hubs connected, where the index of the map is the hub address, and the value is the hub ID */ - std::map getHubIds(); + std::map getHubIds(device_map_t) const; /** Gets a vector of device indices from a device_map_t object, optionally filtered by a specific hub */ static std::vector getDeviceIndices(device_map_t deviceMap, int hubIndex = -1); @@ -130,8 +128,6 @@ namespace OnixSourcePlugin uint32_t ACQ_CLK_HZ; - device_map_t deviceTable; - template size_t opt_size_(opt_t opt) { diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index bd39d8b..83e6b0b 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -83,7 +83,7 @@ std::unique_ptr OnixSource::createEditor(SourceNode* sn) return e; } -void OnixSource::disconnectDevices(bool updateStreamInfo) +bool OnixSource::disconnectDevices(bool updateStreamInfo) { sourceBuffers.clear(true); @@ -93,18 +93,26 @@ void OnixSource::disconnectDevices(bool updateStreamInfo) devicesFound = false; if (context != nullptr && context->isInitialized()) - context->setOption(ONIX_OPT_PASSTHROUGH, 0); + { + if (context->setOption(ONIX_OPT_PASSTHROUGH, 0) != ONI_ESUCCESS) + { + LOGE("Unable to set passthrough option when disconnecting devices."); + return false; + } + } if (updateStreamInfo) CoreServices::updateSignalChain(editor); + + return true; } template bool OnixSource::configureDevice(OnixDeviceVector& sources, OnixSourceCanvas* canvas, std::string deviceName, - std::string hubName, - OnixDeviceType deviceType, - const oni_dev_idx_t deviceIdx, + std::string hubName, + OnixDeviceType deviceType, + const oni_dev_idx_t deviceIdx, std::shared_ptr ctx) { std::shared_ptr device = std::static_pointer_cast(canvas->getDevicePtr(Device::getDeviceType(), deviceIdx)); @@ -148,64 +156,132 @@ bool OnixSource::configureDevice(OnixDeviceVector& sources, return res == ONI_ESUCCESS; } -void OnixSource::initializeDevices(bool updateStreamInfo) +bool OnixSource::getHubFirmwareVersion(std::shared_ptr ctx, uint32_t hubIndex, uint32_t* firmwareVersion) { - if (context == nullptr || !context->isInitialized()) + if (ctx->readRegister(hubIndex + ONIX_HUB_DEV_IDX, ONIX_HUB_FIRMWAREVER, firmwareVersion) != ONI_ESUCCESS) { - LOGE("Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); - return; + LOGE("Unable to read the hub firmware version at index ", hubIndex); + return false; } - if (devicesFound) + return true; +} + +bool OnixSource::enablePassthroughMode(std::shared_ptr ctx, bool passthroughA, bool passthroughB) +{ + if (ctx == nullptr || !ctx->isInitialized()) { - disconnectDevices(false); + Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot enable passthrough mode, context is not initialized correctly."); + return false; } int val = 0; - if (getParameter("passthroughA")->getValue()) + if (passthroughA) { LOGD("Passthrough mode enabled for Port A"); val |= 1 << 0; } - if (getParameter("passthroughB")->getValue()) + if (passthroughB) { LOGD("Passthrough mode enabled for Port B"); val |= 1 << 2; } - context->setOption(ONIX_OPT_PASSTHROUGH, val); + return ctx->setOption(ONIX_OPT_PASSTHROUGH, val) == ONI_ESUCCESS; +} - context->issueReset(); - int rc = context->updateDeviceTable(); +bool OnixSource::configurePort(PortName port) +{ + if (context == nullptr || !context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } - if (rc != ONI_ESUCCESS) return; + if (port == PortName::PortA) + { + if (portA->configureDevice() != ONI_ESUCCESS) + { + Onix1::showWarningMessageBoxAsync("Configuration Error", "Unable to configure Port A."); + return false; + } + } + else if (port == PortName::PortB) + { + if (portB->configureDevice() != ONI_ESUCCESS) + { + Onix1::showWarningMessageBoxAsync("Configuration Error", "Unable to configure Port B."); + return false; + } + } - if (portA->configureDevice() != ONI_ESUCCESS) LOGE("Unable to configure Port A."); - if (portB->configureDevice() != ONI_ESUCCESS) LOGE("Unable to configure Port B."); + return true; +} + +bool OnixSource::checkHubFirmwareCompatibility(std::shared_ptr context, device_map_t deviceTable) +{ + auto hubIds = context->getHubIds(deviceTable); + + if (hubIds.size() == 0) + { + LOGE("No hub IDs found."); + return false; + } + + for (const auto& [hubIndex, hubId] : hubIds) + { + if (hubId == ONIX_HUB_FMCHOST) // NB: Breakout Board + { + static constexpr int RequiredMajorVersion = 1; + uint32_t firmwareVersion = 0; + if (!getHubFirmwareVersion(context, hubIndex, &firmwareVersion)) + { + return false; + } + + auto majorVersion = (firmwareVersion & 0xFF00) >> 8; + auto minorVersion = firmwareVersion & 0xFF; + + LOGD("Breakout board firmware version: v", majorVersion, ".", minorVersion); + + if (majorVersion != RequiredMajorVersion) + { + Onix1::showWarningMessageBoxAsync("Invalid Firmware Version", "The breakout board major version is v" + std::to_string(majorVersion) + ", but this plugin is only compatible with v" + std::to_string(RequiredMajorVersion) + ". To use this plugin, upgrade to a version that supports the breakout board v" + std::to_string(majorVersion)); + return false; + } + } + } - device_map_t deviceMap = context->getDeviceTable(); + return true; +} + +bool OnixSource::initializeDevices(device_map_t deviceTable, bool updateStreamInfo) +{ + if (context == nullptr || !context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } - if (deviceMap.size() == 0) + if (deviceTable.size() == 0) { LOGE("No devices found."); if (updateStreamInfo) CoreServices::updateSignalChain(editor); - return; + return false; } - auto hubIds = context->getHubIds(); + auto hubIds = context->getHubIds(deviceTable); if (hubIds.size() == 0) { LOGE("No hub IDs found."); if (updateStreamInfo) CoreServices::updateSignalChain(editor); - return; + return false; } - devicesFound = true; - - const int bufferSizeInSeconds = 10; + devicesFound = false; // NB: Search through all hubs, and initialize devices for (const auto& [hubIndex, hubId] : hubIds) @@ -213,46 +289,44 @@ void OnixSource::initializeDevices(bool updateStreamInfo) if (hubId == ONIX_HUB_FMCHOST) // NB: Breakout Board { hubNames.insert({ hubIndex, BREAKOUT_BOARD_NAME }); - bool result = false; auto canvas = editor->getCanvas(); - result = configureDevice(sources, canvas, "Heartbeat", BREAKOUT_BOARD_NAME, Heartbeat::getDeviceType(), hubIndex, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Heartbeat", BREAKOUT_BOARD_NAME, Heartbeat::getDeviceType(), hubIndex, context); + if (!devicesFound) return false; - result = configureDevice(sources, canvas, "Output Clock", BREAKOUT_BOARD_NAME, OutputClock::getDeviceType(), hubIndex + 5, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Output Clock", BREAKOUT_BOARD_NAME, OutputClock::getDeviceType(), hubIndex + 5, context); + if (!devicesFound) return false; - result = configureDevice(sources, canvas, "Analog IO", BREAKOUT_BOARD_NAME, AnalogIO::getDeviceType(), hubIndex + 6, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Analog IO", BREAKOUT_BOARD_NAME, AnalogIO::getDeviceType(), hubIndex + 6, context); + if (!devicesFound) return false; - result = configureDevice(sources, canvas, "Digital IO", BREAKOUT_BOARD_NAME, DigitalIO::getDeviceType(), hubIndex + 7, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Digital IO", BREAKOUT_BOARD_NAME, DigitalIO::getDeviceType(), hubIndex + 7, context); + if (!devicesFound) return false; - result = configureDevice(sources, canvas, "Memory Monitor", BREAKOUT_BOARD_NAME, MemoryMonitor::getDeviceType(), hubIndex + 10, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Memory Monitor", BREAKOUT_BOARD_NAME, MemoryMonitor::getDeviceType(), hubIndex + 10, context); + if (!devicesFound) return false; - result = configureDevice(sources, canvas, "Harp Sync Input", BREAKOUT_BOARD_NAME, HarpSyncInput::getDeviceType(), hubIndex + 12, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Harp Sync Input", BREAKOUT_BOARD_NAME, HarpSyncInput::getDeviceType(), hubIndex + 12, context); + if (!devicesFound) return false; } else if (hubId == ONIX_HUB_HSNP) { hubNames.insert({ hubIndex, NEUROPIXELSV1F_HEADSTAGE_NAME }); - bool result = false; auto canvas = editor->getCanvas(); for (int i = 0; i < 2; i++) { - result = configureDevice(sources, canvas, "Probe" + std::to_string(i), NEUROPIXELSV1F_HEADSTAGE_NAME, Neuropixels1f::getDeviceType(), hubIndex + i, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "Probe" + std::to_string(i), NEUROPIXELSV1F_HEADSTAGE_NAME, Neuropixels1f::getDeviceType(), hubIndex + i, context); + if (!devicesFound) return false; } - result = configureDevice(sources, canvas, "BNO055", NEUROPIXELSV1F_HEADSTAGE_NAME, Bno055::getDeviceType(), hubIndex + 2, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "BNO055", NEUROPIXELSV1F_HEADSTAGE_NAME, Bno055::getDeviceType(), hubIndex + 2, context); + if (!devicesFound) return false; } } // NB: Search for passthrough devices, and initialize any headstages found in passthrough mode - for (const auto& [index, device] : deviceMap) + for (const auto& [index, device] : deviceTable) { if (device.id == ONIX_DS90UB9RAW) { @@ -266,21 +340,24 @@ void OnixSource::initializeDevices(bool updateStreamInfo) uint32_t hsid = EEPROM->GetHeadStageID(); LOGD("Detected headstage ", hsid); - bool result = false; auto canvas = editor->getCanvas(); if (hsid == ONIX_HUB_HSNP2E) { auto hubIndex = OnixDevice::getHubIndexFromPassthroughIndex(index); - result = configureDevice(sources, canvas, "", NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "", NEUROPIXELSV2E_HEADSTAGE_NAME, Neuropixels2e::getDeviceType(), hubIndex, context); + if (!devicesFound) return false; - result = configureDevice(sources, canvas, "BNO055", NEUROPIXELSV2E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); - if (!result) devicesFound = false; + devicesFound = configureDevice(sources, canvas, "BNO055", NEUROPIXELSV2E_HEADSTAGE_NAME, PolledBno055::getDeviceType(), hubIndex + 1, context); + if (!devicesFound) return false; if (sources.back()->getDeviceType() != OnixDeviceType::POLLEDBNO) - LOGE("Unknown device encountered when setting headstage."); + { + LOGE("Unknown device encountered when configuring headstage ", NEUROPIXELSV2E_HEADSTAGE_NAME); + devicesFound = false; + return false; + } const auto& polledBno = std::static_pointer_cast(sources.back()); @@ -294,18 +371,87 @@ void OnixSource::initializeDevices(bool updateStreamInfo) context->issueReset(); - oni_size_t frameSize; - rc = context->getOption(ONI_OPT_MAXREADFRAMESIZE, &frameSize); - printf("Max. read frame size: %u bytes\n", frameSize); + if (updateStreamInfo) CoreServices::updateSignalChain(editor); - rc = context->getOption(ONI_OPT_MAXWRITEFRAMESIZE, &frameSize); - printf("Max. write frame size: %u bytes\n", frameSize); + LOGD("All devices initialized."); + return devicesFound; +} - context->setOption(ONI_OPT_BLOCKREADSIZE, block_read_size); +bool OnixSource::configureBlockReadSize(std::shared_ptr context, uint32_t blockReadSize) +{ + if (context == nullptr || !context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot set block read size, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } - if (updateStreamInfo) CoreServices::updateSignalChain(editor); + oni_size_t readFrameSize; + int rc = context->getOption(ONI_OPT_MAXREADFRAMESIZE, &readFrameSize); + if (rc == ONI_ESUCCESS) + { + LOGD("Max read frame size: ", readFrameSize, " bytes"); + } + else + { + LOGE("Unable to get read frame size."); + return false; + } - LOGD("All devices initialized."); + oni_size_t writeFrameSize; + rc = context->getOption(ONI_OPT_MAXWRITEFRAMESIZE, &writeFrameSize); + if (rc == ONI_ESUCCESS) + { + LOGD("Max write frame size: ", writeFrameSize, " bytes"); + } + else + { + LOGE("Unable to get write frame size."); + return false; + } + + if (!writeBlockReadSize(context, blockReadSize, readFrameSize)) + { + return false; + } + + return true; +} + +uint32_t OnixSource::getBlockReadSize() const +{ + return blockReadSize; +} + +void OnixSource::setBlockReadSize(uint32_t newReadSize) +{ + blockReadSize = newReadSize; +} + +bool OnixSource::writeBlockReadSize(std::shared_ptr context, uint32_t blockReadSize, uint32_t readFrameSize) +{ + if (context == nullptr || !context->isInitialized()) + { + Onix1::showWarningMessageBoxAsync("Invalid Context", "Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + return false; + } + + if (blockReadSize < readFrameSize) + { + Onix1::showWarningMessageBoxAsync("Invalid Block Read Size", "The block read size is too small. The max read frame size is " + std::to_string(readFrameSize) + ", but the block read size is " + std::to_string(blockReadSize) + ".\n\nTo continue, increase the block read size to be greater than " + std::to_string(readFrameSize) + " and reconnect."); + return false; + } + + int rc = context->setOption(ONI_OPT_BLOCKREADSIZE, blockReadSize); + + if (rc != ONI_ESUCCESS) + { + LOGE("Unknown error found when setting block read size to ", blockReadSize); + return false; + } + + LOGD("Block read size: ", blockReadSize, " bytes"); + + return true; } OnixDeviceVector OnixSource::getDataSources() @@ -493,6 +639,11 @@ std::shared_ptr OnixSource::getContext() return context; } +bool OnixSource::getDeviceTable(device_map_t* deviceTable) +{ + return context->getDeviceTable(deviceTable) == ONI_ESUCCESS; +} + void OnixSource::updateSettings(OwnedArray* continuousChannels, OwnedArray* eventChannels, OwnedArray* spikeChannels, @@ -818,9 +969,7 @@ bool OnixSource::stopAcquisition() devicesFound = false; - MessageManager::callAsync([] { AlertWindow::showMessageBoxAsync(MessageBoxIconType::WarningIcon, "Port Communication Lock Lost", - "The port communication lock was lost during acquisition, inspect hardware connections and port switch." + - String("\n\nTo continue, press disconnect in the GUI, then press connect."), "Okay"); }); + Onix1::showWarningMessageBoxAsync("Port Communication Lock Lost", "The port communication lock was lost during acquisition. Inspect hardware connections and port switch. \n\nTo continue, press disconnect in the GUI, then press connect."); } return true; diff --git a/Source/OnixSource.h b/Source/OnixSource.h index ce37d41..2e732ee 100644 --- a/Source/OnixSource.h +++ b/Source/OnixSource.h @@ -96,16 +96,23 @@ namespace OnixSourcePlugin std::shared_ptr getContext(); - void initializeDevices(bool updateStreamInfo = false); + bool getDeviceTable(device_map_t*); - void disconnectDevices(bool updateStreamInfo = false); + static bool enablePassthroughMode(std::shared_ptr, bool, bool); - OnixDeviceVector getDataSources(); + bool configurePort(PortName); - OnixDeviceVector getEnabledDataSources(); + static bool checkHubFirmwareCompatibility(std::shared_ptr, device_map_t); - OnixDeviceVector getDataSourcesFromPort(PortName port); + bool initializeDevices(device_map_t, bool updateStreamInfo = false); + static bool configureBlockReadSize(std::shared_ptr, uint32_t); + + bool disconnectDevices(bool updateStreamInfo = false); + + OnixDeviceVector getDataSources(); + OnixDeviceVector getEnabledDataSources(); + OnixDeviceVector getDataSourcesFromPort(PortName port); OnixDeviceVector getDataSourcesFromOffset(int offset); std::shared_ptr getDevice(OnixDeviceType type); @@ -128,6 +135,10 @@ namespace OnixSourcePlugin OwnedArray* devices, OwnedArray* configurationObjects); + uint32_t getBlockReadSize() const; + + void setBlockReadSize(uint32_t); + private: /** Available data sources */ @@ -148,7 +159,7 @@ namespace OnixSourcePlugin std::shared_ptr portA; std::shared_ptr portB; - const oni_size_t block_read_size = 2048; + uint32_t blockReadSize = 4096; bool devicesFound = false; @@ -162,6 +173,10 @@ namespace OnixSourcePlugin template static bool configureDevice(OnixDeviceVector&, OnixSourceCanvas*, std::string, std::string, OnixDeviceType, const oni_dev_idx_t, std::shared_ptr); + static bool getHubFirmwareVersion(std::shared_ptr, uint32_t, uint32_t*); + + static bool writeBlockReadSize(std::shared_ptr, uint32_t, uint32_t); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OnixSource); }; } diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index 64cd8c7..bd5c7a1 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -31,19 +31,30 @@ OnixSourceEditor::OnixSourceEditor(GenericProcessor* parentNode, OnixSource* sou { canvas = nullptr; - FontOptions fontOptionSmall = FontOptions("Fira Code", "Regular", 12.0f); - FontOptions fontOptionTitle = FontOptions("Fira Code", "Bold", 15.0f); - - memoryUsage = std::make_unique(parentNode); - memoryUsage->setBounds(10, 30, 15, 95); - memoryUsage->setTooltip("Monitors the percent of the hardware memory buffer used."); - addAndMakeVisible(memoryUsage.get()); + FontOptions fontOptionRegular = FontOptions("Fira Code", 12.0f, Font::plain); + FontOptions fontOptionTitle = FontOptions("Fira Code", 15.0f, Font::bold); if (source->isContextInitialized()) { + memoryUsage = std::make_unique(parentNode); + memoryUsage->setBounds(8, 28, 14, 80); + memoryUsage->setTooltip("Monitors the percent of the hardware memory buffer used."); + addAndMakeVisible(memoryUsage.get()); + + blockReadSizeValue = std::make_unique