From 245061806ca25e213e28987dc37e829e35f42ba7 Mon Sep 17 00:00:00 2001 From: medengineer Date: Wed, 29 Nov 2023 20:12:01 -0800 Subject: [PATCH] Add PLUGIN_API OpenEphysAction --- .../SpikeDetector/SpikeDetector.cpp | 17 +++-- .../SpikeDetector/SpikeDetector.h | 4 +- .../SpikeDetector/SpikeDetectorActions.cpp | 76 +++++++++++++++---- .../SpikeDetector/SpikeDetectorActions.h | 21 +++-- .../SpikeDetector/SpikeDetectorEditor.cpp | 23 +++++- .../SpikeDetector/SpikeDetectorEditor.h | 3 + Source/Processors/Actions/CMakeLists.txt | 7 ++ Source/Processors/Actions/OpenEphysAction.cpp | 4 + Source/Processors/Actions/OpenEphysAction.h | 46 +++++++++++ .../GenericProcessor/GenericProcessor.cpp | 2 + .../GenericProcessor/GenericProcessor.h | 10 +++ .../ProcessorGraph/ProcessorGraph.cpp | 15 ++++ .../ProcessorGraph/ProcessorGraph.h | 7 ++ .../ProcessorGraph/ProcessorGraphActions.cpp | 8 +- 14 files changed, 207 insertions(+), 36 deletions(-) create mode 100644 Source/Processors/Actions/CMakeLists.txt create mode 100644 Source/Processors/Actions/OpenEphysAction.cpp create mode 100644 Source/Processors/Actions/OpenEphysAction.h diff --git a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp index 1af407045..dd2036675 100644 --- a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp +++ b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp @@ -396,7 +396,7 @@ SpikeChannel* SpikeDetector::addSpikeChannel (SpikeChannel::Type type, String name, int index) { - + Array selectedChannels; Array localChannels; @@ -579,12 +579,17 @@ SpikeChannel* SpikeDetector::addSpikeChannel (SpikeChannel::Type type, } -void SpikeDetector::removeSpikeChannel (SpikeChannel* spikeChannel) +void SpikeDetector::removeSpikeChannel(String spikeChannelKey) { - - LOGD("Removing spike channel: ", spikeChannel->getName(), " from stream ", spikeChannel->getStreamId()); - - spikeChannels.removeObject(spikeChannel); + for (int i = 0; i < spikeChannels.size(); i++) + { + if (spikeChannels[i]->getIdentifier() == spikeChannelKey) + { + LOGD("Removing spike channel: ", spikeChannels[i]->getName(), " from stream ", spikeChannels[i]->getStreamId()); + spikeChannels.removeObject(spikeChannels[i]); + break; + } + } //Reset electrode and channel counters if no more spike channels after this delete if (!spikeChannels.size()) diff --git a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.h b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.h index 87dae9620..757fec545 100644 --- a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.h +++ b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.h @@ -232,8 +232,8 @@ class SpikeDetector : public GenericProcessor String name = "", int index = -1); - /** Removes a spike channel, based on a SpikeChannel pointer. */ - void removeSpikeChannel (SpikeChannel*); + /** Removes a spike channel, based on a SpikeChannel identifier. */ + void removeSpikeChannel (String); // ===================================================================== /** Get array of local SpikeChannel objects for a given dataStream*/ diff --git a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.cpp b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.cpp index 15d3563bb..6b3426eba 100644 --- a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.cpp +++ b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.cpp @@ -28,10 +28,11 @@ AddSpikeChannels::AddSpikeChannels(SpikeDetector* processor_, DataStream* stream_, SpikeChannel::Type type_, - int count_, //adds multiple channels atonce + int count_, //adds multiple channels at once Array startChannels_) : - processor(processor_), - streamId(stream_->getStreamId()), + OpenEphysAction("AddSpikeChannels"), + spikeDetector(processor_), + streamKey(stream_->getKey()), type(type_), count(count_), startChannels(startChannels_) @@ -54,33 +55,68 @@ bool AddSpikeChannels::perform() if (i < startChannels.size()) startChannel = startChannels[i]; - processor->addSpikeChannel(type, streamId, startChannel); + //TODO: Should add a convenience function for this + uint16 streamId = 0; + for (auto stream : spikeDetector->getDataStreams()) + { + if (stream->getKey() == streamKey) + { + streamId = stream->getStreamId(); + break; + } + } + if (streamId == 0) return false; + + //TODO: Pass stream name instead of streamId once this is working + SpikeChannel* spikeChannel = spikeDetector->addSpikeChannel(type, streamId, startChannel); + addedSpikeChannels.add(spikeChannel->getIdentifier()); } + spikeDetector->registerUndoableAction(spikeDetector->getNodeId(), this); + CoreServices::updateSignalChain(spikeDetector); + return true; } bool AddSpikeChannels::undo() { + uint16 streamId = 0; + for (auto stream : spikeDetector->getDataStreams()) + { + if (stream->getKey() == streamKey) + { + streamId = stream->getStreamId(); + break; + } + } + if (streamId == 0) return false; for (int i = 0; i < count; i++) - processor->removeSpikeChannel(processor->getSpikeChannelsForStream(streamId).getLast()); + spikeDetector->removeSpikeChannel(addedSpikeChannels[i]); + CoreServices::updateSignalChain(spikeDetector); return true; } +void AddSpikeChannels::restoreOwner(GenericProcessor* owner) +{ + LOGD("RESTORING OWNER FOR: AddSpikeChannels"); + spikeDetector = (SpikeDetector*)owner; +} + RemoveSpikeChannels::RemoveSpikeChannels(SpikeDetector* processor_, DataStream* stream_, Array spikeChannelsToRemove_, Array indeces_) : - processor(processor_), - spikeChannelsToRemove(spikeChannelsToRemove_), + OpenEphysAction("RemoveSpikeChannels"), + spikeDetector(processor_), indeces(indeces_), - streamId(stream_->getStreamId()) + streamKey(stream_->getKey()) { settings = std::make_unique("SPIKE_CHANNELS"); - for (auto spikeChannel : spikeChannelsToRemove) + for (auto spikeChannel : spikeChannelsToRemove_) { if (spikeChannel->isLocal()) { + LOGD("REMOVING SPIKE CHANNEL: ", spikeChannel->getName()); XmlElement* spikeParamsXml = settings->createNewChildElement("SPIKE_CHANNEL"); @@ -106,6 +142,8 @@ RemoveSpikeChannels::RemoveSpikeChannels(SpikeDetector* processor_, } spikeChannel->getParameter("waveform_type")->toXml(spikeParamsXml); + + spikeChannelsToRemove.add(spikeChannel->getIdentifier()); } } } @@ -114,10 +152,18 @@ RemoveSpikeChannels::~RemoveSpikeChannels() { } +void RemoveSpikeChannels::restoreOwner(GenericProcessor* processor) +{ + spikeDetector = (SpikeDetector*)processor; +} + bool RemoveSpikeChannels::perform() { for (auto spikeChannel : spikeChannelsToRemove) - processor->removeSpikeChannel(spikeChannel); + spikeDetector->removeSpikeChannel(spikeChannel); + spikeDetector->registerUndoableAction(spikeDetector->getNodeId(), this); + CoreServices::updateSignalChain(spikeDetector); + return true; } @@ -128,19 +174,17 @@ bool RemoveSpikeChannels::undo() { String name = spikeParamsXml->getStringAttribute("name", ""); - //std::cout << "SPIKE CHANNEL NAME: " << name << std::endl; - double sample_rate = spikeParamsXml->getDoubleAttribute("sample_rate", 0.0f); String stream_name = spikeParamsXml->getStringAttribute("stream_name", ""); int stream_source = spikeParamsXml->getIntAttribute("stream_source", 0); SpikeChannel::Type type = SpikeChannel::typeFromNumChannels(spikeParamsXml->getIntAttribute("num_channels", 1)); - if (!processor->alreadyLoaded(name, type, stream_source, stream_name)) + if (!spikeDetector->alreadyLoaded(name, type, stream_source, stream_name)) { - uint16 streamId = processor->findSimilarStream(stream_source, stream_name, sample_rate, true); + uint16 streamId = spikeDetector->findSimilarStream(stream_source, stream_name, sample_rate, true); - SpikeChannel* spikeChannel = processor->addSpikeChannel(type, streamId, -1, name, indeces[idx]); + SpikeChannel* spikeChannel = spikeDetector->addSpikeChannel(type, streamId, -1, name, indeces[idx]); spikeChannel->getParameter("local_channels")->fromXml(spikeParamsXml); @@ -160,6 +204,6 @@ bool RemoveSpikeChannels::undo() } idx++; } - CoreServices::updateSignalChain(processor->getEditor()); + CoreServices::updateSignalChain(spikeDetector); return true; } diff --git a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.h b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.h index c9cb344e9..de22fe803 100644 --- a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.h +++ b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorActions.h @@ -27,6 +27,7 @@ #include #include "SpikeDetector.h" +#include "SpikeDetectorEditor.h" /** Adds a spike channel to the spike detector, @@ -35,7 +36,7 @@ Undo: removes the spike channel from the spike detector. */ -class AddSpikeChannels : public UndoableAction +class AddSpikeChannels : public OpenEphysAction { public: @@ -50,6 +51,8 @@ class AddSpikeChannels : public UndoableAction /** Destructor */ ~AddSpikeChannels(); + void restoreOwner(GenericProcessor* processor) override; + /** Perform the action*/ bool perform(); @@ -60,16 +63,18 @@ class AddSpikeChannels : public UndoableAction private: - SpikeDetector* processor; - uint16 streamId; + SpikeDetector* spikeDetector; + String streamKey; SpikeChannel::Type type; Array startChannels; + Array addedSpikeChannels; + int count; }; -class RemoveSpikeChannels : public UndoableAction +class RemoveSpikeChannels : public OpenEphysAction { public: @@ -83,6 +88,8 @@ class RemoveSpikeChannels : public UndoableAction /** Destructor */ ~RemoveSpikeChannels(); + void restoreOwner(GenericProcessor* processor) override; + /** Perform the action*/ bool perform(); @@ -93,9 +100,9 @@ class RemoveSpikeChannels : public UndoableAction private: - SpikeDetector* processor; - uint16 streamId; - Array spikeChannelsToRemove; + SpikeDetector* spikeDetector; + String streamKey; + Array spikeChannelsToRemove; Array removedSpikeChannels; Array indeces; diff --git a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.cpp b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.cpp index e914cf969..09b44daf1 100644 --- a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.cpp +++ b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.cpp @@ -68,6 +68,8 @@ void SpikeDetectorEditor::buttonClicked(Button* button) spikeChannels, acquisitionIsActive); + currentConfigWindow->update(processor->getSpikeChannelsForStream(getCurrentStream())); + CallOutBox& myBox = CallOutBox::launchAsynchronously(std::unique_ptr(currentConfigWindow), button->getScreenBounds(), @@ -80,18 +82,30 @@ void SpikeDetectorEditor::buttonClicked(Button* button) } +void SpikeDetectorEditor::updateConfigurationWindow() +{ + if (currentConfigWindow != nullptr) + { + SpikeDetector* processor = (SpikeDetector*)getProcessor(); + currentConfigWindow->update(processor->getSpikeChannelsForStream(getCurrentStream())); + } +} + void SpikeDetectorEditor::addSpikeChannels(PopupConfigurationWindow* window, SpikeChannel::Type type, int count, Array startChannels) { SpikeDetector* processor = (SpikeDetector*)getProcessor(); + LOGD("** ADDING TO CURRENT STREAM: ", getCurrentStream()); + DataStream* stream = processor->getDataStream(getCurrentStream()); AddSpikeChannels* action = new AddSpikeChannels(processor, stream, type, count, startChannels); CoreServices::getUndoManager()->beginNewTransaction(); - CoreServices::getUndoManager()->perform(action); + CoreServices::getUndoManager()->perform((UndoableAction*)action); - CoreServices::updateSignalChain(this); + // Now called in perform + //CoreServices::updateSignalChain(this); if (window != nullptr) window->update(processor->getSpikeChannelsForStream(getCurrentStream())); @@ -109,9 +123,10 @@ void SpikeDetectorEditor::removeSpikeChannels(PopupConfigurationWindow* window, RemoveSpikeChannels* action = new RemoveSpikeChannels(processor, stream, spikeChannelsToRemove, indeces); CoreServices::getUndoManager()->beginNewTransaction(); - CoreServices::getUndoManager()->perform(action); + CoreServices::getUndoManager()->perform((UndoableAction*)action); - CoreServices::updateSignalChain(this); + // Now called in perform + //CoreServices::updateSignalChain(this); if (window != nullptr) window->update(processor->getSpikeChannelsForStream(getCurrentStream())); diff --git a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.h b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.h index 19d69d1f9..e68eff1c4 100644 --- a/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.h +++ b/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetectorEditor.h @@ -68,6 +68,9 @@ class SpikeDetectorEditor : public GenericEditor, /** Called by PopupConfigurationWindow*/ int getNumChannelsForCurrentStream(); + /** Update configuration window */ + void updateConfigurationWindow(); + private: std::unique_ptr configureButton; diff --git a/Source/Processors/Actions/CMakeLists.txt b/Source/Processors/Actions/CMakeLists.txt new file mode 100644 index 000000000..1bdb8d709 --- /dev/null +++ b/Source/Processors/Actions/CMakeLists.txt @@ -0,0 +1,7 @@ +#Open Ephys GUI direcroty-specific file + +#add files in this folder +add_sources(open-ephys + OpenEphysAction.cpp + OpenEphysAction.h +) \ No newline at end of file diff --git a/Source/Processors/Actions/OpenEphysAction.cpp b/Source/Processors/Actions/OpenEphysAction.cpp new file mode 100644 index 000000000..fc83de11a --- /dev/null +++ b/Source/Processors/Actions/OpenEphysAction.cpp @@ -0,0 +1,4 @@ +#include "OpenEphysAction.h" + +// Definition of the static map +std::map> OpenEphysAction::actionsByKey; \ No newline at end of file diff --git a/Source/Processors/Actions/OpenEphysAction.h b/Source/Processors/Actions/OpenEphysAction.h new file mode 100644 index 000000000..365e086f5 --- /dev/null +++ b/Source/Processors/Actions/OpenEphysAction.h @@ -0,0 +1,46 @@ +#include + +#include +#include +#include + +#ifndef OPENEPHYSACTION_H_INCLUDED +#define OPENEPHYSACTION_H_INCLUDED + +class PLUGIN_API OpenEphysAction : public UndoableAction { +public: + //OpenEphysAction(GenericProcessor* p) {}; + OpenEphysAction(const std::string& actionKey) : key(actionKey) { + // Add this action to the static map + //actionsByKey[key].push_back(this); + } + + // Implement the UndoableAction methods + bool perform() override = 0; + + // Implement the UndoableAction methods + bool undo() override = 0; + + // Example static method to access actions by key + /* + static std::vector& getActionsByKey(const std::string& actionKey) { + return actionsByKey[actionKey]; + } + */ + virtual void restoreOwner(GenericProcessor* p) = 0; + + String getIdentifier() { + return key; + } + +private: + + GenericProcessor* processor; + //std::string key; // Private member to store the action key + + // Static map to store actions by keys + //static std::map> actionsByKey; + std::string key; +}; + +#endif // OPENEPHYSACTION_H_INCLUDED \ No newline at end of file diff --git a/Source/Processors/GenericProcessor/GenericProcessor.cpp b/Source/Processors/GenericProcessor/GenericProcessor.cpp index a88a1a74b..c5d7419fe 100755 --- a/Source/Processors/GenericProcessor/GenericProcessor.cpp +++ b/Source/Processors/GenericProcessor/GenericProcessor.cpp @@ -48,6 +48,8 @@ const String GenericProcessor::m_unusedNameString("xxx-UNUSED-OPEN-EPHYS-xxx"); +std::map> GenericProcessor::undoableActions; + GenericProcessor::GenericProcessor(const String& name, bool headlessMode_) : GenericProcessorBase(name) , ParameterOwner(ParameterOwner::Type::OTHER) diff --git a/Source/Processors/GenericProcessor/GenericProcessor.h b/Source/Processors/GenericProcessor/GenericProcessor.h index c115b64c8..74fd7cab9 100755 --- a/Source/Processors/GenericProcessor/GenericProcessor.h +++ b/Source/Processors/GenericProcessor/GenericProcessor.h @@ -44,6 +44,8 @@ #include "../Events/Event.h" #include "../Events/Spike.h" +#include "../Actions/OpenEphysAction.h" + #include #include #include @@ -542,8 +544,16 @@ class PLUGIN_API GenericProcessor : public GenericProcessorBase /** Sets whether the processor is operating in headless mode */ void setHeadlessMode(bool mode) { headlessMode = mode; } + /** Registers a custom undoable action associated with this processor */ + static void registerUndoableAction(int nodeId, OpenEphysAction* action) { undoableActions[nodeId].push_back(action); } + + /** Returns a list of undoable actions for a given processor ID */ + static std::vector getUndoableActions(int nodeId) { return undoableActions[nodeId]; } + protected: + static std::map> undoableActions; + // -------------------------------------------- // SAMPLES + TIMESTAMPS // -------------------------------------------- diff --git a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp index 780550993..c3f1bf28b 100644 --- a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp +++ b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp @@ -1089,6 +1089,21 @@ void ProcessorGraph::restoreParameters() } +std::vector ProcessorGraph::getUndoableActions(int nodeId) +{ + return GenericProcessor::getUndoableActions(nodeId); +} + +void ProcessorGraph::updateUndoableActions(int nodeId) +{ + for (auto action : getUndoableActions(nodeId)) + { + GenericProcessor* p = getProcessorWithNodeId(nodeId); + p->update(); + action->restoreOwner(p); + } +} + bool ProcessorGraph::allRecordNodesAreSynchronized() { Array processors = getListOfProcessors(); diff --git a/Source/Processors/ProcessorGraph/ProcessorGraph.h b/Source/Processors/ProcessorGraph/ProcessorGraph.h index f0f1d5326..99d0a67a7 100644 --- a/Source/Processors/ProcessorGraph/ProcessorGraph.h +++ b/Source/Processors/ProcessorGraph/ProcessorGraph.h @@ -34,6 +34,7 @@ class AudioNode; class MessageCenter; class SignalChainTabButton; class PluginManager; +class OpenEphysAction; struct ChannelKey { int inputNodeId; int inputIndex; @@ -165,6 +166,12 @@ class TESTABLE ProcessorGraph : public AudioProcessorGraph /** Loops through processors and restores parameters, if they're available. */ void restoreParameters(); + + /** Returns a list of all undoable actions for a particular processor ID */ + std::vector getUndoableActions(int nodeId); + + /** Loops through any undoable actions for the processor ID and restores the pointer to the processor */ + void updateUndoableActions(int nodeId); /* Updates the buffer size used for the process() callbacks*/ void updateBufferSize(); diff --git a/Source/Processors/ProcessorGraph/ProcessorGraphActions.cpp b/Source/Processors/ProcessorGraph/ProcessorGraphActions.cpp index 45635e97f..9bea63008 100644 --- a/Source/Processors/ProcessorGraph/ProcessorGraphActions.cpp +++ b/Source/Processors/ProcessorGraph/ProcessorGraphActions.cpp @@ -87,6 +87,7 @@ bool AddProcessor::perform() if (processor != nullptr && !signalChainIsLoading) processor->initialize(false); + } if (processor != nullptr) @@ -99,6 +100,8 @@ bool AddProcessor::perform() mergerPath = merger->getPath(); } + processorGraph->updateUndoableActions(nodeId); + return true; } else @@ -263,7 +266,7 @@ bool DeleteProcessor::undo() processor = processorGraph->createProcessor(description, sourceProcessor, destProcessor, - true); + false); processor->parametersAsXml = settings.get(); if(processor->isMerger()) @@ -277,7 +280,10 @@ bool DeleteProcessor::undo() processorGraph->updateSettings(processor); if (processor != nullptr) + { + processorGraph->updateUndoableActions(nodeId); return true; + } else return false; }