From 15646f5fecf3901d99ca159f5146eb3ca618c64a Mon Sep 17 00:00:00 2001 From: bparks13 Date: Tue, 4 Feb 2025 11:10:25 -0500 Subject: [PATCH 1/7] Check for PortStatus changes - Add PortController as an OnixDevice, implement the necessary methods - Add PortController devices to the FrameReader so the IDs are processed --- Source/Devices/Bno055.cpp | 2 +- Source/Devices/DeviceList.h | 1 + Source/Devices/PortController.cpp | 174 ++++++++++++++++++++++++++++++ Source/Devices/PortController.h | 137 +++++++++++++++++++++++ Source/FrameReader.cpp | 3 +- Source/FrameReader.h | 4 +- Source/OnixDevice.h | 5 +- Source/OnixSource.cpp | 63 +++++++---- Source/OnixSource.h | 13 +-- Source/OnixSourceEditor.cpp | 13 +-- Source/PortController.cpp | 105 ------------------ Source/PortController.h | 81 -------------- 12 files changed, 371 insertions(+), 230 deletions(-) create mode 100644 Source/Devices/PortController.cpp create mode 100644 Source/Devices/PortController.h delete mode 100644 Source/PortController.cpp delete mode 100644 Source/PortController.h diff --git a/Source/Devices/Bno055.cpp b/Source/Devices/Bno055.cpp index 61d6cca..73408aa 100644 --- a/Source/Devices/Bno055.cpp +++ b/Source/Devices/Bno055.cpp @@ -93,7 +93,7 @@ Bno055::~Bno055() int Bno055::enableDevice() { - oni_write_reg(ctx, deviceIdx, (uint32_t)Bno055Registers::ENABLE, (uint32_t)1); + ONI_OK_RETURN_INT(oni_write_reg(ctx, deviceIdx, (uint32_t)Bno055Registers::ENABLE, (uint32_t)1)) return 0; } diff --git a/Source/Devices/DeviceList.h b/Source/Devices/DeviceList.h index 228ff3c..d4ed212 100644 --- a/Source/Devices/DeviceList.h +++ b/Source/Devices/DeviceList.h @@ -3,3 +3,4 @@ #include "HeadStageEEPROM.h" #include "Neuropixels_1.h" #include "Neuropixels2e.h" +#include "PortController.h" diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp new file mode 100644 index 0000000..58bcffd --- /dev/null +++ b/Source/Devices/PortController.cpp @@ -0,0 +1,174 @@ +/* + ------------------------------------------------------------------ + + This file is part of the Open Ephys GUI + Copyright (C) 2023 Allen Institute for Brain Science and 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 "PortController.h" + +PortController::PortController(PortName port_, oni_ctx ctx_) : + OnixDevice(getPortNameString(port_), OnixDeviceType::PORT_CONTROL, (oni_dev_idx_t)port_, ctx_), + port(port_) +{ +} + +PortController::~PortController() +{ +} + +int PortController::enableDevice() +{ + ONI_OK_RETURN_INT(oni_write_reg(ctx, deviceIdx, (uint32_t)PortControllerRegister::ENABLE, (uint32_t)1)) + + return 0; +} + +void PortController::startAcquisition() +{ + errorFlag = false; +} + +void PortController::stopAcquisition() +{ + while (!frameArray.isEmpty()) + { + const GenericScopedLock frameLock(frameArray.getLock()); + oni_destroy_frame(frameArray.removeAndReturn(0)); + } +} + +void PortController::addFrame(oni_frame_t* frame) +{ + const GenericScopedLock frameLock(frameArray.getLock()); + frameArray.add(frame); +} + +void PortController::processFrames() +{ + while (!frameArray.isEmpty()) + { + const GenericScopedLock frameLock(frameArray.getLock()); + oni_frame_t* frame = frameArray.removeAndReturn(0); + + int16_t* dataPtr = (int16_t*)frame->data; + + int dataOffset = 4; + + PortStatusCode code = (PortStatusCode) *(int8_t*)(dataPtr + dataOffset); + + errorFlag = ((uint32_t)code & LINKSTATE_SL) == 0; + + oni_destroy_frame(frame); + + if (errorFlag) + { + LOGE("Port status changed and indicated an error occurred. Port status code is " + String((uint32_t)code)) + return; + } + } +} + +void PortController::updateDiscoveryParameters(DiscoveryParameters parameters) +{ + discoveryParameters = parameters; +} + +DiscoveryParameters PortController::getHeadstageDiscoveryParameters(String headstage) +{ + if (headstage == "Neuropixels 1.0f") + { + return DiscoveryParameters(5.0f, 7.0f, 1.0f, 0.2f); + } + + return DiscoveryParameters(); +} + +bool PortController::configureVoltage(float voltage) const +{ + if (ctx == NULL) return false; + + if (voltage == defaultVoltage) + { + if (discoveryParameters == DiscoveryParameters()) return false; + + for (voltage = discoveryParameters.minVoltage; voltage <= discoveryParameters.maxVoltage; voltage += discoveryParameters.voltageIncrement) + { + setVoltage(voltage); + + if (checkLinkState()) + { + setVoltage(voltage + discoveryParameters.voltageOffset); + return checkLinkState(); + } + } + } + else + { + setVoltage(voltage); + + return checkLinkState(); + } + + return false; +} + +void PortController::setVoltageOverride(float voltage, bool waitToSettle) const +{ + if (ctx == NULL) return; + + ONI_OK(oni_write_reg(ctx, (oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, (oni_reg_val_t)(voltage * 10))); + if (waitToSettle) sleep_for(std::chrono::milliseconds(500)); +} + +void PortController::setVoltage(float voltage) const +{ + if (ctx == NULL) return; + + ONI_OK(oni_write_reg(ctx, (oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, 0)); + sleep_for(std::chrono::milliseconds(300)); + + ONI_OK(oni_write_reg(ctx, (oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::PORTVOLTAGE, (oni_reg_val_t)(voltage * 10))); + sleep_for(std::chrono::milliseconds(500)); +} + +bool PortController::checkLinkState() const +{ + if (ctx == NULL) return false; + + oni_reg_val_t linkState; + int result = oni_read_reg(ctx, (oni_dev_idx_t)port, (oni_reg_addr_t)PortControllerRegister::LINKSTATE, &linkState); + + if (result != 0) { LOGE(oni_error_str(result)); return false; } + else if ((linkState & LINKSTATE_SL) == 0) { LOGE("Unable to acquire communication lock."); return false; } + else return true; +} + +String PortController::getPortNameString(PortName portName) +{ + switch (portName) + { + case PortName::PortA: + return "Port A"; + case PortName::PortB: + return "Port B"; + default: + break; + } +} diff --git a/Source/Devices/PortController.h b/Source/Devices/PortController.h new file mode 100644 index 0000000..e4086ca --- /dev/null +++ b/Source/Devices/PortController.h @@ -0,0 +1,137 @@ +/* + ------------------------------------------------------------------ + + This file is part of the Open Ephys GUI + Copyright (C) 2023 Allen Institute for Brain Science and 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 . + +*/ + +#ifndef __PORTCONTROLLER_H__ +#define __PORTCONTROLLER_H__ + +#include +#include + +#include "oni.h" +#include "../OnixDevice.h" + +using namespace std::this_thread; + +enum class PortControllerRegister : uint32_t +{ + ENABLE = 0, + GPOSTATE = 1, + DESPWR = 2, + PORTVOLTAGE = 3, + SAVEVOLTAGE = 4, + LINKSTATE = 5 +}; + +enum class PortStatusCode : uint32_t +{ + SerdesLock = 0x0001, + SerdesParityPass = 0x0002, + CrcError = 0x0100, + TooManyDevices = 0x0200, + InitializationError = 0x0400, + BadPacketFormat = 0x0800, + InitializationCrcError = 0x1000, +}; + +class DiscoveryParameters +{ +public: + float minVoltage = 0.0f; + float maxVoltage = 0.0f; + float voltageOffset = 0.0f; + float voltageIncrement = 0.0f; + + DiscoveryParameters() {}; + + DiscoveryParameters(float minVoltage_, float maxVoltage_, float voltageOffset_, float voltageIncrement_) + { + minVoltage = minVoltage_; + maxVoltage = maxVoltage_; + voltageOffset = voltageOffset_; + voltageIncrement = voltageIncrement_; + } + + ~DiscoveryParameters() {}; + + bool operator==(const DiscoveryParameters& rhs) const + { + return rhs.minVoltage == minVoltage && rhs.maxVoltage == maxVoltage && rhs.voltageOffset == voltageOffset && rhs.voltageIncrement == voltageIncrement; + } +}; + +class PortController : public OnixDevice +{ +public: + PortController(PortName port_, const oni_ctx ctx_); + + ~PortController(); + + int enableDevice() override; + + int updateSettings() override { return 0; } + + void startAcquisition() override; + + void stopAcquisition() override; + + void addFrame(oni_frame_t*) override; + + void processFrames() override; + + void addSourceBuffers(OwnedArray& sourceBuffers) override {}; + + void updateDiscoveryParameters(DiscoveryParameters parameters); + + bool configureVoltage(float voltage = defaultVoltage) const; + + /** Sets the voltage to the given value, after setting the voltage to zero */ + void setVoltage(float voltage) const; + + /** Overrides the voltage setting and directly sets it to the given voltage */ + void setVoltageOverride(float voltage, bool waitToSettle = true) const; + + bool checkLinkState() const; + + String getPortNameString(PortName portName); + + static DiscoveryParameters getHeadstageDiscoveryParameters(String headstage); + + /** Check if the port status changed and there is an error reported */ + bool getErrorFlag() { return errorFlag; } + +private: + Array frameArray; + + const PortName port; + + static constexpr float defaultVoltage = -1.0f; + + const uint32_t LINKSTATE_PP = 0x2; // parity check pass bit + const uint32_t LINKSTATE_SL = 0x1; // SERDES lock bit + + DiscoveryParameters discoveryParameters; + + std::atomic errorFlag = false; +}; + +#endif // !__PORTCONTROLLER_H__ diff --git a/Source/FrameReader.cpp b/Source/FrameReader.cpp index 6b902bb..8f9f145 100644 --- a/Source/FrameReader.cpp +++ b/Source/FrameReader.cpp @@ -23,7 +23,7 @@ #include "FrameReader.h" -FrameReader::FrameReader(OwnedArray& sources_, oni_ctx ctx_) +FrameReader::FrameReader(Array sources_, oni_ctx ctx_) : Thread("FrameReader"), sources(sources_), ctx(ctx_) @@ -62,6 +62,7 @@ void FrameReader::run() { source->addFrame(frame); destroyFrame = false; + break; } } diff --git a/Source/FrameReader.h b/Source/FrameReader.h index db21fdf..f72ae91 100644 --- a/Source/FrameReader.h +++ b/Source/FrameReader.h @@ -32,7 +32,7 @@ class FrameReader : public Thread { public: - FrameReader(OwnedArray& sources_, oni_ctx ctx_); + FrameReader(Array sources_, oni_ctx ctx_); ~FrameReader(); @@ -40,7 +40,7 @@ class FrameReader : public Thread private: - OwnedArray& sources; + Array sources; oni_ctx ctx; }; diff --git a/Source/OnixDevice.h b/Source/OnixDevice.h index 5c372a6..4246f42 100644 --- a/Source/OnixDevice.h +++ b/Source/OnixDevice.h @@ -39,7 +39,7 @@ #define ONI_OK(exp) {int res = exp; if (res != ONI_ESUCCESS){LOGD(oni_error_str(res));}} #define ONI_OK_RETURN_BOOL(exp) {int res = exp; if (res != ONI_ESUCCESS){LOGD(oni_error_str(res));return false;}} -#define ONI_OK_RETURN_INT(exp, val) {int res = exp; if (res != ONI_ESUCCESS){LOGD(oni_error_str(res));return val;}} +#define ONI_OK_RETURN_INT(exp) {int res = exp; if (res != ONI_ESUCCESS){LOGD(oni_error_str(res));return res;}} using namespace std::chrono; @@ -54,7 +54,8 @@ enum class OnixDeviceType { BNO, NEUROPIXELS_1, NEUROPIXELS_2, - ADC + ADC, + PORT_CONTROL }; struct StreamInfo { diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 7d8b710..fddbf56 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -35,6 +35,9 @@ OnixSource::OnixSource(SourceNode* sn) : addBooleanParameter(Parameter::PROCESSOR_SCOPE, "connected", "Connect", "Connect to Onix hardware", false, true); + portA = std::make_unique(PortName::PortA, context.get()); + portB = std::make_unique(PortName::PortB, context.get()); + if (!context.isInitialized()) { LOGE("Failed to initialize context."); return; } } @@ -97,9 +100,9 @@ void OnixSource::initializeDevices(bool updateStreamInfo) size_t devices_sz = sizeof(oni_device_t) * num_devs; devices = (oni_device_t*)realloc(devices, devices_sz); - if (devices == NULL) - { - LOGE("No devices found."); + if (devices == NULL) + { + LOGE("No devices found."); if (updateStreamInfo) CoreServices::updateSignalChain(editor); return; } @@ -199,7 +202,7 @@ void OnixSource::initializeDevices(bool updateStreamInfo) auto serializer = std::make_unique(DS90UB9x::SER_ADDR, devices[dev_idx].idx, ctx); serializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xSerializerI2CRegister::SCLHIGH, 20); serializer->WriteByte((uint32_t)DS90UB9x::DS90UB9xSerializerI2CRegister::SCLLOW, 20); - + auto EEPROM = std::make_unique(devices[dev_idx].idx, ctx); uint32_t hsid = EEPROM->GetHeadStageID(); LOGD("Detected headstage ", hsid); @@ -225,6 +228,9 @@ void OnixSource::initializeDevices(bool updateStreamInfo) } } + portA->enableDevice(); + portB->enableDevice(); + val = 1; oni_set_opt(ctx, ONI_OPT_RESET, &val, sizeof(val)); @@ -275,10 +281,10 @@ void OnixSource::updateDiscoveryParameters(PortName port, DiscoveryParameters pa switch (port) { case PortName::PortA: - portA.updateDiscoveryParameters(parameters); + portA->updateDiscoveryParameters(parameters); break; case PortName::PortB: - portB.updateDiscoveryParameters(parameters); + portB->updateDiscoveryParameters(parameters); break; default: break; @@ -294,30 +300,32 @@ bool OnixSource::configurePortVoltage(PortName port, String voltage) const switch (port) { case PortName::PortA: - if (voltage == "") return portA.configureVoltage(ctx); - else return portA.configureVoltage(ctx, voltage.getFloatValue()); + if (voltage == "") return portA->configureVoltage(); + else return portA->configureVoltage(voltage.getFloatValue()); case PortName::PortB: - if (voltage == "") return portB.configureVoltage(ctx); - else return portB.configureVoltage(ctx, voltage.getFloatValue()); + if (voltage == "") return portB->configureVoltage(); + else return portB->configureVoltage(voltage.getFloatValue()); default: return false; } } -bool OnixSource::setPortVoltage(PortName port, float voltage) const +void OnixSource::setPortVoltage(PortName port, float voltage) const { - if (!context.isInitialized()) return false; + if (!context.isInitialized()) return; oni_ctx ctx = context.get(); switch (port) { case PortName::PortA: - return portA.setVoltage(ctx, voltage); + portA->setVoltageOverride(voltage); + return; case PortName::PortB: - return portB.setVoltage(ctx, voltage); + portB->setVoltageOverride(voltage); + return; default: - return false; + return; } } @@ -426,8 +434,8 @@ bool OnixSource::isReady() if (!context.isInitialized() || !devicesFound) return false; - if (editor->isHeadstageSelected(PortName::PortA) && !portA.checkLinkState(context.get())) return false; - if (editor->isHeadstageSelected(PortName::PortB) && !portB.checkLinkState(context.get())) return false; + if (editor->isHeadstageSelected(PortName::PortA) && !portA->checkLinkState()) return false; + if (editor->isHeadstageSelected(PortName::PortB) && !portB->checkLinkState()) return false; for (auto source : sources) { @@ -457,7 +465,17 @@ bool OnixSource::startAcquisition() frameReader.reset(); - frameReader = std::make_unique(sources, context.get()); + Array devices; + + for (auto source : sources) + { + devices.add(source); + } + + devices.add(portA.get()); + devices.add(portB.get()); + + frameReader = std::make_unique(devices, context.get()); frameReader->startThread(); for (auto source : sources) @@ -479,6 +497,7 @@ bool OnixSource::stopAcquisition() frameReader->signalThreadShouldExit(); waitForThreadToExit(2000); + frameReader->waitForThreadToExit(1000); if (devicesFound) { @@ -502,6 +521,9 @@ bool OnixSource::stopAcquisition() source->stopAcquisition(); } + portA->stopAcquisition(); + portB->stopAcquisition(); + for (auto buffers : sourceBuffers) buffers->clear(); @@ -517,5 +539,8 @@ bool OnixSource::updateBuffer() source->processFrames(); } - return true; + portA->processFrames(); + portB->processFrames(); + + return !portA->getErrorFlag() && !portB->getErrorFlag(); } diff --git a/Source/OnixSource.h b/Source/OnixSource.h index 4ffc36d..8a0b2ff 100644 --- a/Source/OnixSource.h +++ b/Source/OnixSource.h @@ -33,7 +33,6 @@ #include "OnixSourceEditor.h" #include "Devices/DeviceList.h" #include "FrameReader.h" -#include "PortController.h" class Onix1 { @@ -87,8 +86,8 @@ class OnixSource : public DataThread { if (context.isInitialized()) { - portA.setVoltage(context.get(), 0.0f); - portB.setVoltage(context.get(), 0.0f); + portA->setVoltageOverride(0.0f, false); + portB->setVoltageOverride(0.0f, false); } } @@ -119,9 +118,7 @@ class OnixSource : public DataThread bool configurePortVoltage(PortName port, String voltage) const; /** Sets the port voltage */ - bool setPortVoltage(PortName port, float voltage) const; - - void initializeContext(); + void setPortVoltage(PortName port, float voltage) const; void initializeDevices(bool updateStreamInfo = false); @@ -152,8 +149,8 @@ class OnixSource : public DataThread Onix1 context; - PortController portA = PortController(PortName::PortA); - PortController portB = PortController(PortName::PortB); + std::unique_ptr portA; + std::unique_ptr portB; const oni_size_t block_read_size = 2048; diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index 3a7f002..7601049 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -156,17 +156,8 @@ void OnixSourceEditor::buttonClicked(Button* b) } else { - if (!thread->setPortVoltage(PortName::PortA, 0)) - { - CoreServices::sendStatusMessage("Unable to set port voltage to 0 for Port A."); - return; - } - - if (!thread->setPortVoltage(PortName::PortB, 0)) - { - CoreServices::sendStatusMessage("Unable to set port voltage to 0 for Port B."); - return; - } + thread->setPortVoltage(PortName::PortA, 0); + thread->setPortVoltage(PortName::PortB, 0); canvas->removeTabs(); thread->disconnectDevices(true); diff --git a/Source/PortController.cpp b/Source/PortController.cpp deleted file mode 100644 index 104469c..0000000 --- a/Source/PortController.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - ------------------------------------------------------------------ - - This file is part of the Open Ephys GUI - Copyright (C) 2023 Allen Institute for Brain Science and 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 "PortController.h" - -PortController::PortController(PortName port_) : - port(port_) -{ -} - -PortController::~PortController() -{ -} - -void PortController::updateDiscoveryParameters(DiscoveryParameters parameters) -{ - discoveryParameters = parameters; -} - -DiscoveryParameters PortController::getHeadstageDiscoveryParameters(String headstage) -{ - if (headstage == "Neuropixels 1.0f") - { - return DiscoveryParameters(5.0f, 7.0f, 1.0f, 0.2f); - } - - return DiscoveryParameters(); -} - -bool PortController::configureVoltage(oni_ctx ctx, float voltage) const -{ - if (ctx == NULL) return false; - - if (voltage == defaultVoltage) - { - for (voltage = discoveryParameters.minVoltage; voltage <= discoveryParameters.maxVoltage; voltage += discoveryParameters.voltageIncrement) - { - setVoltage(ctx, voltage); - - if (checkLinkState(ctx) == 0) - { - setVoltage(ctx, voltage + discoveryParameters.voltageOffset); - return checkLinkState(ctx); - } - } - } - else - { - return setVoltage(ctx, voltage); - } -} - -bool PortController::setVoltage(oni_ctx ctx, float voltage) const -{ - if (ctx == NULL) return false; - - ONI_OK_RETURN_BOOL(oni_write_reg(ctx, (oni_dev_idx_t)port, voltageRegister, 0)); - - if (voltage == 0.0f) return true; - - sleep_for(std::chrono::milliseconds(300)); - - ONI_OK_RETURN_BOOL(oni_write_reg(ctx, (oni_dev_idx_t)port, voltageRegister, (oni_reg_val_t)(voltage * 10))); - - sleep_for(std::chrono::milliseconds(500)); - - auto val = 1; - ONI_OK_RETURN_BOOL(oni_set_opt(ctx, ONI_OPT_RESET, &val, sizeof(val))); - - sleep_for(std::chrono::milliseconds(200)); - - return true; -} - -bool PortController::checkLinkState(oni_ctx ctx) const -{ - if (ctx == NULL) return false; - - oni_reg_val_t linkState; - int result = oni_read_reg(ctx, (oni_dev_idx_t)port, linkStateRegister, &linkState); - - if (result != 0) { LOGE(oni_error_str(result)); return false; } - else if ((linkState & (uint32_t)0x1) == 0) { LOGE("Unable to acquire communication lock."); return false; } - else return true; -} diff --git a/Source/PortController.h b/Source/PortController.h deleted file mode 100644 index 91a10f4..0000000 --- a/Source/PortController.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - ------------------------------------------------------------------ - - This file is part of the Open Ephys GUI - Copyright (C) 2023 Allen Institute for Brain Science and 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 . - -*/ - -#ifndef __PORTCONTROLLER_H__ -#define __PORTCONTROLLER_H__ - -#include -#include - -#include "oni.h" -#include "OnixDevice.h" - -using namespace std::this_thread; - -struct DiscoveryParameters -{ - float minVoltage = 0.0f; - float maxVoltage = 0.0f; - float voltageOffset = 0.0f; - float voltageIncrement = 0.0f; - - DiscoveryParameters() {}; - - DiscoveryParameters(float minVoltage_, float maxVoltage_, float voltageOffset_, float voltageIncrement_) - { - minVoltage = minVoltage_; - maxVoltage = maxVoltage_; - voltageOffset = voltageOffset_; - voltageIncrement = voltageIncrement_; - } -}; - -class PortController -{ -public: - PortController(PortName port_); - - ~PortController(); - - void updateDiscoveryParameters(DiscoveryParameters parameters); - - bool configureVoltage(oni_ctx ctx, float voltage = defaultVoltage) const; - - bool setVoltage(oni_ctx ctx, float voltage) const; - - bool checkLinkState(oni_ctx ctx) const; - - static DiscoveryParameters getHeadstageDiscoveryParameters(String headstage); - -private: - const PortName port; - - static constexpr float defaultVoltage = -1.0f; - - const oni_reg_addr_t voltageRegister = 3; - const oni_reg_addr_t linkStateRegister = 5; - - DiscoveryParameters discoveryParameters; -}; - -#endif // !__PORTCONTROLLER_H__ From 9bbfae12419a3cfd64fff50478a30654a8493e8f Mon Sep 17 00:00:00 2001 From: bparks13 Date: Fri, 7 Mar 2025 17:06:10 -0500 Subject: [PATCH 2/7] Stop acquisition gracefully when port communication is lost - Correctly parses the data frame from a Port Controller - Run startAcquisition for the port control devices - Modified language when unable to acquire the communication lock --- Source/Devices/PortController.cpp | 22 ++++++++++------------ Source/Devices/PortController.h | 6 +++--- Source/OnixSource.cpp | 16 ++++++++-------- Source/OnixSourceEditor.cpp | 12 ++++++++++-- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp index 58bcffd..4f04e89 100644 --- a/Source/Devices/PortController.cpp +++ b/Source/Devices/PortController.cpp @@ -67,21 +67,19 @@ void PortController::processFrames() const GenericScopedLock frameLock(frameArray.getLock()); oni_frame_t* frame = frameArray.removeAndReturn(0); - int16_t* dataPtr = (int16_t*)frame->data; + int8_t* dataPtr = (int8_t*)frame->data; - int dataOffset = 4; + int dataOffset = 8; - PortStatusCode code = (PortStatusCode) *(int8_t*)(dataPtr + dataOffset); + uint32_t code = (uint32_t) *(dataPtr + dataOffset); + uint32_t data = (uint32_t) *(dataPtr + dataOffset + 1); - errorFlag = ((uint32_t)code & LINKSTATE_SL) == 0; + if (code & (uint32_t)PortStatusCode::SerdesLock) + errorFlag = ((uint32_t)data & LINKSTATE_SL) == 0; oni_destroy_frame(frame); - if (errorFlag) - { - LOGE("Port status changed and indicated an error occurred. Port status code is " + String((uint32_t)code)) - return; - } + LOGE("Port status changed for " + getName() + ". Port status code is " + String((uint32_t)code)); } } @@ -100,7 +98,7 @@ DiscoveryParameters PortController::getHeadstageDiscoveryParameters(String heads return DiscoveryParameters(); } -bool PortController::configureVoltage(float voltage) const +bool PortController::configureVoltage(float voltage) { if (ctx == NULL) return false; @@ -129,7 +127,7 @@ bool PortController::configureVoltage(float voltage) const return false; } -void PortController::setVoltageOverride(float voltage, bool waitToSettle) const +void PortController::setVoltageOverride(float voltage, bool waitToSettle) { if (ctx == NULL) return; @@ -137,7 +135,7 @@ void PortController::setVoltageOverride(float voltage, bool waitToSettle) const if (waitToSettle) sleep_for(std::chrono::milliseconds(500)); } -void PortController::setVoltage(float voltage) const +void PortController::setVoltage(float voltage) { if (ctx == NULL) return; diff --git a/Source/Devices/PortController.h b/Source/Devices/PortController.h index e4086ca..5adb9ea 100644 --- a/Source/Devices/PortController.h +++ b/Source/Devices/PortController.h @@ -102,13 +102,13 @@ class PortController : public OnixDevice void updateDiscoveryParameters(DiscoveryParameters parameters); - bool configureVoltage(float voltage = defaultVoltage) const; + bool configureVoltage(float voltage = defaultVoltage); /** Sets the voltage to the given value, after setting the voltage to zero */ - void setVoltage(float voltage) const; + void setVoltage(float voltage); /** Overrides the voltage setting and directly sets it to the given voltage */ - void setVoltageOverride(float voltage, bool waitToSettle = true) const; + void setVoltageOverride(float voltage, bool waitToSettle = true); bool checkLinkState() const; diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index fddbf56..8a97707 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -461,8 +461,6 @@ bool OnixSource::isReady() bool OnixSource::startAcquisition() { - startThread(); - frameReader.reset(); Array devices; @@ -475,16 +473,18 @@ bool OnixSource::startAcquisition() devices.add(portA.get()); devices.add(portB.get()); - frameReader = std::make_unique(devices, context.get()); - frameReader->startThread(); - - for (auto source : sources) + for (auto source : devices) { if (!source->isEnabled()) continue; source->startAcquisition(); } + frameReader = std::make_unique(devices, context.get()); + frameReader->startThread(); + + startThread(); + return true; } @@ -496,8 +496,8 @@ bool OnixSource::stopAcquisition() if (frameReader->isThreadRunning()) frameReader->signalThreadShouldExit(); - waitForThreadToExit(2000); - frameReader->waitForThreadToExit(1000); + if (!portA->getErrorFlag() && !portB->getErrorFlag()) + waitForThreadToExit(2000); if (devicesFound) { diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index 7601049..92c13b8 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -127,21 +127,29 @@ void OnixSourceEditor::buttonClicked(Button* b) { if (!thread->configurePortVoltage(PortName::PortA, portVoltageValueA->getText())) { - CoreServices::sendStatusMessage("Unable to set port voltage for Port A."); + CoreServices::sendStatusMessage("Unable to acquire communication lock on Port A."); connectButton->setToggleState(false, true); return; } } + else + { + thread->setPortVoltage(PortName::PortA, 0); + } if (isHeadstageSelected(PortName::PortB)) { if (!thread->configurePortVoltage(PortName::PortB, portVoltageValueB->getText())) { - CoreServices::sendStatusMessage("Unable to set port voltage for Port B."); + CoreServices::sendStatusMessage("Unable to acquire communication lock on Port B."); connectButton->setToggleState(false, true); return; } } + else + { + thread->setPortVoltage(PortName::PortB, 0); + } thread->initializeDevices(true); canvas->refreshTabs(); From b339d546b41b358b7fee857e27f9cf35e2a764cb Mon Sep 17 00:00:00 2001 From: bparks13 Date: Tue, 11 Mar 2025 09:32:58 -0400 Subject: [PATCH 3/7] Force hardware reconnect when communication lock is lost --- Source/OnixSource.cpp | 19 ++++++- Source/OnixSourceEditor.cpp | 109 +++++++++++++++++++++++------------- Source/OnixSourceEditor.h | 2 + 3 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 8a97707..9dd9af1 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -314,8 +314,6 @@ void OnixSource::setPortVoltage(PortName port, float voltage) const { if (!context.isInitialized()) return; - oni_ctx ctx = context.get(); - switch (port) { case PortName::PortA: @@ -514,6 +512,23 @@ bool OnixSource::stopAcquisition() oni_set_opt(context.get(), ONI_OPT_BLOCKREADSIZE, &block_read_size, sizeof(block_read_size)); } + if (portA->getErrorFlag() || portB->getErrorFlag()) + { + if (portA->getErrorFlag()) + { + LOGE("Port A lost communication lock. Reconnect hardware to continue."); + CoreServices::sendStatusMessage("Port A lost communication lock"); + } + + if (portB->getErrorFlag()) + { + LOGE("Port B lost communication lock. Reconnect hardware to continue."); + CoreServices::sendStatusMessage("Port B lost communication lock"); + } + + editor->updateConnectedStatus(false); + } + for (auto source : sources) { if (!source->isEnabled()) continue; diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index 92c13b8..e1393bc 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -111,8 +111,32 @@ OnixSourceEditor::OnixSourceEditor(GenericProcessor* parentNode, OnixSource* oni void OnixSourceEditor::labelTextChanged(Label* l) { + // TODO: Add headstage specific parameters to limit voltage within safe levels if (l == portVoltageValueA.get()) { + float input = l->getText().getFloatValue(); + + if (input < 0.0f) + { + l->setText("0.0", dontSendNotification); + } + else if (input > 7.0f) + { + l->setText("7.0", dontSendNotification); + } + } + else if (l == portVoltageValueB.get()) + { + float input = l->getText().getFloatValue(); + + if (input < 0.0f) + { + l->setText("0.0", dontSendNotification); + } + else if (input > 7.0f) + { + l->setText("7.0", dontSendNotification); + } } } @@ -120,58 +144,65 @@ void OnixSourceEditor::buttonClicked(Button* b) { if (b == connectButton.get()) { - if (connectButton->getToggleState() == true) + updateConnectedStatus(connectButton->getToggleState()); + } +} + +void OnixSourceEditor::updateConnectedStatus(bool connected) +{ + connectButton->setToggleState(connected, dontSendNotification); + + if (connected) + { + // NB: Configure port voltages, using either the automated voltage discovery algorithm, or the explicit voltage value given + if (isHeadstageSelected(PortName::PortA)) { - // NB: Configure port voltages, using either the automated voltage discovery algorithm, or the explicit voltage value given - if (isHeadstageSelected(PortName::PortA)) - { - if (!thread->configurePortVoltage(PortName::PortA, portVoltageValueA->getText())) - { - CoreServices::sendStatusMessage("Unable to acquire communication lock on Port A."); - connectButton->setToggleState(false, true); - return; - } - } - else + if (!thread->configurePortVoltage(PortName::PortA, portVoltageValueA->getText())) { - thread->setPortVoltage(PortName::PortA, 0); + CoreServices::sendStatusMessage("Unable to acquire communication lock on Port A."); + connectButton->setToggleState(false, dontSendNotification); + return; } - - if (isHeadstageSelected(PortName::PortB)) - { - if (!thread->configurePortVoltage(PortName::PortB, portVoltageValueB->getText())) - { - CoreServices::sendStatusMessage("Unable to acquire communication lock on Port B."); - connectButton->setToggleState(false, true); - return; - } - } - else - { - thread->setPortVoltage(PortName::PortB, 0); - } - - thread->initializeDevices(true); - canvas->refreshTabs(); - - connectButton->setLabel("DISCONNECT"); + } + else + { + thread->setPortVoltage(PortName::PortA, 0); + } - if (!thread->foundInputSource()) + if (isHeadstageSelected(PortName::PortB)) + { + if (!thread->configurePortVoltage(PortName::PortB, portVoltageValueB->getText())) { - CoreServices::sendStatusMessage("No Onix hardware found."); - connectButton->setToggleState(false, true); + CoreServices::sendStatusMessage("Unable to acquire communication lock on Port B."); + connectButton->setToggleState(false, dontSendNotification); + return; } } else { - thread->setPortVoltage(PortName::PortA, 0); thread->setPortVoltage(PortName::PortB, 0); + } + + thread->initializeDevices(true); + canvas->refreshTabs(); - canvas->removeTabs(); - thread->disconnectDevices(true); - connectButton->setLabel("CONNECT"); + connectButton->setLabel("DISCONNECT"); + + if (!thread->foundInputSource()) + { + CoreServices::sendStatusMessage("No Onix hardware found."); + connectButton->setToggleState(false, sendNotification); } } + else + { + thread->setPortVoltage(PortName::PortA, 0); + thread->setPortVoltage(PortName::PortB, 0); + + canvas->removeTabs(); + thread->disconnectDevices(true); + connectButton->setLabel("CONNECT"); + } } void OnixSourceEditor::comboBoxChanged(ComboBox* cb) diff --git a/Source/OnixSourceEditor.h b/Source/OnixSourceEditor.h index e5c6dc4..8c0a8a3 100644 --- a/Source/OnixSourceEditor.h +++ b/Source/OnixSourceEditor.h @@ -76,6 +76,8 @@ class OnixSourceEditor : public VisualizerEditor, bool isHeadstageSelected(PortName port); + void updateConnectedStatus(bool); + OnixSourceCanvas* canvas; private: From aae87d944eeccf4d46a5b1b177c086981b572208 Mon Sep 17 00:00:00 2001 From: bparks13 Date: Tue, 11 Mar 2025 12:17:15 -0400 Subject: [PATCH 4/7] Fix merge conflicts - Rename enableDevice to configureDevice - Only update Neuropixels settings when the device is enabled --- Source/Devices/Neuropixels_1.cpp | 5 ++++- Source/Devices/PortController.cpp | 2 +- Source/Devices/PortController.h | 2 +- Source/OnixSource.cpp | 4 ++-- Source/UI/Bno055Interface.cpp | 2 +- Source/UI/NeuropixV1Interface.cpp | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Source/Devices/Neuropixels_1.cpp b/Source/Devices/Neuropixels_1.cpp index dbb201c..fd0a9d3 100644 --- a/Source/Devices/Neuropixels_1.cpp +++ b/Source/Devices/Neuropixels_1.cpp @@ -36,7 +36,10 @@ BackgroundUpdaterWithProgressWindow::~BackgroundUpdaterWithProgressWindow() int BackgroundUpdaterWithProgressWindow::updateSettings() { - runThread(); + if (device->isEnabled()) + runThread(); + else + return 0; return result; } diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp index 4f04e89..5b5e94f 100644 --- a/Source/Devices/PortController.cpp +++ b/Source/Devices/PortController.cpp @@ -33,7 +33,7 @@ PortController::~PortController() { } -int PortController::enableDevice() +int PortController::configureDevice() { ONI_OK_RETURN_INT(oni_write_reg(ctx, deviceIdx, (uint32_t)PortControllerRegister::ENABLE, (uint32_t)1)) diff --git a/Source/Devices/PortController.h b/Source/Devices/PortController.h index 5adb9ea..8e3a561 100644 --- a/Source/Devices/PortController.h +++ b/Source/Devices/PortController.h @@ -86,7 +86,7 @@ class PortController : public OnixDevice ~PortController(); - int enableDevice() override; + int configureDevice() override; int updateSettings() override { return 0; } diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 4c72110..89f0039 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -224,8 +224,8 @@ void OnixSource::initializeDevices(bool updateStreamInfo) } } - portA->enableDevice(); - portB->enableDevice(); + portA->configureDevice(); + portB->configureDevice(); context.issueReset(); diff --git a/Source/UI/Bno055Interface.cpp b/Source/UI/Bno055Interface.cpp index 1187278..9d3ee83 100644 --- a/Source/UI/Bno055Interface.cpp +++ b/Source/UI/Bno055Interface.cpp @@ -48,7 +48,7 @@ void Bno055Interface::buttonClicked(Button* button) if (button == deviceEnableButton.get()) { device->setEnabled(deviceEnableButton->getToggleState()); - device->enableDevice(); + device->configureDevice(); canvas->resetContext(); if (device->isEnabled()) diff --git a/Source/UI/NeuropixV1Interface.cpp b/Source/UI/NeuropixV1Interface.cpp index 8694aa9..df62553 100644 --- a/Source/UI/NeuropixV1Interface.cpp +++ b/Source/UI/NeuropixV1Interface.cpp @@ -583,7 +583,7 @@ void NeuropixV1Interface::buttonClicked(Button* button) if (button == probeEnableButton.get()) { device->setEnabled(probeEnableButton->getToggleState()); - device->enableDevice(); + device->configureDevice(); canvas->resetContext(); if (device->isEnabled()) From af28825d86b35b1144ac8080583911ea84c1fc63 Mon Sep 17 00:00:00 2001 From: bparks13 Date: Tue, 11 Mar 2025 13:06:25 -0400 Subject: [PATCH 5/7] Alert user that lock was lost, and instruct how to proceed - Upon testing, the previous configuration could delete buffers before they were cleared, leading to invalid behavior. Now, the plugin reports that it cannot record so the user cannot start acquisition again without disconnecting/reconnecting the hardware --- Source/OnixSource.cpp | 31 +++++++++++++++++-------------- Source/OnixSourceEditor.cpp | 4 ++-- Source/OnixSourceEditor.h | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 89f0039..8dd4d20 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -67,7 +67,7 @@ void OnixSource::initializeDevices(bool updateStreamInfo) { if (!context.isInitialized()) { - LOGE("Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); + LOGE("Cannot initialize devices, context is not initialized correctly. Please try removing the plugin and adding it again."); return; } @@ -505,6 +505,19 @@ bool OnixSource::stopAcquisition() context.issueReset(); } + for (auto source : sources) + { + if (!source->isEnabled()) continue; + + source->stopAcquisition(); + } + + portA->stopAcquisition(); + portB->stopAcquisition(); + + for (auto buffers : sourceBuffers) + buffers->clear(); + if (portA->getErrorFlag() || portB->getErrorFlag()) { if (portA->getErrorFlag()) @@ -519,22 +532,12 @@ bool OnixSource::stopAcquisition() CoreServices::sendStatusMessage("Port B lost communication lock"); } - editor->updateConnectedStatus(false); - } - - for (auto source : sources) - { - if (!source->isEnabled()) continue; + devicesFound = false; - source->stopAcquisition(); + MessageManager::callAsync([] { AlertWindow::showMessageBoxAsync(MessageBoxIconType::WarningIcon, "Port Communication Lock Lost", + "The port communication lock was lost during acquisition. To continue, please disconnect and reconnect the hardware.", "Okay"); }); } - portA->stopAcquisition(); - portB->stopAcquisition(); - - for (auto buffers : sourceBuffers) - buffers->clear(); - return true; } diff --git a/Source/OnixSourceEditor.cpp b/Source/OnixSourceEditor.cpp index bb95927..fd82d0b 100644 --- a/Source/OnixSourceEditor.cpp +++ b/Source/OnixSourceEditor.cpp @@ -144,11 +144,11 @@ void OnixSourceEditor::buttonClicked(Button* b) { if (b == connectButton.get()) { - updateConnectedStatus(connectButton->getToggleState()); + setConnectedStatus(connectButton->getToggleState()); } } -void OnixSourceEditor::updateConnectedStatus(bool connected) +void OnixSourceEditor::setConnectedStatus(bool connected) { connectButton->setToggleState(connected, dontSendNotification); diff --git a/Source/OnixSourceEditor.h b/Source/OnixSourceEditor.h index 4a09f39..383db5a 100644 --- a/Source/OnixSourceEditor.h +++ b/Source/OnixSourceEditor.h @@ -76,7 +76,7 @@ class OnixSourceEditor : public VisualizerEditor, bool isHeadstageSelected(PortName port); - void updateConnectedStatus(bool); + void setConnectedStatus(bool); OnixSourceCanvas* canvas; From 4039b90213bdca69d41d6947316947c382860fc1 Mon Sep 17 00:00:00 2001 From: bparks13 Date: Mon, 17 Mar 2025 11:53:00 -0400 Subject: [PATCH 6/7] Modify error message when lock is lost --- Source/OnixSource.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/OnixSource.cpp b/Source/OnixSource.cpp index 8dd4d20..e0d84ef 100644 --- a/Source/OnixSource.cpp +++ b/Source/OnixSource.cpp @@ -535,7 +535,8 @@ bool OnixSource::stopAcquisition() devicesFound = false; MessageManager::callAsync([] { AlertWindow::showMessageBoxAsync(MessageBoxIconType::WarningIcon, "Port Communication Lock Lost", - "The port communication lock was lost during acquisition. To continue, please disconnect and reconnect the hardware.", "Okay"); }); + "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"); }); } return true; From ab6a682a7494e5b70997435deb63a4bc9bc06bee Mon Sep 17 00:00:00 2001 From: bparks13 Date: Mon, 17 Mar 2025 13:55:16 -0400 Subject: [PATCH 7/7] Check SerdesLock bit directly instead of relying on PortStatusCode --- Source/Devices/PortController.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Devices/PortController.cpp b/Source/Devices/PortController.cpp index 5b5e94f..4794a39 100644 --- a/Source/Devices/PortController.cpp +++ b/Source/Devices/PortController.cpp @@ -74,12 +74,11 @@ void PortController::processFrames() uint32_t code = (uint32_t) *(dataPtr + dataOffset); uint32_t data = (uint32_t) *(dataPtr + dataOffset + 1); - if (code & (uint32_t)PortStatusCode::SerdesLock) - errorFlag = ((uint32_t)data & LINKSTATE_SL) == 0; + errorFlag = errorFlag || ((uint32_t)data & LINKSTATE_SL) == 0; oni_destroy_frame(frame); - LOGE("Port status changed for " + getName() + ". Port status code is " + String((uint32_t)code)); + LOGE("Port status changed for " + getName() + "."); } }