diff --git a/docs/pages/userdocs/workflow.dox b/docs/pages/userdocs/workflow.dox index 106f46ff..4f90846c 100644 --- a/docs/pages/userdocs/workflow.dox +++ b/docs/pages/userdocs/workflow.dox @@ -8,94 +8,94 @@ * 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 + * 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 + * 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 + * 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 + * 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 + * + * 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` + * 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 + * + * 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. + * + * 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`. - * + * 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 private member + * ``RecordingContainers.m_containers` object and their index can be tracked + * using the \ref AQNWB::NWB::RecordingContainers::size "RecordingContainers.size" of the input `recordingArrays`. + * * \snippet tests/examples/testWorkflowExamples.cpp example_workflow_datasets_snippet * * * \subsection start_recording 5. Start the recording. * - * Then, start the recording process with a call to the ``startRecording`` function of the I/O object. + * 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 + * \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. + * \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 + * 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 * * diff --git a/src/BaseIO.cpp b/src/BaseIO.cpp index 353f709d..027d81c6 100644 --- a/src/BaseIO.cpp +++ b/src/BaseIO.cpp @@ -31,29 +31,15 @@ const BaseDataType BaseDataType::DSTR = BaseDataType(T_STR, DEFAULT_STR_SIZE); // BaseIO -BaseIO::BaseIO() - : readyToOpen(true) - , opened(false) +BaseIO::BaseIO(const std::string& filename) + : m_filename(filename) + , m_readyToOpen(true) + , m_opened(false) { } BaseIO::~BaseIO() {} -bool BaseIO::isOpen() const -{ - return opened; -} - -bool BaseIO::isReadyToOpen() const -{ - return readyToOpen; -} - -bool BaseIO::canModifyObjects() -{ - return true; -} - Status BaseIO::createCommonNWBAttributes(const std::string& path, const std::string& objectNamespace, const std::string& neurodataType, diff --git a/src/BaseIO.hpp b/src/BaseIO.hpp index 19d47cb1..480d96e1 100644 --- a/src/BaseIO.hpp +++ b/src/BaseIO.hpp @@ -94,7 +94,7 @@ class BaseIO /** * @brief Constructor for the BaseIO class. */ - BaseIO(); + BaseIO(const std::string& filename); /** * @brief Copy constructor is deleted to prevent construction-copying. @@ -115,7 +115,7 @@ class BaseIO * @brief Returns the full path to the file. * @return The full path to the file. */ - virtual std::string getFileName() = 0; + virtual std::string getFileName() const { return m_filename; } /** * @brief Opens the file for writing. @@ -267,7 +267,7 @@ class BaseIO * override this function to check if objects can be modified. * @return True if the file is in a modification mode, false otherwise. */ - virtual bool canModifyObjects(); + virtual bool canModifyObjects() { return true; } /** * @brief Creates an extendable dataset with a given base data type, size, @@ -338,20 +338,20 @@ class BaseIO * @brief Returns true if the file is open. * @return True if the file is open, false otherwise. */ - bool isOpen() const; + inline bool isOpen() const { return m_opened; } /** * @brief Returns true if the file is able to be opened. * @return True if the file is able to be opened, false otherwise. */ - bool isReadyToOpen() const; + inline bool isReadyToOpen() const { return m_readyToOpen; } +protected: /** * @brief The name of the file. */ - const std::string filename; + const std::string m_filename; -protected: /** * @brief Creates a new group if it does not already exist. * @param path The location of the group in the file. @@ -362,12 +362,12 @@ class BaseIO /** * @brief Whether the file is ready to be opened. */ - bool readyToOpen; + bool m_readyToOpen; /** * @brief Whether the file is currently open. */ - bool opened; + bool m_opened; }; /** diff --git a/src/Channel.cpp b/src/Channel.cpp index d8f9357d..54598b9d 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -14,32 +14,17 @@ Channel::Channel(const std::string name, const float bitVolts, const std::array position, const std::string comments) - : name(name) - , groupName(groupName) - , groupIndex(groupIndex) - , localIndex(localIndex) - , globalIndex(globalIndex) - , position(position) - , conversion(conversion) - , samplingRate(samplingRate) - , bitVolts(bitVolts) - , comments(comments) + : m_name(name) + , m_groupName(groupName) + , m_groupIndex(groupIndex) + , m_localIndex(localIndex) + , m_globalIndex(globalIndex) + , m_position(position) + , m_conversion(conversion) + , m_samplingRate(samplingRate) + , m_bitVolts(bitVolts) + , m_comments(comments) { } Channel::~Channel() {} - -float Channel::getConversion() const -{ - return bitVolts / conversion; -} - -float Channel::getSamplingRate() const -{ - return samplingRate; -} - -float Channel::getBitVolts() const -{ - return bitVolts; -} diff --git a/src/Channel.hpp b/src/Channel.hpp index 4166aa8b..3d157f5d 100644 --- a/src/Channel.hpp +++ b/src/Channel.hpp @@ -36,73 +36,159 @@ class Channel */ ~Channel(); + /** + * @brief Copy constructor + */ + Channel(const Channel& other) = default; + + /** + * @brief Move constructor + */ + Channel(Channel&& other) = default; + + /** + * @brief Assignment operator + */ + Channel& operator=(const Channel& other) = default; + + /** + * @brief Move assignment operator + */ + Channel& operator=(Channel&& other) = default; + /** * @brief Getter for conversion factor * @return The conversion value. */ - float getConversion() const; + inline float getConversion() const { return m_bitVolts / m_conversion; } /** - * @brief Getter for samplingRate - * @return The samplingRate value. + * @brief Getter for sampling rate of the channel. + * @return The sampling rate value. */ - float getSamplingRate() const; + inline float getSamplingRate() const { return m_samplingRate; } /** - * @brief Getter for bitVolts + * @brief Getter for bitVolts floating point value of microvolts per bit * @return The bitVolts value. */ - float getBitVolts() const; + inline float getBitVolts() const { return m_bitVolts; } /** - * @brief Name of the channel. + * @brief Get the name of the array group the channel belongs to. + * @return The groupName value. */ - std::string name; + inline std::string getGroupName() const { return m_groupName; } /** - * @brief Name of the array group the channel belongs to. + * @brief Get the name of the channel + * @return The name value. */ - std::string groupName; + inline std::string getName() const { return m_name; } /** - * @brief Index of array group the channel belongs to. + * @brief Get the array group index the channel belongs to + * @return The groupIndex value. */ - SizeType groupIndex; + inline SizeType getGroupIndex() const { return m_groupIndex; } /** - * @brief Index of channel within the recording array. + * @brief Get the index of the channel within the recording array. + * @return The localIndex value. */ - SizeType localIndex; + inline SizeType getLocalIndex() const { return m_localIndex; } /** - * @brief Index of channel across the recording system. + * @brief Get the index of the channel across the recording system. + * @return The globalIndex value. */ - SizeType globalIndex; + inline SizeType getGlobalIndex() const { return m_globalIndex; } /** - * @brief Coordinates of channel (x, y, z) within the recording array. + * @brief Get the coordinates of channel (x, y, z) within the recording array. + * @return The position value. */ - std::array position; + inline const std::array& getPosition() const { return m_position; } /** - * @brief Comments about the channel. + * @brief Get comments about the channel + * @return The comments value. + */ + inline std::string getComments() const { return m_comments; } + + /** + * @brief Set comments about the channel. + * @param comments The comments to set. + */ + inline void setComments(const std::string& comments) + { + m_comments = comments; + } + + /** + * @brief Set coordinates of channel (x, y, z) within the recording array. + * @param position The position to set. + */ + inline void setPosition(const std::array& position) + { + m_position = position; + } + + /** + * @brief Set name of the channel. + * @param name The name to set. */ - std::string comments; + inline void setName(const std::string& name) { m_name = name; } private: + /** + * @brief Comments about the channel. + */ + std::string m_comments; + + /** + * @brief Coordinates of channel (x, y, z) within the recording array. + */ + std::array m_position; + + /** + * @brief Index of channel across the recording system. + */ + SizeType m_globalIndex; + + /** + * @brief Index of channel within the recording array. + */ + SizeType m_localIndex; + + /** + * @brief Index of array group the channel belongs to. + */ + SizeType m_groupIndex; + + /** + * @brief Name of the channel. + */ + std::string m_name; + + /** + * @brief Name of the array group the channel belongs to. + */ + std::string m_groupName; + /** * @brief Conversion factor. */ - float conversion; + float m_conversion; /** * @brief Sampling rate of the channel. */ - float samplingRate; + float m_samplingRate; /** * @brief floating point value of microvolts per bit */ - float bitVolts; + float m_bitVolts; }; -} // namespace AQNWB +} // namespace AQNWB \ No newline at end of file diff --git a/src/hdf5/HDF5IO.cpp b/src/hdf5/HDF5IO.cpp index 58fe39e0..9529f246 100644 --- a/src/hdf5/HDF5IO.cpp +++ b/src/hdf5/HDF5IO.cpp @@ -15,12 +15,9 @@ using namespace H5; using namespace AQNWB::HDF5; // HDF5IO - -HDF5IO::HDF5IO() {} - -HDF5IO::HDF5IO(const std::string& fileName, const bool disableSWMRMode) - : filename(fileName) - , disableSWMRMode(disableSWMRMode) +HDF5IO::HDF5IO(const std::string& filename, const bool disableSWMRMode) + : BaseIO(filename) + , m_disableSWMRMode(disableSWMRMode) { } @@ -29,11 +26,6 @@ HDF5IO::~HDF5IO() close(); } -std::string HDF5IO::getFileName() -{ - return filename; -} - Status HDF5IO::open() { if (std::filesystem::exists(getFileName())) { @@ -47,7 +39,7 @@ Status HDF5IO::open(bool newfile) { int accFlags = 0; - if (opened) + if (m_opened) return Status::Failure; FileAccPropList fapl = FileAccPropList::DEFAULT; @@ -58,19 +50,19 @@ Status HDF5IO::open(bool newfile) else accFlags = H5F_ACC_RDWR; - file = std::make_unique( + m_file = std::make_unique( getFileName(), accFlags, FileCreatPropList::DEFAULT, fapl); - opened = true; + m_opened = true; return Status::Success; } Status HDF5IO::close() { - if (this->file != nullptr && opened) { - this->file->close(); - this->file = nullptr; - this->opened = false; + if (m_file != nullptr && m_opened) { + m_file->close(); + m_file = nullptr; + m_opened = false; } return Status::Success; @@ -86,7 +78,7 @@ Status checkStatus(int status) Status HDF5IO::flush() { - int status = H5Fflush(this->file->getId(), H5F_SCOPE_GLOBAL); + int status = H5Fflush(m_file->getId(), H5F_SCOPE_GLOBAL); return checkStatus(status); } @@ -103,18 +95,18 @@ Status HDF5IO::createAttribute(const BaseDataType& type, DataType H5type; DataType origType; - if (!opened) + if (!m_opened) return Status::Failure; // open the group or dataset H5O_type_t objectType = getObjectType(path); switch (objectType) { case H5O_TYPE_GROUP: - gloc = file->openGroup(path); + gloc = m_file->openGroup(path); loc = &gloc; break; case H5O_TYPE_DATASET: - dloc = file->openDataSet(path); + dloc = m_file->openDataSet(path); loc = &dloc; break; default: @@ -178,7 +170,7 @@ Status HDF5IO::createAttribute(const std::vector& data, Attribute attr; hsize_t dims[1]; - if (!opened) + if (!m_opened) return Status::Failure; StrType H5type(PredType::C_S1, maxSize); @@ -188,11 +180,11 @@ Status HDF5IO::createAttribute(const std::vector& data, H5O_type_t objectType = getObjectType(path); switch (objectType) { case H5O_TYPE_GROUP: - gloc = file->openGroup(path); + gloc = m_file->openGroup(path); loc = &gloc; break; case H5O_TYPE_DATASET: - dloc = file->openDataSet(path); + dloc = m_file->openDataSet(path); loc = &dloc; break; default: @@ -235,18 +227,18 @@ Status HDF5IO::createReferenceAttribute(const std::string& referencePath, DataSet dloc; Attribute attr; - if (!opened) + if (!m_opened) return Status::Failure; // open the group or dataset H5O_type_t objectType = getObjectType(path); switch (objectType) { case H5O_TYPE_GROUP: - gloc = file->openGroup(path); + gloc = m_file->openGroup(path); loc = &gloc; break; case H5O_TYPE_DATASET: - dloc = file->openDataSet(path); + dloc = m_file->openDataSet(path); loc = &dloc; break; default: @@ -263,7 +255,7 @@ Status HDF5IO::createReferenceAttribute(const std::string& referencePath, hobj_ref_t* rdata = new hobj_ref_t[sizeof(hobj_ref_t)]; - file->reference(rdata, referencePath.c_str()); + m_file->reference(rdata, referencePath.c_str()); attr.write(H5::PredType::STD_REF_OBJ, rdata); delete[] rdata; @@ -283,10 +275,10 @@ Status HDF5IO::createReferenceAttribute(const std::string& referencePath, Status HDF5IO::createGroup(const std::string& path) { - if (!opened) + if (!m_opened) return Status::Failure; try { - file->createGroup(path); + m_file->createGroup(path); } catch (FileIException error) { error.printErrorStack(); } catch (GroupIException error) { @@ -297,10 +289,10 @@ Status HDF5IO::createGroup(const std::string& path) Status HDF5IO::createGroupIfDoesNotExist(const std::string& path) { - if (!opened) + if (!m_opened) return Status::Failure; try { - file->childObjType(path); + m_file->childObjType(path); } catch (FileIException) { return createGroup(path); } @@ -310,11 +302,11 @@ Status HDF5IO::createGroupIfDoesNotExist(const std::string& path) /** Creates a link to another location in the file */ Status HDF5IO::createLink(const std::string& path, const std::string& reference) { - if (!opened) + if (!m_opened) return Status::Failure; herr_t error = H5Lcreate_soft(reference.c_str(), - file->getLocId(), + m_file->getLocId(), path.c_str(), H5P_DEFAULT, H5P_DEFAULT); @@ -325,7 +317,7 @@ Status HDF5IO::createLink(const std::string& path, const std::string& reference) Status HDF5IO::createReferenceDataSet( const std::string& path, const std::vector& references) { - if (!opened) + if (!m_opened) return Status::Failure; const hsize_t size = references.size(); @@ -333,12 +325,12 @@ Status HDF5IO::createReferenceDataSet( hobj_ref_t* rdata = new hobj_ref_t[size * sizeof(hobj_ref_t)]; for (SizeType i = 0; i < size; i++) { - file->reference(&rdata[i], references[i].c_str()); + m_file->reference(&rdata[i], references[i].c_str()); } hid_t space = H5Screate_simple(1, &size, NULL); - hid_t dset = H5Dcreate(file->getLocId(), + hid_t dset = H5Dcreate(m_file->getLocId(), path.c_str(), H5T_STD_REF_OBJ, space, @@ -360,15 +352,15 @@ Status HDF5IO::createReferenceDataSet( Status HDF5IO::createStringDataSet(const std::string& path, const std::string& value) { - if (!opened) + if (!m_opened) return Status::Failure; std::unique_ptr dataset; DataType H5type = getH5Type(BaseDataType::STR(value.length())); DataSpace dSpace(H5S_SCALAR); - dataset = - std::make_unique(file->createDataSet(path, H5type, dSpace)); + dataset = std::make_unique( + m_file->createDataSet(path, H5type, dSpace)); dataset->write(value.c_str(), H5type); return Status::Success; @@ -377,7 +369,7 @@ Status HDF5IO::createStringDataSet(const std::string& path, Status HDF5IO::createStringDataSet(const std::string& path, const std::vector& values) { - if (!opened) + if (!m_opened) return Status::Failure; std::vector cStrs; @@ -397,11 +389,11 @@ Status HDF5IO::createStringDataSet(const std::string& path, Status HDF5IO::startRecording() { - if (!opened) + if (!m_opened) return Status::Failure; - if (!disableSWMRMode) { - herr_t status = H5Fstart_swmr_write(this->file->getId()); + if (!m_disableSWMRMode) { + herr_t status = H5Fstart_swmr_write(m_file->getId()); return checkStatus(status); } return Status::Success; @@ -410,7 +402,7 @@ Status HDF5IO::startRecording() Status HDF5IO::stopRecording() { // if SWMR mode is disabled, stopping the recording will leave the file open - if (!disableSWMRMode) { + if (!m_disableSWMRMode) { close(); // SWMR mode cannot be disabled so close the file } else { this->flush(); @@ -420,13 +412,13 @@ Status HDF5IO::stopRecording() bool HDF5IO::canModifyObjects() { - if (!opened) + if (!m_opened) return false; // Check if we are in SWMR mode bool inSWMRMode = false; unsigned int intent; - herr_t status = H5Fget_intent(this->file->getId(), &intent); + herr_t status = H5Fget_intent(m_file->getId(), &intent); bool statusOK = (status >= 0); if (statusOK) { inSWMRMode = (intent & (H5F_ACC_SWMR_READ | H5F_ACC_SWMR_WRITE)); @@ -439,7 +431,7 @@ bool HDF5IO::canModifyObjects() bool HDF5IO::objectExists(const std::string& path) { - htri_t exists = H5Lexists(file->getId(), path.c_str(), H5P_DEFAULT); + htri_t exists = H5Lexists(m_file->getId(), path.c_str(), H5P_DEFAULT); if (exists > 0) { return true; } else { @@ -452,11 +444,11 @@ std::unique_ptr HDF5IO::getDataSet( { std::unique_ptr data; - if (!opened) + if (!m_opened) return nullptr; try { - data = std::make_unique(file->openDataSet(path)); + data = std::make_unique(m_file->openDataSet(path)); return std::make_unique(std::move(data)); } catch (DataSetIException error) { error.printErrorStack(); @@ -480,7 +472,7 @@ std::unique_ptr HDF5IO::createArrayDataSet( DSetCreatPropList prop; DataType H5type = getH5Type(type); - if (!opened) + if (!m_opened) return nullptr; SizeType dimension = size.size(); @@ -509,7 +501,7 @@ std::unique_ptr HDF5IO::createArrayDataSet( prop.setChunk(static_cast(dimension), chunk_dims.data()); data = std::make_unique( - file->createDataSet(path, H5type, dSpace, prop)); + m_file->createDataSet(path, H5type, dSpace, prop)); return std::make_unique(std::move(data)); } @@ -520,11 +512,11 @@ H5O_type_t HDF5IO::getObjectType(const std::string& path) // get whether path is a dataset or group H5O_info_t objInfo; // Structure to hold information about the object H5Oget_info_by_name( - this->file->getId(), path.c_str(), &objInfo, H5O_INFO_BASIC, H5P_DEFAULT); + m_file->getId(), path.c_str(), &objInfo, H5O_INFO_BASIC, H5P_DEFAULT); #else // get whether path is a dataset or group H5O_info_t objInfo; // Structure to hold information about the object - H5Oget_info_by_name(this->file->getId(), path.c_str(), &objInfo, H5P_DEFAULT); + H5Oget_info_by_name(m_file->getId(), path.c_str(), &objInfo, H5P_DEFAULT); #endif H5O_type_t objectType = objInfo.type; @@ -653,7 +645,7 @@ HDF5RecordingData::HDF5RecordingData(std::unique_ptr data) this->nDimensions = nDimensions; this->position = std::vector( nDimensions, 0); // Initialize position with 0 for each dimension - this->dSet = std::make_unique(*data); + m_dataset = std::make_unique(*data); } // HDF5RecordingData @@ -661,7 +653,7 @@ HDF5RecordingData::HDF5RecordingData(std::unique_ptr data) HDF5RecordingData::~HDF5RecordingData() { // Safety - dSet->flush(H5F_SCOPE_GLOBAL); + m_dataset->flush(H5F_SCOPE_GLOBAL); } Status HDF5RecordingData::writeDataBlock( @@ -690,10 +682,10 @@ Status HDF5RecordingData::writeDataBlock( } // Adjust dataset dimensions if necessary - dSet->extend(dSetDims.data()); + m_dataset->extend(dSetDims.data()); // Set size to new size based on updated dimensionality - DataSpace fSpace = dSet->getSpace(); + DataSpace fSpace = m_dataset->getSpace(); fSpace.getSimpleExtentDims(dSetDims.data()); for (int i = 0; i < nDimensions; ++i) { size[i] = dSetDims[i]; @@ -716,7 +708,7 @@ Status HDF5RecordingData::writeDataBlock( // Write the data DataType nativeType = HDF5IO::getNativeType(type); - dSet->write(data, nativeType, mSpace, fSpace); + m_dataset->write(data, nativeType, mSpace, fSpace); // Update position for simple extension for (int i = 0; i < dataShape.size(); ++i) { @@ -731,8 +723,3 @@ Status HDF5RecordingData::writeDataBlock( } return Status::Success; } - -const H5::DataSet* HDF5RecordingData::getDataSet() -{ - return dSet.get(); -}; diff --git a/src/hdf5/HDF5IO.hpp b/src/hdf5/HDF5IO.hpp index 8d2480dc..d9f3c851 100644 --- a/src/hdf5/HDF5IO.hpp +++ b/src/hdf5/HDF5IO.hpp @@ -32,11 +32,6 @@ class HDF5RecordingData; // declare here because gets used in HDF5IO class class HDF5IO : public BaseIO { public: - /** - * @brief Default constructor for the HDF5IO class. - */ - HDF5IO(); - /** * @brief Constructor for the HDF5IO class that takes a file name as input. * @param fileName The name of the HDF5 file. @@ -53,12 +48,6 @@ class HDF5IO : public BaseIO */ ~HDF5IO(); - /** - * @brief Returns the full path to the HDF5 file. - * @return The full path to the HDF5 file. - */ - std::string getFileName() override; - /** * @brief Opens an existing file or creates a new file for writing. * @return The status of the file opening operation. @@ -264,8 +253,6 @@ class HDF5IO : public BaseIO static H5::DataType getH5Type(BaseDataType type); protected: - std::string filename; - /** * @brief Creates a new group if it does not exist. * @param path The location in the file of the group. @@ -274,9 +261,14 @@ class HDF5IO : public BaseIO Status createGroupIfDoesNotExist(const std::string& path) override; private: - std::unique_ptr file; - bool disableSWMRMode; // when set do not use SWMR mode when opening the HDF5 - // file + /** + * @brief the HDF5 file + */ + std::unique_ptr m_file; + /** + * @brief When set do not use SWMR mode when opening the HDF5 file + */ + bool m_disableSWMRMode; }; /** @@ -327,17 +319,17 @@ class HDF5RecordingData : public BaseRecordingData * @brief Gets a const pointer to the HDF5 dataset. * @return A const pointer to the HDF5 dataset. */ - const H5::DataSet* getDataSet(); + inline const H5::DataSet* getDataSet() const { return m_dataset.get(); } private: /** - * @brief Pointer to an extendable HDF5 dataset + * @brief Return status of HDF5 operations. */ - std::unique_ptr dSet; + Status checkStatus(int status); /** - * @brief Return status of HDF5 operations. + * @brief Pointer to an extendable HDF5 dataset */ - Status checkStatus(int status); + std::unique_ptr m_dataset; }; } // namespace AQNWB::HDF5 diff --git a/src/nwb/NWBFile.cpp b/src/nwb/NWBFile.cpp index 0142634e..9e2ed943 100644 --- a/src/nwb/NWBFile.cpp +++ b/src/nwb/NWBFile.cpp @@ -31,8 +31,9 @@ std::vector NWBFile::emptyContainerIndexes = {}; // NWBFile NWBFile::NWBFile(const std::string& idText, std::shared_ptr io) - : identifierText(idText) - , io(io) + : Container("/", io) + , m_identifierText(idText) + { } @@ -41,44 +42,44 @@ NWBFile::~NWBFile() {} Status NWBFile::initialize(const std::string description, const std::string dataCollection) { - if (std::filesystem::exists(io->getFileName())) { - return io->open(false); + if (std::filesystem::exists(m_io->getFileName())) { + return m_io->open(false); } else { - io->open(true); + m_io->open(true); return createFileStructure(description, dataCollection); } } Status NWBFile::finalize() { - return io->close(); + return m_io->close(); } Status NWBFile::createFileStructure(std::string description, std::string dataCollection) { - if (!io->canModifyObjects()) { + if (!m_io->canModifyObjects()) { return Status::Failure; } - io->createCommonNWBAttributes("/", "core", "NWBFile", ""); - io->createAttribute(AQNWB::SPEC::CORE::version, "/", "nwb_version"); - - io->createGroup("/acquisition"); - io->createGroup("/analysis"); - io->createGroup("/processing"); - io->createGroup("/stimulus"); - io->createGroup("/stimulus/presentation"); - io->createGroup("/stimulus/templates"); - io->createGroup("/general"); - io->createGroup("/general/devices"); - io->createGroup("/general/extracellular_ephys"); + m_io->createCommonNWBAttributes("/", "core", "NWBFile", ""); + m_io->createAttribute(AQNWB::SPEC::CORE::version, "/", "nwb_version"); + + m_io->createGroup("/acquisition"); + m_io->createGroup("/analysis"); + m_io->createGroup("/processing"); + m_io->createGroup("/stimulus"); + m_io->createGroup("/stimulus/presentation"); + m_io->createGroup("/stimulus/templates"); + m_io->createGroup("/general"); + m_io->createGroup("/general/devices"); + m_io->createGroup("/general/extracellular_ephys"); if (dataCollection != "") { - io->createStringDataSet("/general/data_collection", dataCollection); + m_io->createStringDataSet("/general/data_collection", dataCollection); } - io->createGroup("/specifications"); - io->createReferenceAttribute("/specifications", "/", ".specloc"); + m_io->createGroup("/specifications"); + m_io->createReferenceAttribute("/specifications", "/", ".specloc"); cacheSpecifications( "core", AQNWB::SPEC::CORE::version, AQNWB::SPEC::CORE::specVariables); @@ -91,11 +92,11 @@ Status NWBFile::createFileStructure(std::string description, std::string time = getCurrentTime(); std::vector timeVec = {time}; - io->createStringDataSet("/file_create_date", timeVec); - io->createStringDataSet("/session_description", description); - io->createStringDataSet("/session_start_time", time); - io->createStringDataSet("/timestamps_reference_time", time); - io->createStringDataSet("/identifier", identifierText); + m_io->createStringDataSet("/file_create_date", timeVec); + m_io->createStringDataSet("/session_description", description); + m_io->createStringDataSet("/session_start_time", time); + m_io->createStringDataSet("/timestamps_reference_time", time); + m_io->createStringDataSet("/identifier", m_identifierText); return Status::Success; } @@ -107,7 +108,7 @@ Status NWBFile::createElectricalSeries( RecordingContainers* recordingContainers, std::vector& containerIndexes) { - if (!io->canModifyObjects()) { + if (!m_io->canModifyObjects()) { return Status::Failure; } @@ -117,14 +118,14 @@ Status NWBFile::createElectricalSeries( // Setup electrode table if it was not yet created bool electrodeTableCreated = - io->objectExists(ElectrodeTable::electrodeTablePath); + m_io->objectExists(ElectrodeTable::electrodeTablePath); if (!electrodeTableCreated) { - elecTable = std::make_unique(io); - elecTable->initialize(); + m_electrodeTable = std::make_unique(m_io); + m_electrodeTable->initialize(); // Add electrode information to table (does not write to datasets yet) for (const auto& channelVector : recordingArrays) { - elecTable->addElectrodes(channelVector); + m_electrodeTable->addElectrodes(channelVector); } } @@ -134,26 +135,26 @@ Status NWBFile::createElectricalSeries( const std::string& recordingName = recordingNames[i]; // Setup electrodes and devices - std::string groupName = channelVector[0].groupName; + std::string groupName = channelVector[0].getGroupName(); std::string devicePath = "/general/devices/" + groupName; std::string electrodePath = "/general/extracellular_ephys/" + groupName; std::string electricalSeriesPath = acquisitionPath + "/" + recordingName; // Check if device exists for groupName, create device and electrode group // if not - if (!io->objectExists(devicePath)) { - Device device = Device(devicePath, io, "description", "unknown"); + if (!m_io->objectExists(devicePath)) { + Device device = Device(devicePath, m_io, "description", "unknown"); device.initialize(); ElectrodeGroup elecGroup = - ElectrodeGroup(electrodePath, io, "description", "unknown", device); + ElectrodeGroup(electrodePath, m_io, "description", "unknown", device); elecGroup.initialize(); } // Setup electrical series datasets auto electricalSeries = std::make_unique( electricalSeriesPath, - io, + m_io, dataType, channelVector, "Stores continuously sampled voltage data from an " @@ -162,13 +163,13 @@ Status NWBFile::createElectricalSeries( SizeArray {CHUNK_XSIZE, 0}); electricalSeries->initialize(); recordingContainers->addContainer(std::move(electricalSeries)); - containerIndexes.push_back(recordingContainers->containers.size() - 1); + containerIndexes.push_back(recordingContainers->size() - 1); } // write electrode information to datasets // (requires that the ElectrodeGroup has been written) if (!electrodeTableCreated) { - elecTable->finalize(); + m_electrodeTable->finalize(); } return Status::Success; @@ -181,7 +182,7 @@ Status NWBFile::createSpikeEventSeries( RecordingContainers* recordingContainers, std::vector& containerIndexes) { - if (!io->canModifyObjects()) { + if (!m_io->canModifyObjects()) { return Status::Failure; } @@ -191,14 +192,14 @@ Status NWBFile::createSpikeEventSeries( // Setup electrode table if it was not yet created bool electrodeTableCreated = - io->objectExists(ElectrodeTable::electrodeTablePath); + m_io->objectExists(ElectrodeTable::electrodeTablePath); if (!electrodeTableCreated) { - elecTable = std::make_unique(io); - elecTable->initialize(); + m_electrodeTable = std::make_unique(m_io); + m_electrodeTable->initialize(); // Add electrode information to table (does not write to datasets yet) for (const auto& channelVector : recordingArrays) { - elecTable->addElectrodes(channelVector); + m_electrodeTable->addElectrodes(channelVector); } } @@ -208,19 +209,19 @@ Status NWBFile::createSpikeEventSeries( const std::string& recordingName = recordingNames[i]; // Setup electrodes and devices - std::string groupName = channelVector[0].groupName; + std::string groupName = channelVector[0].getGroupName(); std::string devicePath = "/general/devices/" + groupName; std::string electrodePath = "/general/extracellular_ephys/" + groupName; std::string spikeEventSeriesPath = acquisitionPath + "/" + recordingName; // Check if device exists for groupName, create device and electrode group // if not - if (!io->objectExists(devicePath)) { - Device device = Device(devicePath, io, "description", "unknown"); + if (!m_io->objectExists(devicePath)) { + Device device = Device(devicePath, m_io, "description", "unknown"); device.initialize(); ElectrodeGroup elecGroup = - ElectrodeGroup(electrodePath, io, "description", "unknown", device); + ElectrodeGroup(electrodePath, m_io, "description", "unknown", device); elecGroup.initialize(); } @@ -237,7 +238,7 @@ Status NWBFile::createSpikeEventSeries( auto spikeEventSeries = std::make_unique( spikeEventSeriesPath, - io, + m_io, dataType, channelVector, "Stores spike waveforms from an extracellular ephys recording", @@ -245,13 +246,13 @@ Status NWBFile::createSpikeEventSeries( chunkSize); spikeEventSeries->initialize(); recordingContainers->addContainer(std::move(spikeEventSeries)); - containerIndexes.push_back(recordingContainers->containers.size() - 1); + containerIndexes.push_back(recordingContainers->size() - 1); } // write electrode information to datasets // (requires that the ElectrodeGroup has been written) if (!electrodeTableCreated) { - elecTable->finalize(); + m_electrodeTable->finalize(); } return Status::Success; @@ -264,13 +265,13 @@ void NWBFile::cacheSpecifications( const std::array, N>& specVariables) { - io->createGroup("/specifications/" + specPath); - io->createGroup("/specifications/" + specPath + "/" + versionNumber); + m_io->createGroup("/specifications/" + specPath); + m_io->createGroup("/specifications/" + specPath + "/" + versionNumber); for (const auto& [name, content] : specVariables) { - io->createStringDataSet("/specifications/" + specPath + "/" + versionNumber - + "/" + std::string(name), - std::string(content)); + m_io->createStringDataSet("/specifications/" + specPath + "/" + + versionNumber + "/" + std::string(name), + std::string(content)); } } @@ -282,5 +283,5 @@ std::unique_ptr NWBFile::createRecordingData( const std::string& path) { return std::unique_ptr( - io->createArrayDataSet(type, size, chunking, path)); + m_io->createArrayDataSet(type, size, chunking, path)); } diff --git a/src/nwb/NWBFile.hpp b/src/nwb/NWBFile.hpp index a5a5c212..ece6a64e 100644 --- a/src/nwb/NWBFile.hpp +++ b/src/nwb/NWBFile.hpp @@ -12,6 +12,7 @@ #include "nwb/RecordingContainers.hpp" #include "nwb/base/TimeSeries.hpp" #include "nwb/file/ElectrodeTable.hpp" +#include "nwb/hdmf/base/Container.hpp" /*! * \namespace AQNWB::NWB @@ -24,7 +25,7 @@ namespace AQNWB::NWB * @brief The NWBFile class provides an interface for setting up and managing * the NWB file. */ -class NWBFile +class NWBFile : public Container { public: /** @@ -152,11 +153,15 @@ class NWBFile const std::array, N>& specVariables); - std::unique_ptr elecTable; - const std::string identifierText; - std::shared_ptr io; - static std::vector emptyContainerIndexes; inline const static std::string acquisitionPath = "/acquisition"; + static std::vector emptyContainerIndexes; + +private: + /** + * @brief The ElectrodeTable for the file + */ + std::unique_ptr m_electrodeTable; + const std::string m_identifierText; }; } // namespace AQNWB::NWB \ No newline at end of file diff --git a/src/nwb/RecordingContainers.cpp b/src/nwb/RecordingContainers.cpp index 4658d551..59b01bfb 100644 --- a/src/nwb/RecordingContainers.cpp +++ b/src/nwb/RecordingContainers.cpp @@ -14,15 +14,15 @@ RecordingContainers::~RecordingContainers() {} void RecordingContainers::addContainer(std::unique_ptr container) { - this->containers.push_back(std::move(container)); + m_containers.push_back(std::move(container)); } Container* RecordingContainers::getContainer(const SizeType& containerInd) { - if (containerInd >= this->containers.size()) { + if (containerInd >= m_containers.size()) { return nullptr; } else { - return this->containers[containerInd].get(); + return m_containers[containerInd].get(); } } @@ -40,7 +40,7 @@ Status RecordingContainers::writeTimeseriesData( return Status::Failure; // write data and timestamps to datasets - if (channel.localIndex == 0) { + if (channel.getLocalIndex() == 0) { // write with timestamps if it's the first channel return ts->writeData(dataShape, positionOffset, data, timestamps); } else { @@ -62,7 +62,7 @@ Status RecordingContainers::writeElectricalSeriesData( if (es == nullptr) return Status::Failure; - es->writeChannel(channel.localIndex, numSamples, data, timestamps); + es->writeChannel(channel.getLocalIndex(), numSamples, data, timestamps); } Status RecordingContainers::writeSpikeEventData(const SizeType& containerInd, diff --git a/src/nwb/RecordingContainers.hpp b/src/nwb/RecordingContainers.hpp index f38d84c7..0ffd938d 100644 --- a/src/nwb/RecordingContainers.hpp +++ b/src/nwb/RecordingContainers.hpp @@ -105,8 +105,21 @@ class RecordingContainers const void* data, const void* timestamps); - std::vector> containers; - std::string name; + /** + * @brief Get the number of recording containers + */ + inline SizeType size() const { return m_containers.size(); } + +private: + /** + * @brief The Containers used for recording + */ + std::vector> m_containers; + + /** + * @brief The name of the collection of recording containers + */ + std::string m_name; }; } // namespace AQNWB::NWB diff --git a/src/nwb/base/TimeSeries.cpp b/src/nwb/base/TimeSeries.cpp index a9c008b3..a5158077 100644 --- a/src/nwb/base/TimeSeries.cpp +++ b/src/nwb/base/TimeSeries.cpp @@ -37,19 +37,20 @@ void TimeSeries::initialize() Container::initialize(); // setup attributes - io->createCommonNWBAttributes(path, "core", neurodataType, description); - io->createAttribute(comments, path, "comments"); + m_io->createCommonNWBAttributes(m_path, "core", neurodataType, description); + m_io->createAttribute(comments, m_path, "comments"); // setup datasets - this->data = std::unique_ptr(io->createArrayDataSet( - dataType, dsetSize, chunkSize, getPath() + "/data")); - io->createDataAttributes(getPath(), conversion, resolution, unit); + this->data = std::unique_ptr(m_io->createArrayDataSet( + dataType, dsetSize, chunkSize, m_path + "/data")); + m_io->createDataAttributes(m_path, conversion, resolution, unit); SizeArray tsDsetSize = { dsetSize[0]}; // timestamps match data along first dimension - this->timestamps = std::unique_ptr(io->createArrayDataSet( - this->timestampsType, tsDsetSize, chunkSize, getPath() + "/timestamps")); - io->createTimestampsAttributes(getPath()); + this->timestamps = + std::unique_ptr(m_io->createArrayDataSet( + this->timestampsType, tsDsetSize, chunkSize, m_path + "/timestamps")); + m_io->createTimestampsAttributes(m_path); } Status TimeSeries::writeData(const std::vector& dataShape, diff --git a/src/nwb/device/Device.cpp b/src/nwb/device/Device.cpp index 262d9207..01036ad2 100644 --- a/src/nwb/device/Device.cpp +++ b/src/nwb/device/Device.cpp @@ -21,8 +21,8 @@ void Device::initialize() { Container::initialize(); - io->createCommonNWBAttributes(path, "core", "Device", description); - io->createAttribute(manufacturer, path, "manufacturer"); + m_io->createCommonNWBAttributes(m_path, "core", "Device", description); + m_io->createAttribute(manufacturer, m_path, "manufacturer"); } // Getter for manufacturer diff --git a/src/nwb/ecephys/ElectricalSeries.cpp b/src/nwb/ecephys/ElectricalSeries.cpp index bf186d27..7be87dae 100644 --- a/src/nwb/ecephys/ElectricalSeries.cpp +++ b/src/nwb/ecephys/ElectricalSeries.cpp @@ -23,7 +23,7 @@ ElectricalSeries::ElectricalSeries(const std::string& path, dataType, "volts", // default unit for Electrical Series description, - channelVector[0].comments, + channelVector[0].getComments(), dsetSize, chunkSize, channelVector[0].getConversion(), @@ -45,36 +45,39 @@ void ElectricalSeries::initialize() std::vector electrodeInds(channelVector.size()); std::vector channelConversions(channelVector.size()); for (size_t i = 0; i < channelVector.size(); ++i) { - electrodeInds[i] = channelVector[i].globalIndex; + electrodeInds[i] = channelVector[i].getGlobalIndex(); channelConversions[i] = channelVector[i].getConversion(); } samplesRecorded = SizeArray(channelVector.size(), 0); // make channel conversion dataset channelConversion = std::unique_ptr( - io->createArrayDataSet(BaseDataType::F32, - SizeArray {1}, - chunkSize, - getPath() + "/channel_conversion")); + m_io->createArrayDataSet(BaseDataType::F32, + SizeArray {1}, + chunkSize, + getPath() + "/channel_conversion")); channelConversion->writeDataBlock( std::vector(1, channelVector.size()), BaseDataType::F32, &channelConversions[0]); - io->createCommonNWBAttributes(getPath() + "/channel_conversion", - "hdmf-common", - "", - "Bit volts values for all channels"); + m_io->createCommonNWBAttributes(getPath() + "/channel_conversion", + "hdmf-common", + "", + "Bit volts values for all channels"); // make electrodes dataset - electrodesDataset = std::unique_ptr(io->createArrayDataSet( - BaseDataType::I32, SizeArray {1}, chunkSize, getPath() + "/electrodes")); + electrodesDataset = std::unique_ptr( + m_io->createArrayDataSet(BaseDataType::I32, + SizeArray {1}, + chunkSize, + getPath() + "/electrodes")); electrodesDataset->writeDataBlock( std::vector(1, channelVector.size()), BaseDataType::I32, &electrodeInds[0]); - io->createCommonNWBAttributes( + m_io->createCommonNWBAttributes( getPath() + "/electrodes", "hdmf-common", "DynamicTableRegion", ""); - io->createReferenceAttribute( + m_io->createReferenceAttribute( ElectrodeTable::electrodeTablePath, getPath() + "/electrodes", "table"); } diff --git a/src/nwb/ecephys/SpikeEventSeries.hpp b/src/nwb/ecephys/SpikeEventSeries.hpp index ad2dd2ae..cbd541da 100644 --- a/src/nwb/ecephys/SpikeEventSeries.hpp +++ b/src/nwb/ecephys/SpikeEventSeries.hpp @@ -19,8 +19,22 @@ class SpikeEventSeries : public ElectricalSeries * @brief Constructor. * @param path The location of the SpikeEventSeries in the file. * @param io A shared pointer to the IO object. - * @param description The description of the SpikeEventSeries, should describe - * how events were detected. + * @param dataType The data type to use for storing the recorded voltage + * @param channelVector The electrodes to use for recording + * @param description The description of the TimeSeries. + * @param dsetSize Initial size of the main dataset. This must be a vector + * with two elements. The first element specifies the length + * in time and the second element must be equal to the + * length of channelVector + * @param chunkSize Chunk size to use. The number of elements must be two to + * specify the size of a chunk in the time and electrode + * dimension + * @param conversion Scalar to multiply each element in data to convert it to + * the specified ‘unit’ + * @param resolution Smallest meaningful difference between values in data, + * stored in the specified by unit + * @param offset Scalar to add to the data after scaling by ‘conversion’ to + * finalize its coercion to the specified ‘unit' */ SpikeEventSeries(const std::string& path, std::shared_ptr io, @@ -49,7 +63,6 @@ class SpikeEventSeries : public ElectricalSeries * @param numChannels The number of channels in the event * @param data The data of the event * @param timestamps The timestamps of the event - * @param */ Status writeSpike(const SizeType& numSamples, const SizeType& numChannels, diff --git a/src/nwb/file/ElectrodeGroup.cpp b/src/nwb/file/ElectrodeGroup.cpp index b5beaa03..0d283654 100644 --- a/src/nwb/file/ElectrodeGroup.cpp +++ b/src/nwb/file/ElectrodeGroup.cpp @@ -24,9 +24,10 @@ void ElectrodeGroup::initialize() { Container::initialize(); - io->createCommonNWBAttributes(path, "core", "ElectrodeGroup", description); - io->createAttribute(location, path, "location"); - io->createLink("/" + path + "/device", "/" + device.getPath()); + m_io->createCommonNWBAttributes( + m_path, "core", "ElectrodeGroup", description); + m_io->createAttribute(location, m_path, "location"); + m_io->createLink("/" + m_path + "/device", "/" + device.getPath()); } // Getter for description diff --git a/src/nwb/file/ElectrodeTable.cpp b/src/nwb/file/ElectrodeTable.cpp index 027c9f6a..0f162bce 100644 --- a/src/nwb/file/ElectrodeTable.cpp +++ b/src/nwb/file/ElectrodeTable.cpp @@ -11,7 +11,8 @@ ElectrodeTable::ElectrodeTable(std::shared_ptr io, const std::string& description) : DynamicTable(electrodeTablePath, // use the electrodeTablePath io, - description) + description, + {"group", "group_name", "location"}) { } @@ -24,26 +25,28 @@ void ElectrodeTable::initialize() // create group DynamicTable::initialize(); - electrodeDataset->dataset = - std::unique_ptr(io->createArrayDataSet( - BaseDataType::I32, SizeArray {1}, SizeArray {1}, path + "id")); - groupNamesDataset->dataset = std::unique_ptr( - io->createArrayDataSet(BaseDataType::STR(250), - SizeArray {0}, - SizeArray {1}, - path + "group_name")); - locationsDataset - ->dataset = std::unique_ptr(io->createArrayDataSet( - BaseDataType::STR(250), SizeArray {0}, SizeArray {1}, path + "location")); + electrodeDataset->setDataset( + std::unique_ptr(m_io->createArrayDataSet( + BaseDataType::I32, SizeArray {1}, SizeArray {1}, m_path + "id"))); + groupNamesDataset->setDataset(std::unique_ptr( + m_io->createArrayDataSet(BaseDataType::STR(250), + SizeArray {0}, + SizeArray {1}, + m_path + "group_name"))); + locationsDataset->setDataset(std::unique_ptr( + m_io->createArrayDataSet(BaseDataType::STR(250), + SizeArray {0}, + SizeArray {1}, + m_path + "location"))); } void ElectrodeTable::addElectrodes(std::vector channels) { // create datasets for (const auto& ch : channels) { - groupReferences.push_back(groupPathBase + ch.groupName); - groupNames.push_back(ch.groupName); - electrodeNumbers.push_back(ch.globalIndex); + groupReferences.push_back(groupPathBase + ch.getGroupName()); + groupNames.push_back(ch.getGroupName()); + electrodeNumbers.push_back(ch.getGlobalIndex()); locationNames.push_back("unknown"); } } @@ -63,22 +66,3 @@ void ElectrodeTable::finalize() "a reference to the ElectrodeGroup this electrode is a part of", groupReferences); } - -// Getter for colNames -const std::vector& ElectrodeTable::getColNames() -{ - return colNames; -} - -// Setter for colNames -void ElectrodeTable::setColNames(const std::vector& newColNames) -{ - colNames = newColNames; -} - -// Getter for groupPath -std::string ElectrodeTable::getGroupPath() const -{ - return groupReferences[0]; // all channels in ChannelVector should have the - // same groupName -} diff --git a/src/nwb/file/ElectrodeTable.hpp b/src/nwb/file/ElectrodeTable.hpp index b8611a32..6c3775fa 100644 --- a/src/nwb/file/ElectrodeTable.hpp +++ b/src/nwb/file/ElectrodeTable.hpp @@ -52,23 +52,15 @@ class ElectrodeTable : public DynamicTable */ void addElectrodes(std::vector channels); - /** - * @brief Gets the column names of the ElectrodeTable. - * @return The vector of column names. - */ - const std::vector& getColNames() override; - - /** - * @brief Sets the column names of the ElectrodeTable. - * @param newColNames The vector of new column names. - */ - void setColNames(const std::vector& newColNames); - /** * @brief Gets the group path of the ElectrodeTable. * @return The group path. */ - std::string getGroupPath() const; + inline std::string getGroupPath() const + { + // all channels in ChannelVector should have the same groupName + return groupReferences[0]; + } /** * @brief Sets the group path of the ElectrodeTable. @@ -115,11 +107,6 @@ class ElectrodeTable : public DynamicTable */ std::vector groupReferences; - /** - * @brief The vector of column names for the table. - */ - std::vector colNames = {"group", "group_name", "location"}; - /** * @brief The references path to the ElectrodeGroup */ diff --git a/src/nwb/hdmf/base/Container.cpp b/src/nwb/hdmf/base/Container.cpp index 525d82ed..f2221cb8 100644 --- a/src/nwb/hdmf/base/Container.cpp +++ b/src/nwb/hdmf/base/Container.cpp @@ -6,8 +6,8 @@ using namespace AQNWB::NWB; /** Constructor */ Container::Container(const std::string& path, std::shared_ptr io) - : path(path) - , io(io) + : m_path(path) + , m_io(io) { } @@ -17,11 +17,5 @@ Container::~Container() {} /** Initialize */ void Container::initialize() { - io->createGroup(path); -} - -/** Getter for path */ -std::string Container::getPath() const -{ - return path; + m_io->createGroup(m_path); } diff --git a/src/nwb/hdmf/base/Container.hpp b/src/nwb/hdmf/base/Container.hpp index 1d89c875..b1ecdc6e 100644 --- a/src/nwb/hdmf/base/Container.hpp +++ b/src/nwb/hdmf/base/Container.hpp @@ -35,17 +35,17 @@ class Container * @brief Gets the path of the container. * @return The path of the container. */ - std::string getPath() const; + inline std::string getPath() const { return m_path; } protected: /** * @brief The path of the container. */ - std::string path; + std::string m_path; /** * @brief A shared pointer to the IO object. */ - std::shared_ptr io; + std::shared_ptr m_io; }; } // namespace AQNWB::NWB diff --git a/src/nwb/hdmf/base/Data.hpp b/src/nwb/hdmf/base/Data.hpp index ef146485..833b5f87 100644 --- a/src/nwb/hdmf/base/Data.hpp +++ b/src/nwb/hdmf/base/Data.hpp @@ -23,8 +23,23 @@ class Data ~Data() {} /** - * @brief Pointer to dataset. + * @brief Initialize the dataset for the Data object + * + * This functions takes ownership of the passed rvalue unique_ptr and moves + * ownership to its internal m_dataset variable + * + * @param dataset The rvalue unique pointer to the BaseRecordingData object */ - std::unique_ptr dataset; + inline void setDataset(std::unique_ptr&& dataset) + { + m_dataset = std::move(dataset); + } + + /** + * @brief Check whether the m_dataset has been initialized + */ + inline bool isInitialized() { return m_dataset != nullptr; } + + std::unique_ptr m_dataset; }; } // namespace AQNWB::NWB diff --git a/src/nwb/hdmf/table/DynamicTable.cpp b/src/nwb/hdmf/table/DynamicTable.cpp index d8b8c989..8fa217d4 100644 --- a/src/nwb/hdmf/table/DynamicTable.cpp +++ b/src/nwb/hdmf/table/DynamicTable.cpp @@ -7,9 +7,11 @@ using namespace AQNWB::NWB; /** Constructor */ DynamicTable::DynamicTable(const std::string& path, std::shared_ptr io, - const std::string& description) + const std::string& description, + const std::vector& colNames) : Container(path, io) - , description(description) + , m_description(description) + , m_colNames(colNames) { } @@ -21,9 +23,9 @@ void DynamicTable::initialize() { Container::initialize(); - io->createCommonNWBAttributes( - path, "hdmf-common", "DynamicTable", getDescription()); - io->createAttribute(getColNames(), path, "colnames"); + m_io->createCommonNWBAttributes( + m_path, "hdmf-common", "DynamicTable", getDescription()); + m_io->createAttribute(getColNames(), m_path, "colnames"); } /** Add column to table */ @@ -32,30 +34,30 @@ void DynamicTable::addColumn(const std::string& name, std::unique_ptr& vectorData, const std::vector& values) { - if (vectorData->dataset == nullptr) { + if (!vectorData->isInitialized()) { std::cerr << "VectorData dataset is not initialized" << std::endl; } else { // write in loop because variable length string for (SizeType i = 0; i < values.size(); i++) - vectorData->dataset->writeDataBlock( + vectorData->m_dataset->writeDataBlock( std::vector(1, 1), BaseDataType::STR(values[i].size() + 1), values[i].c_str()); // TODO - add tests for this - io->createCommonNWBAttributes( - path + name, "hdmf-common", "VectorData", colDescription); + m_io->createCommonNWBAttributes( + m_path + name, "hdmf-common", "VectorData", colDescription); } } void DynamicTable::setRowIDs(std::unique_ptr& elementIDs, const std::vector& values) { - if (elementIDs->dataset == nullptr) { + if (!elementIDs->isInitialized()) { std::cerr << "ElementIdentifiers dataset is not initialized" << std::endl; } else { - elementIDs->dataset->writeDataBlock( + elementIDs->m_dataset->writeDataBlock( std::vector(1, values.size()), BaseDataType::I32, &values[0]); - io->createCommonNWBAttributes( - path + "id", "hdmf-common", "ElementIdentifiers"); + m_io->createCommonNWBAttributes( + m_path + "id", "hdmf-common", "ElementIdentifiers"); } } @@ -66,20 +68,8 @@ void DynamicTable::addColumn(const std::string& name, if (values.empty()) { std::cerr << "Data to add to column is empty" << std::endl; } else { - io->createReferenceDataSet(path + name, values); - io->createCommonNWBAttributes( - path + name, "hdmf-common", "VectorData", colDescription); + m_io->createReferenceDataSet(m_path + name, values); + m_io->createCommonNWBAttributes( + m_path + name, "hdmf-common", "VectorData", colDescription); } } - -// Getter for description -std::string DynamicTable::getDescription() const -{ - return description; -} - -// Getter for colNames -const std::vector& DynamicTable::getColNames() -{ - return colNames; -} diff --git a/src/nwb/hdmf/table/DynamicTable.hpp b/src/nwb/hdmf/table/DynamicTable.hpp index 6cd8c2a5..0148c756 100644 --- a/src/nwb/hdmf/table/DynamicTable.hpp +++ b/src/nwb/hdmf/table/DynamicTable.hpp @@ -27,7 +27,8 @@ class DynamicTable : public Container */ DynamicTable(const std::string& path, std::shared_ptr io, - const std::string& description); + const std::string& description, + const std::vector& colNames); /** * @brief Destructor @@ -74,23 +75,35 @@ class DynamicTable : public Container * @brief Gets the description of the table. * @return The description of the table. */ - std::string getDescription() const; + inline std::string getDescription() const { return m_description; } /** * @brief Gets the column names of the table. * @return A vector of column names. */ - virtual const std::vector& getColNames() = 0; + virtual const std::vector& getColNames() const + { + return m_colNames; + } -private: + /** + * @brief Sets the column names of the ElectrodeTable. + * @param newColNames The vector of new column names. + */ + virtual void setColNames(const std::vector& newColNames) + { + m_colNames = newColNames; + } + +protected: /** * @brief Description of the DynamicTable. */ - std::string description; + std::string m_description; /** * @brief Names of the columns in the table. */ - std::vector colNames; + std::vector m_colNames; }; } // namespace AQNWB::NWB diff --git a/src/nwb/hdmf/table/VectorData.cpp b/src/nwb/hdmf/table/VectorData.cpp index 1438387e..e96945a5 100644 --- a/src/nwb/hdmf/table/VectorData.cpp +++ b/src/nwb/hdmf/table/VectorData.cpp @@ -1,9 +1,3 @@ #include "nwb/hdmf/table/VectorData.hpp" using namespace AQNWB::NWB; - -// VectorData -std::string VectorData::getDescription() const -{ - return description; -} diff --git a/src/nwb/hdmf/table/VectorData.hpp b/src/nwb/hdmf/table/VectorData.hpp index 7ee93f09..45ee737f 100644 --- a/src/nwb/hdmf/table/VectorData.hpp +++ b/src/nwb/hdmf/table/VectorData.hpp @@ -16,12 +16,12 @@ class VectorData : public Data * @brief Gets the description of the table. * @return The description of the table. */ - std::string getDescription() const; + inline std::string getDescription() const { return m_description; } private: /** * @brief Description of VectorData. */ - std::string description; + std::string m_description; }; } // namespace AQNWB::NWB diff --git a/tests/examples/testWorkflowExamples.cpp b/tests/examples/testWorkflowExamples.cpp index 64c7d309..9edde1cb 100644 --- a/tests/examples/testWorkflowExamples.cpp +++ b/tests/examples/testWorkflowExamples.cpp @@ -70,17 +70,18 @@ TEST_CASE("workflowExamples") const auto& channelVector = mockRecordingArrays[i]; for (const auto& channel : channelVector) { // copy data into buffer - std::copy(mockData[channel.globalIndex].begin() + samplesRecorded, - mockData[channel.globalIndex].begin() + samplesRecorded - + bufferSize, - dataBuffer.begin()); + std::copy( + mockData[channel.getGlobalIndex()].begin() + samplesRecorded, + mockData[channel.getGlobalIndex()].begin() + samplesRecorded + + bufferSize, + dataBuffer.begin()); std::copy(mockTimestamps.begin() + samplesRecorded, mockTimestamps.begin() + samplesRecorded + bufferSize, timestampsBuffer.begin()); // write timeseries data std::vector positionOffset = {samplesRecorded, - channel.localIndex}; + channel.getLocalIndex()}; std::vector dataShape = {dataBuffer.size(), 1}; std::unique_ptr intBuffer = transformToInt16( dataBuffer.size(), channel.getBitVolts(), dataBuffer.data()); diff --git a/tests/testRecordingWorkflow.cpp b/tests/testRecordingWorkflow.cpp index d5c8d05a..69b2326f 100644 --- a/tests/testRecordingWorkflow.cpp +++ b/tests/testRecordingWorkflow.cpp @@ -65,17 +65,18 @@ TEST_CASE("writeContinuousData", "[recording]") const auto& channelVector = mockRecordingArrays[i]; for (const auto& channel : channelVector) { // copy data into buffer - std::copy(mockData[channel.globalIndex].begin() + samplesRecorded, - mockData[channel.globalIndex].begin() + samplesRecorded - + bufferSize, - dataBuffer.begin()); + std::copy( + mockData[channel.getGlobalIndex()].begin() + samplesRecorded, + mockData[channel.getGlobalIndex()].begin() + samplesRecorded + + bufferSize, + dataBuffer.begin()); std::copy(mockTimestamps.begin() + samplesRecorded, mockTimestamps.begin() + samplesRecorded + bufferSize, timestampsBuffer.begin()); // write timeseries data std::vector positionOffset = {samplesRecorded, - channel.localIndex}; + channel.getLocalIndex()}; std::vector dataShape = {dataBuffer.size(), 1}; recordingContainers->writeTimeseriesData(i,