Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure classes and workflow #84

Merged
merged 13 commits into from
Sep 3, 2024
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ add_library(
src/Channel.cpp
src/hdf5/HDF5IO.cpp
src/nwb/NWBFile.cpp
src/nwb/NWBRecording.cpp
src/nwb/RecordingContainers.cpp
src/nwb/base/TimeSeries.cpp
src/nwb/device/Device.cpp
src/nwb/ecephys/ElectricalSeries.cpp
Expand Down
1 change: 1 addition & 0 deletions docs/pages/1_userdocs.dox
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
* with a particular data acquisition software via AqNWB.
*
* - \subpage user_install_page
* - \subpage workflow
* - \subpage hdf5io
*/
102 changes: 102 additions & 0 deletions docs/pages/userdocs/workflow.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* \page workflow AqNWB Workflow
*
* \tableofcontents
*
* \section recording_workflow Overview of a recording workflow
*
* For users wanting to integrate NWB with a particular data acquisition software, here
* we outline the steps for a single recording from file creation to saving.
*
* 1. Create the I/O object (e.g,. \ref AQNWB::HDF5::HDF5IO "HDF5IO") used for
* writing data to the file on disk.
* 2. Create the \ref AQNWB::NWB::RecordingContainers "RecordingContainers" object
* used for managing \ref AQNWB::NWB::Container "Container" objects for storing recordings.
* 3. Create the \ref AQNWB::NWB::NWBFile "NWBFile" object used for managing and creating NWB
* file contents.
* 4. Create the \ref AQNWB::NWB::Container "Container" objects (e.g.,
* \ref AQNWB::NWB::ElectricalSeries "ElectricalSeries") used for recording and add them
* to the \ref AQNWB::NWB::RecordingContainers "RecordingContainers".
* 5. Start the recording.
* 6. Write data.
* 7. Stop the recording and close the \ref AQNWB::NWB::NWBFile "NWBFile".
*
* Below, we walk through these steps in more detail.
*
*
* \subsection create_io 1. Create the I/O object.
*
* First, create an I/O object (e.g., \ref AQNWB::HDF5::HDF5IO "HDF5IO") used for writing
* data to the file. AqNWB provides the convenience method, \ref AQNWB::createIO "createIO"
* to create this object using one of the supported backends. For more fine-grained
* control of different backend parameters, you can create your own `std::shared_ptr`
* using any of the derived \ref AQNWB::BaseIO "BaseIO" classes.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_io_snippet
*
*
* \subsection create_recording_container 2. Create the RecordingContainer object.
*
* Next, create a \ref AQNWB::NWB::RecordingContainers "RecordingContainers" object to manage the
* different \ref AQNWB::NWB::Container "Container" objects with the datasets that you would
* like to write data to.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_recording_containers_snippet
*
*
* \subsection create_nwbfile 3. Create the NWBFile
*
* Next, constructs the \ref AQNWB::NWB::NWBFile "NWBFile" object, using the I/O object as an input.
* Then, initialize the object to create the basic file structure of the NWBFile.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_nwbfile_snippet
*
*
* \subsection create_datasets 4. Create datasets and add to RecordingContainers.
*
* Next, create the different data types (e.g. \ref AQNWB::NWB::ElectricalSeries "ElectricalSeries"
* or other AQNWB::NWB::TimeSeries "TimeSeries") that you would like to write data into. After
* creation, these objects are added to the \ref AQNWB::NWB::RecordingContainers "RecordingContainers"
* object so that it can mana ge access and data writing during the recording process.
* When adding containers, ownership of the \ref AQNWB::NWB::Container "Container" is transferred to the
* \ref AQNWB::NWB::RecordingContainers "RecordingContainers" object, so that we can access it again via
* its index. New containers will always be appended to the end of the
* \ref AQNWB::NWB::RecordingContainers::containers "containers" object and their index can be tracked
* using the size of the input `recordingArrays`.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_datasets_snippet
*
*
stephprince marked this conversation as resolved.
Show resolved Hide resolved
* \subsection start_recording 5. Start the recording.
*
* Then, start the recording process with a call to the ``startRecording`` function of the I/O object.
*
* \note
* When using \ref AQNWB::HDF5::HDF5IO "HDF5IO" for writing to HDF5, calling
* \ref AQNWB::HDF5::HDF5IO::startRecording "startRecording" will by default enable
* \ref hdf5io_swmr "SWMR mode" to ensure file integrity and support concurrent read.
* As a result, no additional datasets or groups can be added to the file once a recording
* has been started unless the file is is closed and reopened.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_start_snippet
*
*
* \subsection write_data 6. Write data.
*
* During the recording process, use the \ref AQNWB::NWB::RecordingContainers "RecordingContainers"
* as an interface to access the various \ref AQNWB::NWB::Container "Container" object and corresponding
* datasets and write blocks of data to the file. Calling `flush()` on the I/O object at any time will
* ensure the data is moved to disk.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_write_snippet
*
*
* \subsection stop_recording 7. Stop the recording and finalize the file.
*
* When the recording process is finished, call `stopRecording` from the I/O object
* to flush any data and close the file.
*
* \snippet tests/examples/testWorkflowExamples.cpp example_workflow_stop_snippet
*
*
*/
4 changes: 2 additions & 2 deletions src/Utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ inline std::string getCurrentTime()
* @brief Factory method to create an IO object.
* @return A pointer to a BaseIO object
*/
inline std::unique_ptr<BaseIO> createIO(const std::string& type,
inline std::shared_ptr<BaseIO> createIO(const std::string& type,
const std::string& filename)
{
if (type == "HDF5") {
return std::make_unique<HDF5::HDF5IO>(filename);
return std::make_shared<HDF5::HDF5IO>(filename);
} else {
throw std::invalid_argument("Invalid IO type");
}
Expand Down
43 changes: 7 additions & 36 deletions src/nwb/NWBFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ using namespace AQNWB::NWB;

constexpr SizeType CHUNK_XSIZE = 2048;

std::vector<SizeType> NWBFile::emptyContainerIndexes = {};

// NWBFile

NWBFile::NWBFile(const std::string& idText, std::shared_ptr<BaseIO> io)
Expand All @@ -45,7 +47,6 @@ Status NWBFile::initialize()

Status NWBFile::finalize()
{
recordingContainers.reset();
return io->close();
}

Expand Down Expand Up @@ -93,7 +94,9 @@ Status NWBFile::createFileStructure()

Status NWBFile::createElectricalSeries(
std::vector<Types::ChannelVector> recordingArrays,
const BaseDataType& dataType)
const BaseDataType& dataType,
RecordingContainers* recordingContainers,
std::vector<SizeType>& containerIndexes)
{
if (!io->canModifyObjects()) {
return Status::Failure;
Expand Down Expand Up @@ -132,7 +135,8 @@ Status NWBFile::createElectricalSeries(
SizeArray {0, channelVector.size()},
SizeArray {CHUNK_XSIZE, 0});
electricalSeries->initialize();
recordingContainers->addData(std::move(electricalSeries));
recordingContainers->addContainer(std::move(electricalSeries));
containerIndexes.push_back(recordingContainers->containers.size() - 1);

// Add electrode information to electrode table (does not write to datasets
// yet)
Expand All @@ -145,16 +149,6 @@ Status NWBFile::createElectricalSeries(
return Status::Success;
}

Status NWBFile::startRecording()
{
return io->startRecording();
}

void NWBFile::stopRecording()
{
io->stopRecording();
}

template<SizeType N>
void NWBFile::cacheSpecifications(
const std::string& specPath,
Expand Down Expand Up @@ -182,26 +176,3 @@ std::unique_ptr<AQNWB::BaseRecordingData> NWBFile::createRecordingData(
return std::unique_ptr<BaseRecordingData>(
io->createArrayDataSet(type, size, chunking, path));
}

TimeSeries* NWBFile::getTimeSeries(const SizeType& timeseriesInd)
{
if (timeseriesInd >= this->recordingContainers->containers.size()) {
return nullptr;
} else {
return this->recordingContainers->containers[timeseriesInd].get();
}
}

// Recording Container

RecordingContainers::RecordingContainers(const std::string& name)
: name(name)
{
}

RecordingContainers::~RecordingContainers() {}

void RecordingContainers::addData(std::unique_ptr<TimeSeries> data)
{
this->containers.push_back(std::move(data));
}
74 changes: 9 additions & 65 deletions src/nwb/NWBFile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "BaseIO.hpp"
#include "Types.hpp"
#include "nwb/RecordingContainers.hpp"
#include "nwb/base/TimeSeries.hpp"

/*!
Expand All @@ -18,8 +19,6 @@
namespace AQNWB::NWB
{

class RecordingContainers; // declare here because gets used in NWBFile class

/**
* @brief The NWBFile class provides an interface for setting up and managing
* the NWB file.
Expand Down Expand Up @@ -69,28 +68,17 @@ class NWBFile
* @param recordingArrays vector of ChannelVector indicating the electrodes to
* record from. A separate ElectricalSeries will be
* created for each ChannelVector.
* @param recordingContainers The container to store the created TimeSeries.
* @param containerIndexes The indexes of the containers added to
* recordingContainers
* @param dataType The data type of the elements in the data block.
* @return Status The status of the object creation operation.
*/
Status createElectricalSeries(
std::vector<Types::ChannelVector> recordingArrays,
const BaseDataType& dataType = BaseDataType::I16);

/**
* @brief Starts the recording.
*/
Status startRecording();

/**
* @brief Stops the recording.
*/
void stopRecording();

/**
* @brief Gets the TimeSeries object from the recordingContainers
* @param timeseriesInd The index of the timeseries dataset within the group.
*/
TimeSeries* getTimeSeries(const SizeType& timeseriesInd);
const BaseDataType& dataType = BaseDataType::I16,
RecordingContainers* recordingContainers = nullptr,
std::vector<SizeType>& containerIndexes = emptyContainerIndexes);

protected:
/**
Expand Down Expand Up @@ -133,53 +121,9 @@ class NWBFile
const std::array<std::pair<std::string_view, std::string_view>, N>&
specVariables);

/**
* @brief Holds the Container (usually TimeSeries) objects that have been
* created in the nwb file for recording.
*/
std::unique_ptr<RecordingContainers> recordingContainers =
std::make_unique<RecordingContainers>("RecordingContainers");

const std::string identifierText;
std::shared_ptr<BaseIO> io;
static std::vector<SizeType> emptyContainerIndexes;
};

/**
* @brief The RecordingContainers class provides an interface for managing
* groups of TimeSeries acquired during a recording.
*/
class RecordingContainers
{
public:
/**
* @brief Constructor for RecordingContainer class.
* @param name The name of the group of time series
*/
RecordingContainers(const std::string& name);

/**
* @brief Deleted copy constructor to prevent construction-copying.
*/
RecordingContainers(const RecordingContainers&) = delete;

/**
* @brief Deleted copy assignment operator to prevent copying.
*/
RecordingContainers& operator=(const RecordingContainers&) = delete;

/**
* @brief Destructor for RecordingContainer class.
*/
~RecordingContainers();

/**
* @brief Adds a TimeSeries object to the container.
* @param data The TimeSeries object to add.
*/
void addData(std::unique_ptr<TimeSeries> data);

std::vector<std::unique_ptr<TimeSeries>> containers;
std::string name;
};

} // namespace AQNWB::NWB
} // namespace AQNWB::NWB
69 changes: 0 additions & 69 deletions src/nwb/NWBRecording.cpp

This file was deleted.

Loading
Loading