diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2ffaaed..26783ba 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -35,6 +35,7 @@ jobs: # - name: test # run: cd build && ctest - name: deploy + if: github.ref == 'refs/heads/main' env: artifactoryApiKey: ${{ secrets.artifactoryApiKey }} build_dir: "Build" diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 232cf13..b345fd3 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -33,6 +33,7 @@ jobs: # - name: test # run: cd build && ctest - name: deploy + if: github.ref == 'refs/heads/main' env: artifactoryApiKey: ${{ secrets.artifactoryApiKey }} build_dir: "Build/Release" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4f9eda5..170d0e3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,6 +54,7 @@ jobs: # - name: test # run: cd build && ctest - name: deploy + if: github.ref == 'refs/heads/main' env: artifactoryApiKey: ${{ secrets.artifactoryApiKey }} build_dir: "Build/Release" diff --git a/Source/OpenEphysLib.cpp b/Source/OpenEphysLib.cpp index 37b2300..933ecd4 100644 --- a/Source/OpenEphysLib.cpp +++ b/Source/OpenEphysLib.cpp @@ -40,7 +40,7 @@ extern "C" EXPORT void getLibInfo(Plugin::LibraryInfo* info) { info->apiVersion = PLUGIN_API_VER; info->name = "NWB2 Format"; - info->libVersion = "0.2.0"; + info->libVersion = "0.2.1"; info->numPlugins = NUM_PLUGINS; } diff --git a/Source/RecordEngine/NWBFormat.cpp b/Source/RecordEngine/NWBFormat.cpp index 50f4cc3..bf24c11 100644 --- a/Source/RecordEngine/NWBFormat.cpp +++ b/Source/RecordEngine/NWBFormat.cpp @@ -79,6 +79,17 @@ int NWBFile::createFileStructure() if (createGroup("/general")) return -1; if (createGroup("general/devices")) return -1; if (createGroup("general/extracellular_ephys")) return -1; + if (createGroup("general/extracellular_ephys/electrodes")) return -1; + + StringArray colnames; + colnames.add("group"); + colnames.add("group_name"); + setAttributeStrArray(colnames, "general/extracellular_ephys/electrodes", "colnames"); + setAttributeStr("metadata about extracellular electrodes", "general/extracellular_ephys/electrodes", "description"); + setAttributeStr("hdmf-common", "general/extracellular_ephys/electrodes", "namespace"); + setAttributeStr("DynamicTable", "general/extracellular_ephys/electrodes", "neurodata_type"); + setAttributeStr(generateUuid(), "general/extracellular_ephys/electrodes", "object_id"); + if (createGroup("/processing")) return -1; if (createGroup("/stimulus")) return -1; @@ -144,6 +155,9 @@ bool NWBFile::startNewRecording( eventDataSets.clearQuick(true); int totalChannelCount = 0; + Array all_electrode_inds; + StringArray groupNames; + StringArray groupReferences; // 1. Create continuous datasets for (int i = 0; i < continuousArray.size(); i++) @@ -158,16 +172,38 @@ bool NWBFile::startNewRecording( channel_conversion.add(group[ch]->getBitVolts() / 1e6); } + String groupName = group[0]->getSourceNodeName() + "-" + + String(group[0]->getSourceNodeId()) + + "." + group[0]->getStreamName(); + + String fullPath = "general/extracellular_ephys/" + groupName; + createGroup(fullPath); + setAttributeStr("description", fullPath, "description"); + setAttributeStr("unknown", fullPath, "location"); + setAttributeStr("core", fullPath, "namespace"); + setAttributeStr("ElectrodeGroup", fullPath, "neurodata_type"); + setAttributeStr(generateUuid(), fullPath, "object_id"); + + createGroup("general/devices/" + groupName); + + setAttributeStr("description", "general/devices/" + groupName, "description"); + setAttributeStr("unknown", "general/devices/" + groupName, "manufacturer"); + setAttributeStr("core", "general/devices/" + groupName, "namespace"); + setAttributeStr("Device", "general/devices/" + groupName, "neurodata_type"); + setAttributeStr(generateUuid(), "general/devices/" + groupName, "object_id"); + + createReference("/" + fullPath + "/device", "/general/devices/" + groupName); + Array electrode_inds; for (int ch = 0; ch < group.size(); ch++) { + int index = group[ch]->getGlobalIndex(); electrode_inds.add(totalChannelCount++); + all_electrode_inds.add(index); + groupNames.add(groupName); + groupReferences.add("/general/extracellular_ephys/" + groupName); } - String groupName = group[0]->getSourceNodeName() + "-" - + String(group[0]->getSourceNodeId()) - + "." + group[0]->getStreamName(); - ecephys::ElectricalSeries* electricalSeries = new ecephys::ElectricalSeries(rootPath, groupName, @@ -235,7 +271,19 @@ bool NWBFile::startNewRecording( for (int ch = 0; ch < sourceInfo->getNumChannels(); ch++) { - electrode_inds.add(sourceInfo->getSourceChannels()[0]->getGlobalIndex()); + int globalIndex = sourceInfo->getSourceChannels()[ch]->getGlobalIndex(); + int tableIndex = all_electrode_inds.indexOf(globalIndex); + + if (tableIndex > -1) + { + electrode_inds.add(tableIndex); + } + else + { + // If we don't have a matching electrode row, just use zero for now. + // In the future this should create a new table row. + electrode_inds.add(0); + } } ecephys::SpikeEventSeries* spikeEventSeries = @@ -385,6 +433,36 @@ bool NWBFile::startNewRecording( syncMsgDataSet.reset(annotationSeries); + // 5. Create electrode table + ScopedPointer elSet = createDataSet(BaseDataType::I32, 1, 1, "general/extracellular_ephys/electrodes/id"); + + std::vector electrodeNumbers; + for (auto i : all_electrode_inds) + electrodeNumbers.push_back(i); + + CHECK_ERROR(elSet->writeDataBlock(electrodeNumbers.size(), BaseDataType::I32, &electrodeNumbers[0])); + + setAttributeStr("hdmf-common", "general/extracellular_ephys/electrodes/id", "namespace"); + setAttributeStr("ElementIdentifiers", "general/extracellular_ephys/electrodes/id", "neurodata_type"); + setAttributeStr(generateUuid(), "general/extracellular_ephys/electrodes/id", "object_id"); + + ScopedPointer groupNamesDataset = createDataSet(BaseDataType::STR(250), 0, 1, "general/extracellular_ephys/electrodes/group_name"); + + for (int i = 0; i < groupNames.size(); i++) + groupNamesDataset->writeDataBlock(1, BaseDataType::STR(groupNames[i].length()), groupNames[i].toUTF8()); + + setAttributeStr("the name of the ElectrodeGroup this electrode is a part of", "general/extracellular_ephys/electrodes/group_name", "description"); + setAttributeStr("hdmf-common", "general/extracellular_ephys/electrodes/group_name", "namespace"); + setAttributeStr("VectorData", "general/extracellular_ephys/electrodes/group_name", "neurodata_type"); + setAttributeStr(generateUuid(), "general/extracellular_ephys/electrodes/group_name", "object_id"); + + createReferenceDataSet("general/extracellular_ephys/electrodes/group", groupReferences); + + setAttributeStr("a reference to the ElectrodeGroup this electrode is a part of", "general/extracellular_ephys/electrodes/group", "description"); + setAttributeStr("hdmf-common", "general/extracellular_ephys/electrodes/group", "namespace"); + setAttributeStr("VectorData", "general/extracellular_ephys/electrodes/group", "neurodata_type"); + setAttributeStr(generateUuid(), "general/extracellular_ephys/electrodes/group", "object_id"); + return true; } @@ -643,6 +721,8 @@ HDF5RecordingData *NWBFile::createElectrodeDataSet(String path, String descripti CHECK_ERROR(setAttributeStr(description, path, "description")); CHECK_ERROR(setAttributeStr("hdmf-common", path, "namespace")); CHECK_ERROR(setAttributeStr("DynamicTableRegion", path, "neurodata_type")); + CHECK_ERROR(setAttributeStr(generateUuid(), path, "object_id")); + CHECK_ERROR(setAttributeRef("general/extracellular_ephys/electrodes", path, "table")); } return elSet; }