├── .gitattributes ├── .gitignore ├── Hdf5DotNetTools.sln ├── Hdf5DotNetTools ├── ChunkedDataset.cs ├── DataProducerConsumer.cs ├── Hdf5AcquisitionFile.cs ├── Hdf5AcquisitionReader.cs ├── Hdf5AcquisitionWriter.cs ├── Hdf5Attributes.cs ├── Hdf5Common.cs ├── Hdf5Compounds.cs ├── Hdf5Dataset.cs ├── Hdf5DotNetTools.csproj ├── Hdf5DotNetTools.nuspec ├── Hdf5DotNetTools.snk ├── Hdf5Groups.cs ├── Hdf5ReadWrite.cs ├── Hdf5Reader.cs ├── Hdf5Strings.cs ├── Hdf5Writer.cs ├── Knf.Utils.Hdf5.snk └── copy nuget.bat ├── Hdf5DotnetTools ├── DataTypes │ ├── FileClosedArgs.cs │ ├── Hdf5Channel.cs │ ├── Hdf5Channels.cs │ ├── Hdf5Event.cs │ ├── Hdf5Events.cs │ ├── Hdf5Patient.cs │ └── Hdf5Recording.cs └── Interfaces │ └── IHdf5AcquisitionFile.cs ├── Hdf5Tests ├── Hdf5AcquisitionFileTests.cs ├── Hdf5AttributeTests.cs ├── Hdf5CompoundTests.cs ├── Hdf5DatasetTests.cs ├── Hdf5GroupTests.cs ├── Hdf5ObjectTests.cs ├── Hdf5StringsTests.cs ├── Hdf5Tests.cs ├── Hdf5UnitTests.csproj ├── KamaTests.cs ├── Properties │ └── AssemblyInfo.cs └── TestUtilities.cs ├── README.md ├── license.md └── misc └── HDF.Pinvoke.snk /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | \copy nuget.bat 2 | ## Ignore Visual Studio temporary files, build results, and 3 | ## files generated by popular Visual Studio add-ons. 4 | 5 | # User-specific files 6 | *.suo 7 | *.user 8 | *.userosscache 9 | *.sln.docstates 10 | 11 | # User-specific files (MonoDevelop/Xamarin Studio) 12 | *.userprefs 13 | 14 | # Build results 15 | [Dd]ebug/ 16 | [Dd]ebugPublic/ 17 | [Rr]elease/ 18 | [Rr]eleases/ 19 | x64/ 20 | x86/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | [Ll]og/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | *.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # Windows image file caches 257 | Thumbs.db 258 | ehthumbs.db 259 | 260 | # Folder config file 261 | Desktop.ini 262 | 263 | # Recycle Bin used on file shares 264 | $RECYCLE.BIN/ 265 | 266 | # Windows Installer files 267 | *.cab 268 | *.msi 269 | *.msm 270 | *.msp 271 | 272 | # Windows shortcuts 273 | *.lnk 274 | 275 | # ========================= 276 | # Operating System Files 277 | # ========================= 278 | 279 | # OSX 280 | # ========================= 281 | 282 | .DS_Store 283 | .AppleDouble 284 | .LSOverride 285 | 286 | # Thumbnails 287 | ._* 288 | 289 | # Files that might appear in the root of a volume 290 | .DocumentRevisions-V100 291 | .fseventsd 292 | .Spotlight-V100 293 | .TemporaryItems 294 | .Trashes 295 | .VolumeIcon.icns 296 | 297 | # Directories potentially created on remote AFP share 298 | .AppleDB 299 | .AppleDesktop 300 | Network Trash Folder 301 | Temporary Items 302 | .apdisk 303 | -------------------------------------------------------------------------------- /Hdf5DotNetTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29519.87 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hdf5UnitTests", "Hdf5Tests\Hdf5UnitTests.csproj", "{8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hdf5DotnetTools", "Hdf5DotnetTools\Hdf5DotnetTools.csproj", "{7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | HDF5 1.10 Debug|Any CPU = HDF5 1.10 Debug|Any CPU 15 | HDF5 1.10 Debug|x64 = HDF5 1.10 Debug|x64 16 | HDF5 1.10 Release|Any CPU = HDF5 1.10 Release|Any CPU 17 | HDF5 1.10 Release|x64 = HDF5 1.10 Release|x64 18 | HDF5 1.8 Debug|Any CPU = HDF5 1.8 Debug|Any CPU 19 | HDF5 1.8 Debug|x64 = HDF5 1.8 Debug|x64 20 | HDF5 1.8 Release|Any CPU = HDF5 1.8 Release|Any CPU 21 | HDF5 1.8 Release|x64 = HDF5 1.8 Release|x64 22 | Release|Any CPU = Release|Any CPU 23 | Release|x64 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Debug|x64.ActiveCfg = Debug|x64 29 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Debug|x64.Build.0 = Debug|x64 30 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Debug|x64.ActiveCfg = Debug|x64 33 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Debug|x64.Build.0 = Debug|x64 34 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Release|Any CPU.Build.0 = Release|Any CPU 36 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Release|x64.ActiveCfg = Release|x64 37 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.10 Release|x64.Build.0 = Release|x64 38 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Debug|x64.ActiveCfg = Debug|x64 41 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Debug|x64.Build.0 = Debug|x64 42 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Release|Any CPU.Build.0 = Release|Any CPU 44 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Release|x64.ActiveCfg = Release|x64 45 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.HDF5 1.8 Release|x64.Build.0 = Release|x64 46 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Release|x64.ActiveCfg = Release|x64 49 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7}.Release|x64.Build.0 = Release|x64 50 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Debug|x64.Build.0 = Debug|Any CPU 54 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Debug|x64.ActiveCfg = Debug|Any CPU 57 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Debug|x64.Build.0 = Debug|Any CPU 58 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Release|Any CPU.Build.0 = Release|Any CPU 60 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Release|x64.ActiveCfg = Release|Any CPU 61 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.10 Release|x64.Build.0 = Release|Any CPU 62 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Debug|x64.ActiveCfg = Debug|Any CPU 65 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Debug|x64.Build.0 = Debug|Any CPU 66 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Release|Any CPU.Build.0 = Release|Any CPU 68 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Release|x64.ActiveCfg = Release|Any CPU 69 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.HDF5 1.8 Release|x64.Build.0 = Release|Any CPU 70 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Release|x64.ActiveCfg = Release|Any CPU 73 | {7E7B1235-2D9C-4696-9FFB-CF7E14C7202A}.Release|x64.Build.0 = Release|Any CPU 74 | EndGlobalSection 75 | GlobalSection(SolutionProperties) = preSolution 76 | HideSolutionNode = FALSE 77 | EndGlobalSection 78 | GlobalSection(ExtensibilityGlobals) = postSolution 79 | SolutionGuid = {FF170207-E294-49AB-96B4-37ACC61A6217} 80 | EndGlobalSection 81 | EndGlobal 82 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/ChunkedDataset.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Hdf5DotNetTools 7 | { 8 | #if HDF5_VER1_10 9 | using hid_t = System.Int64; 10 | #else 11 | using hid_t = System.Int32; 12 | #endif 13 | public class ChunkedDataset : IDisposable where T : struct 14 | { 15 | ulong[] currentDims, oldDims; 16 | readonly ulong[] maxDims = new ulong[] { H5S.UNLIMITED, H5S.UNLIMITED }; 17 | readonly ulong[] chunkDims; 18 | hid_t status, spaceId, datasetId, propId; 19 | readonly hid_t typeId, datatype; 20 | 21 | /// 22 | /// Constructor to create a chuncked dataset object 23 | /// 24 | /// 25 | /// 26 | /// 27 | public ChunkedDataset(string name, hid_t groupId, ulong[] chunckSize) 28 | { 29 | //Datasetname = Hdf5.ToHdf5Name(name); 30 | Datasetname = name; 31 | GroupId = groupId; 32 | datatype = Hdf5.GetDatatype(typeof(T)); 33 | typeId = H5T.copy(datatype); 34 | chunkDims = chunckSize; 35 | } 36 | 37 | /// 38 | /// Constructor to create a chuncked dataset object with an initial dataset. 39 | /// 40 | /// 41 | /// 42 | /// 43 | public ChunkedDataset(string name, hid_t groupId, T[,] dataset) : this(name, groupId, new ulong[] { Convert.ToUInt64(dataset.GetLongLength(0)), Convert.ToUInt64(dataset.GetLongLength(1)) }) 44 | { 45 | FirstDataset(dataset); 46 | } 47 | 48 | public void FirstDataset(Array dataset) 49 | { 50 | if (FalseGroupId) throw new Exception("cannot call FirstDataset because group or file couldn't be created"); 51 | if (DatasetExists) throw new Exception("cannot call FirstDataset because dataset already exists"); 52 | 53 | Rank = dataset.Rank; 54 | currentDims = GetDims(dataset); 55 | 56 | /* Create the data space with unlimited dimensions. */ 57 | spaceId = H5S.create_simple(Rank, currentDims, maxDims); 58 | 59 | /* Modify dataset creation properties, i.e. enable chunking */ 60 | propId = H5P.create(H5P.DATASET_CREATE); 61 | status = H5P.set_chunk(propId, Rank, chunkDims); 62 | 63 | /* Create a new dataset within the file using chunk creation properties. */ 64 | datasetId = H5D.create(GroupId, Datasetname, datatype, spaceId, H5P.DEFAULT, propId, H5P.DEFAULT); 65 | 66 | /* Write data to dataset */ 67 | GCHandle hnd = GCHandle.Alloc(dataset, GCHandleType.Pinned); 68 | status = H5D.write(datasetId, datatype, H5S.ALL, H5S.ALL, H5P.DEFAULT, 69 | hnd.AddrOfPinnedObject()); 70 | hnd.Free(); 71 | H5S.close(spaceId); 72 | } 73 | 74 | public void AppendDataset(Array dataset) 75 | { 76 | if (!DatasetExists) throw new Exception("call constructor or FirstDataset first before appending."); 77 | oldDims = currentDims; 78 | currentDims = GetDims(dataset); 79 | int rank = dataset.Rank; 80 | ulong[] zeros = Enumerable.Range(0, rank).Select(z => (ulong)0).ToArray(); 81 | 82 | /* Extend the dataset. Dataset becomes 10 x 3 */ 83 | var size = new ulong[] { oldDims[0] + currentDims[0] }.Concat(oldDims.Skip(1)).ToArray(); 84 | 85 | status = H5D.set_extent(datasetId, size); 86 | ulong[] offset = new ulong[] { oldDims[0] }.Concat(zeros.Skip(1)).ToArray(); 87 | 88 | /* Select a hyperslab in extended portion of dataset */ 89 | var filespaceId = H5D.get_space(datasetId); 90 | status = H5S.select_hyperslab(filespaceId, H5S.seloper_t.SET, offset, null, 91 | currentDims, null); 92 | 93 | /* Define memory space */ 94 | var memId = H5S.create_simple(Rank, currentDims, null); 95 | 96 | /* Write the data to the extended portion of dataset */ 97 | GCHandle hnd = GCHandle.Alloc(dataset, GCHandleType.Pinned); 98 | status = H5D.write(datasetId, datatype, memId, filespaceId, 99 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 100 | hnd.Free(); 101 | 102 | currentDims = size; 103 | H5S.close(memId); 104 | H5S.close(filespaceId); 105 | } 106 | 107 | /// 108 | /// Finalizer of object 109 | /// 110 | ~ChunkedDataset() 111 | { 112 | Dispose(false); 113 | } 114 | 115 | /// 116 | /// Dispose function as suggested in the stackoverflow discussion below 117 | /// See: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface/538238#538238 118 | /// 119 | /// 120 | protected virtual void Dispose(bool itIsSafeToAlsoFreeManagedObjects) 121 | { 122 | H5D.close(datasetId); 123 | H5P.close(propId); 124 | H5S.close(spaceId); 125 | 126 | if (itIsSafeToAlsoFreeManagedObjects) 127 | { 128 | 129 | } 130 | } 131 | 132 | private ulong[] GetDims(Array dset) 133 | { 134 | return Enumerable.Range(0, dset.Rank).Select(i => 135 | { return (ulong)dset.GetLength(i); }).ToArray(); 136 | } 137 | 138 | /// 139 | /// Dispose function as suggested in the stackoverflow discussion below 140 | /// See: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface/538238#538238 141 | /// 142 | public void Dispose() 143 | { 144 | Dispose(true); //I am calling you from Dispose, it's safe 145 | GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later 146 | } 147 | 148 | public string Datasetname { get; private set; } 149 | public int Rank { get; private set; } 150 | public hid_t GroupId { get; private set; } 151 | protected bool DatasetExists => H5L.exists(GroupId, Datasetname) > 0; 152 | protected bool FalseGroupId => GroupId <= 0; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/DataProducerConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | 6 | namespace Hdf5DotNetTools 7 | { 8 | public class DataProducerConsumer : IDisposable 9 | { 10 | private BlockingCollection _queue = new BlockingCollection(); 11 | private readonly Action _action; 12 | private readonly int _milliSeconds; 13 | 14 | public DataProducerConsumer(Action action, int milliSeconds = 0) 15 | { 16 | _milliSeconds = milliSeconds; 17 | _action = action; 18 | 19 | var thread = new Thread(StartConsuming) 20 | { 21 | IsBackground = true 22 | }; 23 | thread.Start(); 24 | } 25 | 26 | public void Dispose() 27 | { 28 | Dispose(true); 29 | GC.SuppressFinalize(this); 30 | } 31 | 32 | protected virtual void Dispose(bool disposing) 33 | { 34 | if (disposing) 35 | { 36 | _queue.Dispose(); 37 | } 38 | } 39 | 40 | public void Done() 41 | { 42 | _queue.CompleteAdding(); 43 | } 44 | 45 | public void Produce(T item) 46 | { 47 | _queue.Add(item); 48 | } 49 | 50 | private void StartConsuming() 51 | { 52 | while (!_queue.IsCompleted) 53 | { 54 | try 55 | { 56 | if (_queue == null) // || _queue.Count==0 57 | return; 58 | _queue.TryTake(out T data); 59 | if (data != null) 60 | { 61 | Debug.WriteLine($"item {_queue.Count + 1} in queue will be processed"); 62 | _action(data); 63 | } 64 | 65 | } 66 | catch (InvalidOperationException) 67 | { 68 | Debug.WriteLine(string.Format("Work queue on thread {0} has been closed.", Thread.CurrentThread.ManagedThreadId)); 69 | } 70 | } 71 | IsDone?.Invoke(this, new EventArgs()); 72 | Dispose(); 73 | } 74 | 75 | public event EventHandler IsDone; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5AcquisitionFile.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotnetTools.DataTypes; 2 | using Hdf5DotnetTools.Interfaces; 3 | using System.Collections.Generic; 4 | 5 | namespace Hdf5DotNetTools 6 | { 7 | 8 | [Hdf5SaveAttribute(Hdf5Save.Save)] 9 | public class Hdf5AcquisitionFile : IHdf5AcquisitionFile 10 | { 11 | public Hdf5Patient Patient { get; set; } 12 | public Hdf5Recording Recording { get; set; } 13 | public Hdf5Channel[] Channels { get; set; } 14 | public Hdf5Events Events { get; set; } 15 | [Hdf5Save(Hdf5Save.DoNotSave)] 16 | public List EventList { get; private set; } 17 | 18 | [Hdf5Save(Hdf5Save.DoNotSave)] 19 | public short[,] Data { get; set; } 20 | 21 | public Hdf5AcquisitionFile() 22 | { 23 | Patient = new Hdf5Patient(); 24 | Recording = new Hdf5Recording(); 25 | EventList = new List(); 26 | Events = new Hdf5Events(); 27 | 28 | Recording.PropertyChanged += (sender, eventArgs) => 29 | { 30 | if (eventArgs.PropertyName == nameof(Hdf5Recording.NrOfChannels)) 31 | Channels = new Hdf5Channel[Recording.NrOfChannels]; 32 | }; 33 | 34 | } 35 | 36 | 37 | public void EventListToEvents() 38 | { 39 | Events = new Hdf5Events(EventList.Count); 40 | for (int i = 0; i < EventList.Count; i++) 41 | { 42 | Events.Times[i] = EventList[i].Time; 43 | Events.Durations[i] = EventList[i].Duration; 44 | Events.Events[i] = EventList[i].Event; 45 | } 46 | 47 | } 48 | 49 | } 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5AcquisitionReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Hdf5DotNetTools 6 | { 7 | 8 | /*struct ReadInfo { 9 | public ReadInfo() 10 | { 11 | 12 | } 13 | }*/ 14 | 15 | public class Hdf5AcquisitionFileReader : IDisposable 16 | { 17 | 18 | long fileId; 19 | Hdf5AcquisitionFile _header; 20 | IList _labels; 21 | IList _signals; 22 | Dictionary _usedChannels; 23 | readonly int /*_fileChannelCnt,*/ _readChannelCnt; 24 | readonly string _groupName; 25 | 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// A filename. 31 | /// a root group. If not specified used "ROOT 32 | public Hdf5AcquisitionFileReader(string filename, string[] labels = null, string groupName = "ROOT") 33 | { 34 | fileId = Hdf5.OpenFile(filename, readOnly: true); 35 | _header = Hdf5.ReadObject(fileId, groupName); 36 | _groupName = groupName; 37 | 38 | _usedChannels = new Dictionary(); 39 | for (short i = 0; i < _header.Recording.NrOfChannels; i++) 40 | _usedChannels.Add(_header.Channels[i].Label, i); 41 | if (labels == null) 42 | _labels = _header.Channels.Select(c => c.Label).ToList(); 43 | else 44 | _labels = labels; 45 | _readChannelCnt = _labels.Count; 46 | _signals = new List(_readChannelCnt); 47 | } 48 | 49 | /// 50 | /// Reads this instance. 51 | /// 52 | public IList Read() 53 | { 54 | return Read(_header.Recording.StartTime, _header.Recording.EndTime); 55 | } 56 | 57 | /// 58 | /// Reads this instance from a specified start index to a specified end index. 59 | /// 60 | /// The start index. 61 | /// The end index. 62 | public IList Read(ulong startIndex, ulong endIndex) 63 | { 64 | _signals.Clear(); 65 | int rows = Convert.ToInt32(endIndex - startIndex + 1); 66 | int cols = _labels.Count(); 67 | var dataName = string.Concat("/", _groupName, "/Data"); 68 | short[,] data = Hdf5.ReadDataset(fileId, dataName, startIndex, endIndex); 69 | IEnumerable dataEnum = data.Cast(); 70 | 71 | 72 | int byteLength = sizeof(short) * rows; 73 | TimeSpan zeroSpan = new TimeSpan(0); 74 | for (int i = 0; i < _readChannelCnt; i++) 75 | { 76 | //_signals.Add(new short[rows]); 77 | string lbl = _labels[i]; 78 | int nr = _usedChannels[lbl]; 79 | int pos = nr * byteLength; 80 | var ar = dataEnum.Where((d, j) => (j - nr) % cols == 0); 81 | _signals.Add(ar.ToArray()); 82 | //Buffer.BlockCopy(data, pos, _signals[i], 0, byteLength); 83 | } 84 | return _signals; 85 | } 86 | 87 | /// 88 | /// Reads this instance from a specified start time to a specified end time. 89 | /// 90 | public IList ReadDouble() 91 | { 92 | TimeSpan oneSample = TimeSpan.FromSeconds(1 / _header.Recording.SampleRate); 93 | return ReadDouble(_header.Recording.StartTime, _header.Recording.EndTime.Subtract(oneSample)); 94 | } 95 | 96 | /// 97 | /// Reads this instance from a specified start time to a specified end time. 98 | /// 99 | /// The start index. 100 | /// The end index. 101 | public IList ReadDouble(ulong startIndex, ulong endIndex) 102 | { 103 | Read(startIndex, endIndex); 104 | int rows = Convert.ToInt32(endIndex - startIndex + 1); 105 | var dblList = new List(_readChannelCnt); 106 | for (int i = 0; i < _readChannelCnt; i++) 107 | { 108 | var sig = _signals[i]; 109 | string lbl = _labels[i]; 110 | int nr = _usedChannels[lbl]; 111 | double amp = _header.Channels[nr].Amplification; 112 | double offset = _header.Channels[nr].Offset; 113 | dblList.Add(sig.Select(s => s * amp + offset).ToArray()); 114 | 115 | } 116 | return dblList; 117 | } 118 | 119 | /// 120 | /// Reads this instance from a specified start time to a specified end time. 121 | /// 122 | /// The start time. 123 | /// The end time. 124 | public IList ReadDouble(DateTime startTime, DateTime endTime) 125 | { 126 | double sr = _header.Recording.SampleRate; 127 | TimeSpan startSpan = startTime - _header.Recording.StartTime; 128 | TimeSpan endSpan = endTime - _header.Recording.StartTime; 129 | ulong startIndex = Convert.ToUInt64(Math.Round(startSpan.TotalSeconds * sr, MidpointRounding.AwayFromZero)); 130 | ulong endIndex = Convert.ToUInt64(Math.Round(endSpan.TotalSeconds * sr)) - 1; 131 | return ReadDouble(startIndex, endIndex); 132 | } 133 | 134 | /// 135 | /// Reads this instance from a specified start time to a specified end time. 136 | /// 137 | /// The start time. 138 | /// The end time. 139 | public IList Read(DateTime startTime, DateTime endTime) 140 | { 141 | double sr = _header.Recording.SampleRate; 142 | TimeSpan startSpan = startTime - _header.Recording.StartTime; 143 | TimeSpan endSpan = endTime - _header.Recording.StartTime; 144 | ulong startIndex = Convert.ToUInt64(Math.Round(startSpan.TotalSeconds * sr, MidpointRounding.AwayFromZero)); 145 | ulong endIndex = Convert.ToUInt64(Math.Round(endSpan.TotalSeconds * sr)) - 1; 146 | return Read(startIndex, endIndex); 147 | } 148 | 149 | public void Dispose() 150 | { 151 | Dispose(true); 152 | GC.SuppressFinalize(this); 153 | } 154 | 155 | protected virtual void Dispose(bool disposing) 156 | { 157 | if (disposing) 158 | { 159 | fileId = Hdf5.CloseFile(fileId); 160 | _signals.Clear(); 161 | } 162 | } 163 | 164 | public Hdf5AcquisitionFile Header => _header; 165 | 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5AcquisitionWriter.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using Hdf5DotnetTools.DataTypes; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace Hdf5DotNetTools 10 | { 11 | 12 | 13 | public class Hdf5AcquisitionFileWriter : IDisposable 14 | { 15 | long fileId; 16 | readonly string _groupName, _filename; 17 | ChunkedDataset dset = null; 18 | ulong _nrOfRecords, _sampleCount; 19 | long _groupId; 20 | 21 | // private readonly ReaderWriterLockSlim lock_ = new ReaderWriterLockSlim(); 22 | 23 | public Hdf5AcquisitionFileWriter(string filename, string groupName = "ROOT") 24 | { 25 | H5E.set_auto(H5E.DEFAULT, null, IntPtr.Zero); 26 | //lock_.EnterWriteLock(); 27 | _filename = filename; 28 | fileId = Hdf5.CreateFile(filename); 29 | _groupName = groupName; 30 | _groupId = Hdf5.CreateGroup(fileId, _groupName); 31 | 32 | Header = new Hdf5AcquisitionFile(); 33 | _nrOfRecords = 0; 34 | _sampleCount = 0; 35 | //lock_.ExitWriteLock(); 36 | 37 | } 38 | 39 | public void Dispose() 40 | { 41 | Dispose(true); 42 | GC.SuppressFinalize(this); 43 | FileClosed?.Invoke(this, new FileClosedArgs(_filename)); 44 | } 45 | 46 | protected virtual void Dispose(bool disposing) 47 | { 48 | if (disposing) 49 | { 50 | SaveHeader(); 51 | dset?.Dispose(); 52 | var info = Hdf5.GroupInfo(_groupId); 53 | _groupId = Hdf5.CloseGroup(_groupId); 54 | fileId = Hdf5.CloseFile(fileId); 55 | } 56 | } 57 | 58 | public void SaveHeader() 59 | { 60 | //lock_.EnterWriteLock(); 61 | Trace.WriteLine($"saving file {Header.Patient.Name} samples: {_sampleCount}; fileId: {fileId}"); 62 | Header.Recording.EndTime = Header.Recording.StartTime + TimeSpan.FromSeconds(_sampleCount / Header.Recording.SampleRate); 63 | Header.Recording.NrOfSamples = _sampleCount; 64 | Header.EventListToEvents(); 65 | for (int i = 0; i < Header.Channels.Count(); i++) 66 | { 67 | Header.Channels[i].NrOfSamples = _sampleCount; 68 | } 69 | Trace.WriteLine($"writing file {Header.Patient.Name} groupId: {_groupId}; fileId: {fileId}"); 70 | Hdf5.WriteObject(_groupId, Header); 71 | //lock_.ExitWriteLock(); 72 | } 73 | 74 | /// 75 | /// Writes data to the hdf5 file. 76 | /// 77 | public void Write(IEnumerable signals, bool setDatetime = true) 78 | { 79 | //lock_.EnterWriteLock(); 80 | int cols = signals.Count(); 81 | if (cols == 0) return; 82 | int rows = signals.First().Length; 83 | if (rows == 0) return; 84 | //double sr = _header.Recording.SampleRate; 85 | 86 | var data = new short[rows, cols]; 87 | //var byteLength = rows * sizeof(short); 88 | int i = 0; 89 | foreach (var sig in signals) 90 | { 91 | for (int j = 0; j < rows; j++) 92 | data[j, i] = Convert2Short(sig[j], i); 93 | i++; 94 | } 95 | Write(data, setDatetime); 96 | //lock_.ExitWriteLock(); 97 | } 98 | 99 | /// 100 | /// Writes data asynchronously to the hdf5 file. 101 | /// 102 | public async void WriteAsync(IEnumerable signals) 103 | { 104 | Task writeTask = new Task(() => Write(signals)); 105 | writeTask.Start(); 106 | await writeTask; 107 | } 108 | 109 | /// 110 | /// Writes data to the hdf5 file. 111 | /// 112 | public void Write(short[,] data, bool setDatetime = true) 113 | { 114 | //lock_.EnterWriteLock(); 115 | if (_nrOfRecords == 0) 116 | { 117 | if (setDatetime) 118 | Header.Recording.StartTime = DateTime.Now; 119 | var dataName = "Data"; 120 | dset = new ChunkedDataset(dataName, _groupId, data); 121 | } 122 | else 123 | dset.AppendDataset(data); 124 | _sampleCount += (ulong)data.GetLongLength(0); 125 | _nrOfRecords++; 126 | //lock_.ExitWriteLock(); 127 | } 128 | 129 | /// 130 | /// Writes data asynchronously to the hdf5 file. 131 | /// 132 | public async void WriteAsync(short[,] data) 133 | { 134 | Task writeTask = new Task(() => Write(data)); 135 | writeTask.Start(); 136 | await writeTask; 137 | } 138 | 139 | public short Convert2Short(double val, int channelNr) 140 | { 141 | val = (val - Header.Channels[channelNr].Offset) / Header.Channels[channelNr].Amplification; 142 | //val = val * short.MaxValue; 143 | if (val > short.MaxValue) 144 | val = short.MaxValue; 145 | if (val < short.MinValue) 146 | val = short.MinValue; 147 | return Convert.ToInt16(Math.Round(val, MidpointRounding.AwayFromZero)); 148 | 149 | } 150 | 151 | public Hdf5AcquisitionFile Header { get; } 152 | 153 | public event EventHandler FileClosed; 154 | 155 | } 156 | 157 | 158 | } 159 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Attributes.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace Hdf5DotNetTools 10 | { 11 | #if HDF5_VER1_10 12 | using hid_t = System.Int64; 13 | #else 14 | using hid_t = System.Int32; 15 | #endif 16 | [System.AttributeUsage(System.AttributeTargets.Class | 17 | System.AttributeTargets.Struct)] 18 | public sealed class Hdf5GroupName : Attribute 19 | { 20 | 21 | public Hdf5GroupName(string name) 22 | { 23 | Name = name; 24 | } 25 | 26 | public string Name { get; private set; } 27 | } 28 | 29 | public sealed class Hdf5Attributes : Attribute 30 | { 31 | 32 | public Hdf5Attributes(string[] names) 33 | { 34 | Names = names; 35 | } 36 | 37 | public string[] Names { get; private set; } 38 | } 39 | 40 | public sealed class Hdf5Attribute : Attribute 41 | { 42 | 43 | public Hdf5Attribute(string name) 44 | { 45 | Name = name; 46 | } 47 | 48 | public string Name { get; private set; } 49 | } 50 | 51 | public static partial class Hdf5 52 | { 53 | private static Hdf5ReaderWriter attrRW = new Hdf5ReaderWriter(new Hdf5AttributeRW()); 54 | 55 | public static Array ReadAttributes(hid_t groupId, string name) 56 | { 57 | return attrRW.ReadArray(groupId, name); 58 | /*if (typeof(T) == typeof(string)) 59 | return ReadStringAttributes(groupId, name).Cast().ToArray(); 60 | else 61 | return ReadPrimitiveAttributes(groupId, name);*/ 62 | } 63 | 64 | public static T ReadAttribute(hid_t groupId, string name) 65 | { 66 | var attrs = attrRW.ReadArray(groupId, name); 67 | int[] first = new int[attrs.Rank].Select(f => 0).ToArray(); 68 | T result = (T)attrs.GetValue(first); 69 | return result; 70 | } 71 | public static IEnumerable ReadStringAttributes(hid_t groupId, string name, string attributeName = null) 72 | { 73 | 74 | hid_t attrId; 75 | if (string.IsNullOrEmpty(attributeName)) 76 | attrId = H5A.open(groupId, name); 77 | else 78 | { 79 | var datasetId = H5D.open(groupId, name); 80 | attrId = H5A.open(datasetId, attributeName); 81 | H5D.close(datasetId); 82 | } 83 | 84 | long typeId = H5A.get_type(attrId); 85 | long spaceId = H5A.get_space(attrId); 86 | long count = H5S.get_simple_extent_npoints(spaceId); 87 | H5S.close(spaceId); 88 | 89 | IntPtr[] rdata = new IntPtr[count]; 90 | GCHandle hnd = GCHandle.Alloc(rdata, GCHandleType.Pinned); 91 | H5A.read(attrId, typeId, hnd.AddrOfPinnedObject()); 92 | 93 | var strs = new List(); 94 | for (int i = 0; i < rdata.Length; ++i) 95 | { 96 | if (rdata[i] == IntPtr.Zero) 97 | { 98 | continue; 99 | } 100 | int len = 0; 101 | while (Marshal.ReadByte(rdata[i], len) != 0) { ++len; } 102 | byte[] buffer = new byte[len]; 103 | Marshal.Copy(rdata[i], buffer, 0, buffer.Length); 104 | string s = Encoding.UTF8.GetString(buffer); 105 | strs.Add(s); 106 | 107 | H5.free_memory(rdata[i]); 108 | } 109 | 110 | hnd.Free(); 111 | H5T.close(typeId); 112 | H5A.close(attrId); 113 | return (strs); 114 | } 115 | 116 | public static Array ReadPrimitiveAttributes(hid_t groupId, string name) //where T : struct 117 | { 118 | Type type = typeof(T); 119 | var datatype = GetDatatype(type); 120 | 121 | var attributeId = H5A.open(groupId, name); 122 | var spaceId = H5A.get_space(attributeId); 123 | int rank = H5S.get_simple_extent_ndims(spaceId); 124 | ulong[] maxDims = new ulong[rank]; 125 | ulong[] dims = new ulong[rank]; 126 | hid_t memId = H5S.get_simple_extent_dims(spaceId, dims, maxDims); 127 | long[] lengths = dims.Select(d => Convert.ToInt64(d)).ToArray(); 128 | Array attributes = Array.CreateInstance(type, lengths); 129 | 130 | var typeId = H5A.get_type(attributeId); 131 | var mem_type = H5T.copy(datatype); 132 | if (datatype == H5T.C_S1) 133 | H5T.set_size(datatype, new IntPtr(2)); 134 | 135 | var propId = H5A.get_create_plist(attributeId); 136 | 137 | memId = H5S.create_simple(rank, dims, maxDims); 138 | GCHandle hnd = GCHandle.Alloc(attributes, GCHandleType.Pinned); 139 | H5A.read(attributeId, datatype, hnd.AddrOfPinnedObject()); 140 | hnd.Free(); 141 | H5A.close(typeId); 142 | H5A.close(attributeId); 143 | H5S.close(spaceId); 144 | return attributes; 145 | } 146 | 147 | public static (int success, hid_t attributeId) WriteStringAttribute(hid_t groupId, string name, string str, string datasetName = null) 148 | { 149 | return WriteStringAttributes(groupId, name, new string[] { str }, datasetName); 150 | } 151 | 152 | public static (int success, hid_t CreatedgroupId) WriteStringAttributes(hid_t groupId, string name, IEnumerable strs, string datasetName = null) 153 | { 154 | hid_t tmpId = groupId; 155 | if (!string.IsNullOrWhiteSpace(datasetName)) 156 | { 157 | hid_t datasetId = H5D.open(groupId, datasetName); 158 | if (datasetId > 0) 159 | groupId = datasetId; 160 | } 161 | 162 | // create UTF-8 encoded attributes 163 | hid_t datatype = H5T.create(H5T.class_t.STRING, H5T.VARIABLE); 164 | H5T.set_cset(datatype, H5T.cset_t.UTF8); 165 | H5T.set_strpad(datatype, H5T.str_t.SPACEPAD); 166 | 167 | int strSz = strs.Count(); 168 | hid_t spaceId = H5S.create_simple(1, 169 | new ulong[] { (ulong)strSz }, null); 170 | 171 | var attributeId = H5A.create(groupId, name, datatype, spaceId); 172 | 173 | GCHandle[] hnds = new GCHandle[strSz]; 174 | IntPtr[] wdata = new IntPtr[strSz]; 175 | 176 | int cntr = 0; 177 | foreach (string str in strs) 178 | { 179 | hnds[cntr] = GCHandle.Alloc( 180 | Encoding.UTF8.GetBytes(str), 181 | GCHandleType.Pinned); 182 | wdata[cntr] = hnds[cntr].AddrOfPinnedObject(); 183 | cntr++; 184 | } 185 | 186 | var hnd = GCHandle.Alloc(wdata, GCHandleType.Pinned); 187 | 188 | var result = H5A.write(attributeId, datatype, hnd.AddrOfPinnedObject()); 189 | hnd.Free(); 190 | 191 | for (int i = 0; i < strSz; ++i) 192 | { 193 | hnds[i].Free(); 194 | } 195 | 196 | H5A.close(attributeId); 197 | H5S.close(spaceId); 198 | H5T.close(datatype); 199 | if (tmpId != groupId) 200 | { 201 | H5D.close(groupId); 202 | } 203 | return (result, attributeId); 204 | } 205 | 206 | public static void WriteAttribute(hid_t groupId, string name, T attribute, string datasetName = null) //where T : struct 207 | { 208 | WriteAttributes(groupId, name, new T[1] { attribute }, datasetName); 209 | /*if (typeof(T) == typeof(string)) 210 | attrRW.WriteArray(groupId, name, new T[1] { attribute }); 211 | else 212 | { 213 | Array oneVal = new T[1, 1] { { attribute } }; 214 | attrRW.WriteArray(groupId, name, oneVal); 215 | }*/ 216 | } 217 | 218 | public static void WriteAttributes(hid_t groupId, string name, Array attributes, string datasetName = null) // 219 | { 220 | attrRW.WriteArray(groupId, name, attributes, datasetName); 221 | /* if (attributes.GetType().GetElementType() == typeof(string)) 222 | return WriteStringAttributes(groupId, name, attributes.Cast(), datasetName); 223 | else 224 | return WritePrimitiveAttribute(groupId, name, attributes, datasetName);*/ 225 | } 226 | 227 | public static (int success, hid_t CreatedgroupId) WritePrimitiveAttribute(hid_t groupId, string name, Array attributes, string datasetName = null) //where T : struct 228 | { 229 | var tmpId = groupId; 230 | if (!string.IsNullOrWhiteSpace(datasetName)) 231 | { 232 | var datasetId = H5D.open(groupId, datasetName); 233 | if (datasetId > 0) 234 | groupId = datasetId; 235 | } 236 | int rank = attributes.Rank; 237 | ulong[] dims = Enumerable.Range(0, rank).Select(i => 238 | { return (ulong)attributes.GetLength(i); }).ToArray(); 239 | ulong[] maxDims = null; 240 | var spaceId = H5S.create_simple(rank, dims, maxDims); 241 | var datatype = GetDatatype(typeof(T)); 242 | var typeId = H5T.copy(datatype); 243 | var attributeId = H5A.create(groupId, name, datatype, spaceId); 244 | GCHandle hnd = GCHandle.Alloc(attributes, GCHandleType.Pinned); 245 | var result = H5A.write(attributeId, datatype, hnd.AddrOfPinnedObject()); 246 | hnd.Free(); 247 | 248 | H5A.close(attributeId); 249 | H5S.close(spaceId); 250 | H5T.close(typeId); 251 | if (tmpId != groupId) 252 | { 253 | H5D.close(groupId); 254 | } 255 | return (result, attributeId); 256 | } 257 | } 258 | } 259 | 260 | public enum Hdf5Save 261 | { 262 | Save, 263 | DoNotSave, 264 | } 265 | 266 | [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)] 267 | public sealed class Hdf5SaveAttribute : System.Attribute 268 | { 269 | private readonly Hdf5Save saveKind; 270 | 271 | public Hdf5Save SaveKind => saveKind; // Topic is a named parameter 272 | 273 | 274 | public Hdf5SaveAttribute(Hdf5Save saveKind) // url is a positional parameter 275 | { 276 | this.saveKind = saveKind; 277 | } 278 | 279 | } -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Common.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using HDF.PInvoke; 6 | using System.Runtime.InteropServices; 7 | using System.Reflection; 8 | 9 | #if HDF5_VER1_10 10 | using hid_t = System.Int64; 11 | #else 12 | using hid_t = System.Int32; 13 | #endif 14 | 15 | 16 | namespace Hdf5DotNetTools 17 | { 18 | public struct OffsetInfo 19 | { 20 | public string name; 21 | public int offset; 22 | public int size; 23 | public Type type; 24 | public hid_t datatype; 25 | } 26 | 27 | 28 | public static partial class Hdf5 29 | { 30 | private static readonly IEnumerable primitiveTypes = Enumerable.Except(Enum.GetValues(typeof(TypeCode)).Cast(), 31 | new TypeCode[] { TypeCode.Empty, TypeCode.DBNull, TypeCode.Object }); 32 | 33 | public static int sizeofType(T obj, FieldInfo info) 34 | { 35 | Type type = info.FieldType; 36 | if (type.IsEnum) 37 | { 38 | return Marshal.SizeOf(Enum.GetUnderlyingType(type)); 39 | } 40 | if (type.IsValueType) 41 | { 42 | return Marshal.SizeOf(type); 43 | } 44 | if (type == typeof(string)) 45 | { 46 | return Encoding.Default.GetByteCount((char[])info.GetValue(obj)); 47 | } 48 | return 0; 49 | } 50 | 51 | public static T fromBytes(byte[] arr) where T : new() 52 | { 53 | T strct = new T(); 54 | 55 | int size = Marshal.SizeOf(strct); 56 | IntPtr ptr = Marshal.AllocHGlobal(size); 57 | 58 | Marshal.Copy(arr, 0, ptr, size); 59 | 60 | strct = (T)Marshal.PtrToStructure(ptr, strct.GetType()); 61 | Marshal.FreeHGlobal(ptr); 62 | 63 | return strct; 64 | } 65 | 66 | public static byte[] getBytes(T strct) 67 | { 68 | int size = Marshal.SizeOf(strct); 69 | byte[] arr = new byte[size]; 70 | 71 | IntPtr ptr = Marshal.AllocHGlobal(size); 72 | Marshal.StructureToPtr(strct, ptr, true); 73 | Marshal.Copy(ptr, arr, 0, size); 74 | Marshal.FreeHGlobal(ptr); 75 | return arr; 76 | } 77 | 78 | /// 79 | /// Opens a Hdf-5 file 80 | /// 81 | /// 82 | /// 83 | /// 84 | public static hid_t OpenFile(string filename, bool readOnly = false) 85 | { 86 | uint access = (readOnly) ? H5F.ACC_RDONLY : H5F.ACC_RDWR; 87 | var fileId = H5F.open(filename, access); 88 | return fileId; 89 | } 90 | 91 | public static hid_t CreateFile(string filename) 92 | { 93 | return H5F.create(filename, H5F.ACC_TRUNC); 94 | } 95 | 96 | 97 | 98 | public static hid_t CloseFile(hid_t fileId) 99 | { 100 | return H5F.close(fileId); 101 | } 102 | 103 | //internal static string ToHdf5Name(string name) 104 | //{ 105 | // return string.Concat(@"/", name); 106 | //} 107 | 108 | 109 | internal static hid_t GetDatatype(System.Type type) 110 | { 111 | //var typeName = type.Name; 112 | hid_t dataType; 113 | 114 | var typeCode = Type.GetTypeCode(type); 115 | switch (typeCode) 116 | { 117 | case TypeCode.Byte: 118 | dataType = H5T.NATIVE_INT8; 119 | break; 120 | case TypeCode.SByte: 121 | dataType = H5T.NATIVE_UINT8; 122 | break; 123 | case TypeCode.Int16: 124 | dataType = H5T.NATIVE_INT16; 125 | break; 126 | case TypeCode.Int32: 127 | dataType = H5T.NATIVE_INT32; 128 | break; 129 | case TypeCode.Int64: 130 | dataType = H5T.NATIVE_INT64; 131 | break; 132 | case TypeCode.UInt16: 133 | dataType = H5T.NATIVE_UINT16; 134 | break; 135 | case TypeCode.UInt32: 136 | dataType = H5T.NATIVE_UINT32; 137 | break; 138 | case TypeCode.UInt64: 139 | dataType = H5T.NATIVE_UINT64; 140 | break; 141 | case TypeCode.Single: 142 | dataType = H5T.NATIVE_FLOAT; 143 | break; 144 | case TypeCode.Double: 145 | dataType = H5T.NATIVE_DOUBLE; 146 | break; 147 | //case TypeCode.DateTime: 148 | // dataType = H5T.Native_t; 149 | // break; 150 | case TypeCode.Char: 151 | //dataType = H5T.NATIVE_UCHAR; 152 | dataType = H5T.C_S1; 153 | break; 154 | case TypeCode.String: 155 | //dataType = H5T.NATIVE_UCHAR; 156 | dataType = H5T.C_S1; 157 | break; 158 | default: 159 | throw new Exception(string.Format("Datatype {0} not supported", type)); 160 | } 161 | return dataType; 162 | } 163 | 164 | internal static hid_t GetDatatypeIEEE(System.Type type) 165 | { 166 | var typeCode = Type.GetTypeCode(type); 167 | hid_t dataType; 168 | switch (typeCode) 169 | { 170 | case TypeCode.Int16: 171 | dataType = H5T.STD_I16BE; 172 | break; 173 | case TypeCode.Int32: 174 | dataType = H5T.STD_I32BE; 175 | break; 176 | case TypeCode.Int64: 177 | dataType = H5T.STD_I64BE; 178 | break; 179 | case TypeCode.UInt16: 180 | dataType = H5T.STD_U16BE; 181 | break; 182 | case TypeCode.UInt32: 183 | dataType = H5T.STD_U32BE; 184 | break; 185 | case TypeCode.UInt64: 186 | dataType = H5T.STD_U64BE; 187 | break; 188 | case TypeCode.Single: 189 | dataType = H5T.IEEE_F32BE; 190 | break; 191 | case TypeCode.Double: 192 | dataType = H5T.IEEE_F64BE; 193 | break; 194 | case TypeCode.Boolean: 195 | dataType = H5T.STD_I8BE; 196 | break; 197 | case TypeCode.Char: 198 | //dataType = H5T.NATIVE_UCHAR; 199 | dataType = H5T.STD_I8BE; 200 | break; 201 | case TypeCode.String: 202 | //dataType = H5T.NATIVE_UCHAR; 203 | dataType = H5T.C_S1; 204 | break; 205 | default: 206 | throw new Exception(string.Format("Datatype {0} not supported", type)); 207 | } 208 | return dataType; 209 | } 210 | 211 | /*private static T[,] convertArrayToType(Array collection) 212 | { 213 | System.Collections.IEnumerator myEnumerator = collection.GetEnumerator(); 214 | if (collection.Rank > 2) 215 | throw new Exception("rank of the array rank is to high"); 216 | int rows = collection.GetLength(0); 217 | int cols = (collection.Rank == 1) ? 1 : collection.GetLength(1); 218 | T[,] output = new T[rows, cols]; 219 | for (int row = 0; row <= collection.GetUpperBound(0); row++) 220 | if (cols == 1) 221 | output[row, 0] = (T)collection.GetValue(row); 222 | else 223 | for (int col = 0; col <= collection.GetUpperBound(1); col++) 224 | output[row, col] = (T)collection.GetValue(row, col); 225 | return output; 226 | }*/ 227 | 228 | /// 229 | /// http://stackoverflow.com/questions/9914230/iterate-through-an-array-of-arbitrary-dimension 230 | /// 231 | /// 232 | /// 233 | /// 234 | public static Array ConvertArray(this Array array, Func convertFunc) 235 | { 236 | // Gets the lengths and lower bounds of the input array 237 | int[] lowerBounds = new int[array.Rank]; 238 | int[] lengths = new int[array.Rank]; 239 | for (int numDimension = 0; numDimension < array.Rank; numDimension++) 240 | { 241 | lowerBounds[numDimension] = array.GetLowerBound(numDimension); 242 | lengths[numDimension] = array.GetLength(numDimension); 243 | } 244 | Func firstIndex = a => Enumerable.Range(0, a.Rank).Select(_i => a.GetLowerBound(_i)).ToArray(); 245 | 246 | Func nextIndex = (a, index) => 247 | { 248 | for (int i = index.Length - 1; i >= 0; --i) 249 | { 250 | index[i]++; 251 | if (index[i] <= array.GetUpperBound(i)) 252 | return index; 253 | index[i] = array.GetLowerBound(i); 254 | } 255 | return null; 256 | }; 257 | 258 | Type type = typeof(T2); 259 | Array ar = Array.CreateInstance(type, lengths, lowerBounds); 260 | if (lowerBounds[0] != 0 || lengths[0] != 0) 261 | for (var index = firstIndex(array); index != null; index = nextIndex(array, index)) 262 | { 263 | var v = (T1)array.GetValue(index); 264 | ar.SetValue(convertFunc(v), index); 265 | } 266 | 267 | return ar; 268 | } 269 | 270 | private static T[] convert2DtoArray(T[,] set) 271 | { 272 | int rows = set.GetLength(0); 273 | int cols = set.GetLength(1); 274 | T[] output = new T[cols * rows]; 275 | for (int i = 0; i < cols; i++) 276 | { 277 | for (int j = 0; j < rows; j++) 278 | { 279 | output[i * cols + j] = set[j, i]; 280 | } 281 | } 282 | return output; 283 | } 284 | 285 | private static Tout[] convert2DtoArray(Tin[,] set, Func convert) 286 | { 287 | int rows = set.GetLength(0); 288 | int cols = set.GetLength(1); 289 | Tout[] output = new Tout[cols * rows]; 290 | for (int i = 0; i < cols; i++) 291 | { 292 | for (int j = 0; j < rows; j++) 293 | { 294 | output[i * cols + j] = convert(set[j, i]); 295 | } 296 | } 297 | return output; 298 | } 299 | 300 | //private static T[] getdataOfType(int datatype) where T : struct 301 | //{ 302 | // System.Type type; 303 | // switch (datatype) 304 | // { 305 | // case H5T.NATIVE_INT16: 306 | // type = Int16.; 307 | // break; 308 | // case nameof(Int32): 309 | // dataType = H5T.NATIVE_INT; 310 | // break; 311 | // case nameof(Int64): 312 | // dataType = H5T.NATIVE_INT64; 313 | // break; 314 | // case nameof(Double): 315 | // dataType = H5T.NATIVE_DOUBLE; 316 | // break; 317 | // case nameof(Boolean): 318 | // dataType = H5T.NATIVE_INT8; 319 | // break; 320 | // default: 321 | // throw new Exception(string.Format("Datatype {0} not supported", type)); 322 | // } 323 | // return type; 324 | //} 325 | 326 | public static bool Similar(this IEnumerable first, IEnumerable second, double precision = 1e-2) 327 | { 328 | var result = first.Zip(second, (f, s) => Math.Abs(f - s) < precision); 329 | return result.All(r => r); 330 | } 331 | 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Compounds.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace Hdf5DotNetTools 10 | { 11 | #if HDF5_VER1_10 12 | using hid_t = System.Int64; 13 | #else 14 | using hid_t = System.Int32; 15 | #endif 16 | public static partial class Hdf5 17 | { 18 | // information: https://www.hdfgroup.org/ftp/HDF5/examples/examples-by-api/hdf5-examples/1_8/C/H5T/h5ex_t_cmpd.c 19 | //or: https://www.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/index.html#t=HDF5_Users_Guide%2FDatatypes%2FHDF5_Datatypes.htm%3Frhtocid%3Dtoc6.5%23TOC_6_8_Complex_Combinationsbc-22 20 | 21 | public static int WriteCompounds(hid_t groupId, string name, IEnumerable list) //where T : struct 22 | { 23 | Type type = typeof(T); 24 | var size = Marshal.SizeOf(type); 25 | var cnt = list.Count(); 26 | 27 | var typeId = CreateType(type); 28 | 29 | var log10 = (int)Math.Log10(cnt); 30 | ulong pow = (ulong)Math.Pow(10, log10); 31 | ulong c_s = Math.Min(1000, pow); 32 | ulong[] chunk_size = new ulong[] { c_s }; 33 | 34 | ulong[] dims = new ulong[] { (ulong)cnt }; 35 | 36 | long dcpl = 0; 37 | if (!list.Any() || log10 == 0) { } 38 | else 39 | { 40 | dcpl = CreateProperty(chunk_size); 41 | } 42 | 43 | // Create dataspace. Setting maximum size to NULL sets the maximum 44 | // size to be the current size. 45 | var spaceId = H5S.create_simple(dims.Length, dims, null); 46 | 47 | // Create the dataset and write the compound data to it. 48 | var datasetId = H5D.create(groupId, name, typeId, spaceId, H5P.DEFAULT, dcpl); 49 | 50 | IntPtr p = Marshal.AllocHGlobal(size * (int)dims[0]); 51 | 52 | var ms = new MemoryStream(); 53 | BinaryWriter writer = new BinaryWriter(ms); 54 | foreach (var strct in list) 55 | writer.Write(getBytes(strct)); 56 | var bytes = ms.ToArray(); 57 | 58 | GCHandle hnd = GCHandle.Alloc(bytes, GCHandleType.Pinned); 59 | var statusId = H5D.write(datasetId, typeId, spaceId, H5S.ALL, 60 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 61 | 62 | hnd.Free(); 63 | /* 64 | * Close and release resources. 65 | */ 66 | H5D.close(datasetId); 67 | H5S.close(spaceId); 68 | H5T.close(typeId); 69 | H5P.close(dcpl); 70 | Marshal.FreeHGlobal(p); 71 | return statusId; 72 | } 73 | 74 | private static long CreateProperty(ulong[] chunk_size) 75 | { 76 | var dcpl = H5P.create(H5P.DATASET_CREATE); 77 | H5P.set_layout(dcpl, H5D.layout_t.CHUNKED); 78 | H5P.set_chunk(dcpl, chunk_size.Length, chunk_size); 79 | H5P.set_deflate(dcpl, 6); 80 | return dcpl; 81 | } 82 | 83 | private static long CreateType(Type t) 84 | { 85 | var size = Marshal.SizeOf(t); 86 | var float_size = Marshal.SizeOf(typeof(float)); 87 | var int_size = Marshal.SizeOf(typeof(int)); 88 | var typeId = H5T.create(H5T.class_t.COMPOUND, new IntPtr(size)); 89 | 90 | var compoundInfo = Hdf5.GetCompoundInfo(t); 91 | foreach (var cmp in compoundInfo) 92 | { 93 | //Console.WriteLine(string.Format("{0} {1}", cmp.name, cmp.datatype)); 94 | // Lines below don't produce an error message but hdfview can't read compounds properly 95 | //var typeLong = GetDatatype(cmp.type); 96 | //H5T.insert(typeId, cmp.name, Marshal.OffsetOf(t, cmp.name), typeLong); 97 | H5T.insert(typeId, cmp.name, Marshal.OffsetOf(t, cmp.name), cmp.datatype); 98 | } 99 | return typeId; 100 | } 101 | private static IEnumerable ChangeStrings(IEnumerable array, FieldInfo[] fields) where T : struct 102 | { 103 | foreach (var info in fields) 104 | if (info.FieldType == typeof(string)) 105 | { 106 | var attr = info.GetCustomAttributes(typeof(MarshalAsAttribute), false); 107 | MarshalAsAttribute maa = (MarshalAsAttribute)attr[0]; 108 | object value = info.GetValue(array); 109 | } 110 | return array; 111 | } 112 | 113 | 114 | /// 115 | private static int CalcCompoundSize(Type type, bool useIEEE, ref hid_t id) 116 | { 117 | // Create the compound datatype for the file. Because the standard 118 | // types we are using for the file may have different sizes than 119 | // the corresponding native types 120 | var compoundInfo = Hdf5.GetCompoundInfo(type, useIEEE); 121 | var curCompound = compoundInfo.Last(); 122 | var compoundSize = curCompound.offset + curCompound.size; 123 | //Create the compound datatype for memory. 124 | id = H5T.create(H5T.class_t.COMPOUND, new IntPtr(compoundSize)); 125 | foreach (var cmp in compoundInfo) 126 | H5T.insert(id, cmp.name, new IntPtr(cmp.offset), cmp.datatype); 127 | return compoundSize; 128 | } 129 | 130 | public static IEnumerable GetCompoundInfo(Type type, bool ieee = false) 131 | { 132 | //Type t = typeof(T); 133 | var strtype = H5T.copy(H5T.C_S1); 134 | int strsize = (int)H5T.get_size(strtype); 135 | int curSize = 0; 136 | List offsets = new List(); 137 | foreach (var x in type.GetFields()) 138 | { 139 | //var fldType = x.FieldType; 140 | //OffsetInfo oi = new OffsetInfo() 141 | //{ 142 | // name = x.Name, 143 | // type = fldType, 144 | // datatype = ieee ? GetDatatypeIEEE(fldType) : GetDatatype(fldType), 145 | // size = fldType == typeof(string) ? StringLength(x) : Marshal.SizeOf(fldType), 146 | // offset = 0 + curSize 147 | //}; 148 | var fldType = x.FieldType; 149 | var marshallAsAttribute = type.GetMember(x.Name).Select(m => m.GetCustomAttribute()).FirstOrDefault(); 150 | 151 | OffsetInfo oi = new OffsetInfo() 152 | { 153 | name = x.Name, 154 | type = fldType, 155 | datatype = !fldType.IsArray ? ieee ? GetDatatypeIEEE(fldType) : GetDatatype(fldType) 156 | : H5T.array_create(ieee ? GetDatatypeIEEE(fldType.GetElementType()) : GetDatatype(fldType.GetElementType()), (uint)fldType.GetArrayRank(), Enumerable.Range(0, fldType.GetArrayRank()).Select(i => (ulong)marshallAsAttribute.SizeConst).ToArray()), 157 | size = fldType == typeof(string) ? StringLength(x) : !fldType.IsArray ? Marshal.SizeOf(fldType) : Marshal.SizeOf(fldType.GetElementType()) * marshallAsAttribute.SizeConst, 158 | offset = 0 + curSize 159 | }; 160 | if (oi.datatype == H5T.C_S1) 161 | { 162 | strtype = H5T.copy(H5T.C_S1); 163 | H5T.set_size(strtype, new IntPtr(oi.size)); 164 | oi.datatype = strtype; 165 | } 166 | if (oi.datatype == H5T.STD_I64BE) 167 | oi.size = oi.size * 2; 168 | curSize = curSize + oi.size; 169 | 170 | offsets.Add(oi); 171 | } 172 | /* poging om ook properties te bewaren. 173 | * foreach (var x in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)) 174 | { 175 | bool saveProperty = false; 176 | bool isNotPublic = x.PropertyType.Attributes != TypeAttributes.Public; 177 | foreach (Attribute attr in Attribute.GetCustomAttributes(x)) 178 | { 179 | var legAttr = attr as Hdf5SaveAttribute; 180 | var kind = legAttr?.SaveKind; 181 | bool saveAndPrivateProp = isNotPublic && kind == Hdf5Save.Save; 182 | bool doNotSaveProp = (kind == Hdf5Save.DoNotSave) ; 183 | if (saveAndPrivateProp && !doNotSaveProp) 184 | { 185 | saveProperty = true; 186 | continue; 187 | } 188 | 189 | } 190 | if (!saveProperty) 191 | continue; 192 | var propType = x.PropertyType; 193 | OffsetInfo oi = new OffsetInfo() 194 | { 195 | name = x.Name, 196 | type = propType, 197 | datatype = ieee ? GetDatatypeIEEE(propType) : GetDatatype(propType), 198 | size = propType == typeof(string) ? stringLength(x) : Marshal.SizeOf(propType), 199 | offset = 0 + curSize 200 | }; 201 | if (oi.datatype == H5T.C_S1) 202 | { 203 | strtype = H5T.copy(H5T.C_S1); 204 | H5T.set_size(strtype, new IntPtr(oi.size)); 205 | oi.datatype = strtype; 206 | } 207 | if (oi.datatype == H5T.STD_I64BE) 208 | oi.size = oi.size * 2; 209 | curSize = curSize + oi.size; 210 | 211 | offsets.Add(oi); 212 | }*/ 213 | H5T.close(strtype); 214 | return offsets; 215 | 216 | } 217 | 218 | private static int StringLength(MemberInfo fld) 219 | { 220 | var attr = fld.GetCustomAttributes(typeof(MarshalAsAttribute), false); 221 | MarshalAsAttribute maa = (MarshalAsAttribute)attr[0]; 222 | var constSize = maa.SizeConst; 223 | return constSize; 224 | } 225 | 226 | public static IEnumerable ReadCompounds(hid_t groupId, string name) where T : struct 227 | { 228 | Type type = typeof(T); 229 | hid_t typeId = 0; 230 | // open dataset 231 | var datasetId = H5D.open(groupId, name); 232 | 233 | typeId = CreateType(type); 234 | var compoundSize = Marshal.SizeOf(type); 235 | 236 | /* 237 | * Get dataspace and allocate memory for read buffer. 238 | */ 239 | var spaceId = H5D.get_space(datasetId); 240 | int rank = H5S.get_simple_extent_ndims(spaceId); 241 | ulong[] dims = new ulong[rank]; 242 | var ndims = H5S.get_simple_extent_dims(spaceId, dims, null); 243 | int rows = Convert.ToInt32(dims[0]); 244 | 245 | byte[] bytes = new byte[rows * compoundSize]; 246 | // Read the data. 247 | GCHandle hnd = GCHandle.Alloc(bytes, GCHandleType.Pinned); 248 | IntPtr hndAddr = hnd.AddrOfPinnedObject(); 249 | H5D.read(datasetId, typeId, spaceId, H5S.ALL, H5P.DEFAULT, hndAddr); 250 | int counter = 0; 251 | IEnumerable strcts = Enumerable.Range(1, rows).Select(i => 252 | { 253 | byte[] select = new byte[compoundSize]; 254 | Array.Copy(bytes, counter, select, 0, compoundSize); 255 | T s = fromBytes(select); 256 | counter = counter + compoundSize; 257 | return s; 258 | }); 259 | /* 260 | * Close and release resources. 261 | */ 262 | H5D.vlen_reclaim(typeId, spaceId, H5P.DEFAULT, hndAddr); 263 | hnd.Free(); 264 | H5D.close(datasetId); 265 | H5S.close(spaceId); 266 | H5T.close(typeId); 267 | 268 | return strcts; 269 | } 270 | 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Dataset.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Hdf5DotNetTools 7 | { 8 | #if HDF5_VER1_10 9 | using hid_t = System.Int64; 10 | #else 11 | using hid_t = System.Int32; 12 | #endif 13 | 14 | public static partial class Hdf5 15 | { 16 | static Hdf5ReaderWriter dsetRW = new Hdf5ReaderWriter(new Hdf5Dataset()); 17 | 18 | /// 19 | /// Reads an n-dimensional dataset. 20 | /// 21 | /// Generic parameter strings or primitive type 22 | /// id of the group. Can also be a file Id 23 | /// name of the dataset 24 | /// The n-dimensional dataset 25 | public static Array ReadDatasetToArray(hid_t groupId, string name) //where T : struct 26 | { 27 | var datatype = GetDatatype(typeof(T)); 28 | 29 | 30 | var datasetId = H5D.open(groupId, name); 31 | var spaceId = H5D.get_space(datasetId); 32 | int rank = H5S.get_simple_extent_ndims(spaceId); 33 | long count = H5S.get_simple_extent_npoints(spaceId); 34 | Array dset; 35 | Type type = typeof(T); 36 | if (rank >= 0 && count >= 0) 37 | { 38 | int rankChunk; 39 | ulong[] maxDims = new ulong[rank]; 40 | ulong[] dims = new ulong[rank]; 41 | ulong[] chunkDims = new ulong[rank]; 42 | hid_t memId = H5S.get_simple_extent_dims(spaceId, dims, maxDims); 43 | long[] lengths = dims.Select(d => Convert.ToInt64(d)).ToArray(); 44 | dset = Array.CreateInstance(type, lengths); 45 | var typeId = H5D.get_type(datasetId); 46 | var mem_type = H5T.copy(datatype); 47 | if (datatype == H5T.C_S1) 48 | H5T.set_size(datatype, new IntPtr(2)); 49 | 50 | var propId = H5D.get_create_plist(datasetId); 51 | 52 | if (H5D.layout_t.CHUNKED == H5P.get_layout(propId)) 53 | rankChunk = H5P.get_chunk(propId, rank, chunkDims); 54 | 55 | memId = H5S.create_simple(rank, dims, maxDims); 56 | GCHandle hnd = GCHandle.Alloc(dset, GCHandleType.Pinned); 57 | H5D.read(datasetId, datatype, memId, spaceId, 58 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 59 | hnd.Free(); 60 | } 61 | else 62 | dset = Array.CreateInstance(type, new long[1] { 0 }); 63 | H5D.close(datasetId); 64 | H5S.close(spaceId); 65 | return dset; 66 | 67 | } 68 | 69 | /// 70 | /// Reads part of a two dimensional dataset. 71 | /// 72 | /// Generic parameter strings or primitive type 73 | /// id of the group. Can also be a file Id 74 | /// name of the dataset 75 | /// The index of the first row to be read 76 | /// The index of the last row to be read 77 | /// The two dimensional dataset 78 | public static T[,] ReadDataset(hid_t groupId, string name, ulong beginIndex, ulong endIndex) //where T : struct 79 | { 80 | ulong[] start = { 0, 0 }, stride = null, count = { 0, 0 }, 81 | block = null, offsetOut = new ulong[] { 0, 0 }; 82 | var datatype = GetDatatype(typeof(T)); 83 | 84 | var datasetId = H5D.open(groupId, name); 85 | var spaceId = H5D.get_space(datasetId); 86 | int rank = H5S.get_simple_extent_ndims(spaceId); 87 | ulong[] maxDims = new ulong[rank]; 88 | ulong[] dims = new ulong[rank]; 89 | ulong[] chunkDims = new ulong[rank]; 90 | var memId_n = H5S.get_simple_extent_dims(spaceId, dims, maxDims); 91 | 92 | start[0] = beginIndex; 93 | start[1] = 0; 94 | count[0] = endIndex - beginIndex + 1; 95 | count[1] = dims[1]; 96 | 97 | var status = H5S.select_hyperslab(spaceId, H5S.seloper_t.SET, start, stride, count, block); 98 | 99 | 100 | // Define the memory dataspace. 101 | T[,] dset = new T[count[0], count[1]]; 102 | var memId = H5S.create_simple(rank, count, null); 103 | 104 | // Define memory hyperslab. 105 | status = H5S.select_hyperslab(memId, H5S.seloper_t.SET, offsetOut, null, 106 | count, null); 107 | 108 | // Read data from hyperslab in the file into the hyperslab in 109 | // memory and display. 110 | GCHandle hnd = GCHandle.Alloc(dset, GCHandleType.Pinned); 111 | H5D.read(datasetId, datatype, memId, spaceId, 112 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 113 | hnd.Free(); 114 | H5D.close(datasetId); 115 | H5S.close(spaceId); 116 | H5S.close(memId); 117 | return dset; 118 | } 119 | 120 | /// 121 | /// Reads a dataset or string array with one value in it 122 | /// 123 | /// Generic parameter strings or primitive type 124 | /// id of the group. Can also be a file Id 125 | /// name of the dataset 126 | /// One value or string 127 | public static T ReadOneValue(hid_t groupId, string name) //where T : struct 128 | { 129 | var dset = dsetRW.ReadArray(groupId, name); 130 | int[] first = new int[dset.Rank].Select(f => 0).ToArray(); 131 | T result = (T)dset.GetValue(first); 132 | return result; 133 | } 134 | 135 | public static Array ReadDataset(hid_t groupId, string name) 136 | { 137 | return dsetRW.ReadArray(groupId, name); 138 | } 139 | 140 | /// 141 | /// Writes one value to a hdf5 file 142 | /// 143 | /// Generic parameter strings or primitive type 144 | /// id of the group. Can also be a file Id 145 | /// name of the dataset 146 | /// The dataset 147 | /// status of the write method 148 | public static (int success, hid_t CreatedgroupId) WriteOneValue(hid_t groupId, string name, T dset) 149 | { 150 | if (typeof(T) == typeof(string)) 151 | //WriteStrings(groupId, name, new string[] { dset.ToString() }); 152 | return dsetRW.WriteArray(groupId, name, new T[1] { dset }); 153 | else 154 | { 155 | Array oneVal = new T[1, 1] { { dset } }; 156 | return dsetRW.WriteArray(groupId, name, oneVal); 157 | } 158 | } 159 | 160 | public static void WriteDataset(hid_t groupId, string name, Array collection) 161 | { 162 | dsetRW.WriteArray(groupId, name, collection); 163 | } 164 | 165 | 166 | public static (int success, hid_t CreatedgroupId) WriteDatasetFromArray(hid_t groupId, string name, Array dset, string datasetName = null) //where T : struct 167 | { 168 | int rank = dset.Rank; 169 | ulong[] dims = Enumerable.Range(0, rank).Select(i => { return (ulong)dset.GetLength(i); }).ToArray(); 170 | 171 | ulong[] maxDims = null; 172 | var spaceId = H5S.create_simple(rank, dims, maxDims); 173 | var datatype = GetDatatype(typeof(T)); 174 | var typeId = H5T.copy(datatype); 175 | if (datatype == H5T.C_S1) 176 | { 177 | H5T.set_size(datatype, new IntPtr(2)); 178 | } 179 | var datasetId = H5D.create(groupId, name, datatype, spaceId); 180 | GCHandle hnd = GCHandle.Alloc(dset, GCHandleType.Pinned); 181 | var result = H5D.write(datasetId, datatype, H5S.ALL, H5S.ALL, H5P.DEFAULT, 182 | hnd.AddrOfPinnedObject()); 183 | hnd.Free(); 184 | H5D.close(datasetId); 185 | H5S.close(spaceId); 186 | H5T.close(typeId); 187 | return (result, datasetId); 188 | } 189 | 190 | /// 191 | /// Appends a dataset to a hdf5 file. If called the first time a dataset is created 192 | /// 193 | /// Generic parameter only primitive types are allowed 194 | /// id of the group. Can also be a file Id 195 | /// name of the dataset 196 | /// The dataset 197 | /// status of the write method 198 | public static hid_t AppendDataset(hid_t groupId, string name, Array dset, ulong chunkX = 200) where T : struct 199 | { 200 | var rank = dset.Rank; 201 | ulong[] dimsExtend = Enumerable.Range(0, rank).Select(i => 202 | { return (ulong)dset.GetLength(i); }).ToArray(); 203 | ulong[] maxDimsExtend = null; 204 | ulong[] dimsChunk = new ulong[] { chunkX }.Concat(dimsExtend.Skip(1)).ToArray(); 205 | ulong[] zeros = Enumerable.Range(0, rank).Select(z => (ulong)0).ToArray(); 206 | hid_t status, spaceId, datasetId; 207 | 208 | 209 | // name = ToHdf5Name(name); 210 | var datatype = GetDatatype(typeof(T)); 211 | var typeId = H5T.copy(datatype); 212 | var datasetExists = H5L.exists(groupId, name) > 0; 213 | 214 | /* Create a new dataset within the file using chunk 215 | creation properties. */ 216 | if (!datasetExists) 217 | { 218 | 219 | spaceId = H5S.create_simple(dset.Rank, dimsExtend, maxDimsExtend); 220 | 221 | var propId = H5P.create(H5P.DATASET_CREATE); 222 | status = H5P.set_chunk(propId, rank, dimsChunk); 223 | datasetId = H5D.create(groupId, name, datatype, spaceId, 224 | H5P.DEFAULT, propId, H5P.DEFAULT); 225 | /* Write data to dataset */ 226 | GCHandle hnd = GCHandle.Alloc(dset, GCHandleType.Pinned); 227 | status = H5D.write(datasetId, datatype, H5S.ALL, H5S.ALL, H5P.DEFAULT, 228 | hnd.AddrOfPinnedObject()); 229 | hnd.Free(); 230 | H5P.close(propId); 231 | } 232 | else 233 | { 234 | datasetId = H5D.open(groupId, name); 235 | spaceId = H5D.get_space(datasetId); 236 | var rank_old = H5S.get_simple_extent_ndims(spaceId); 237 | ulong[] maxDims = new ulong[rank_old]; 238 | ulong[] dims = new ulong[rank_old]; 239 | var memId1 = H5S.get_simple_extent_dims(spaceId, dims, maxDims); 240 | 241 | ulong[] oldChunk = null; 242 | int chunkDims = 0; 243 | var propId = H5P.create(H5P.DATASET_ACCESS); 244 | status = H5P.get_chunk(propId, chunkDims, oldChunk); 245 | 246 | /* Extend the dataset. */ 247 | var size = new ulong[] { dims[0] + dimsExtend[0] }.Concat(dims.Skip(1)).ToArray(); 248 | status = H5D.set_extent(datasetId, size); 249 | 250 | /* Select a hyperslab in extended portion of dataset */ 251 | var filespaceId = H5D.get_space(datasetId); 252 | var offset = new ulong[] { dims[0] }.Concat(zeros.Skip(1)).ToArray(); 253 | status = H5S.select_hyperslab(filespaceId, H5S.seloper_t.SET, offset, null, 254 | dimsExtend, null); 255 | 256 | /* Define memory space */ 257 | var memId2 = H5S.create_simple(rank, dimsExtend, null); 258 | 259 | /* Write the data to the extended portion of dataset */ 260 | GCHandle hnd = GCHandle.Alloc(dset, GCHandleType.Pinned); 261 | status = H5D.write(datasetId, datatype, memId2, spaceId, 262 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 263 | hnd.Free(); 264 | H5S.close(memId1); 265 | H5S.close(memId2); 266 | H5D.close(filespaceId); 267 | } 268 | 269 | H5D.close(datasetId); 270 | H5S.close(spaceId); 271 | return status; 272 | } 273 | 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5DotNetTools.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net8.0;net6.0;net48 5 | Hdf5DotnetTools 6 | Hdf5DotnetTools 7 | true 8 | false 9 | Hdf5DotNetTools.snk 10 | true 11 | Hdf5DotNetTools 12 | Hdf5DotNetTools 13 | 0.2.0 14 | Robert Reijntjes 15 | Leiden University Medical Center (LUMC) 16 | Set of tools that help in reading and writing hdf5 files for .net environments 17 | Hdf5 18 | 19 | 20 | 21 | TRACE;HDF5_VER1_10 22 | 23 | 24 | 25 | TRACE; HDF5_VER1_10 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5DotNetTools.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hdf5DotNetTools 5 | 0.1.0 6 | Hdf5DotNetTools 7 | Robert Reijntjes 8 | false 9 | MIT 10 | https://github.com/reyntjesr/Hdf5DotnetTools 11 | 12 | Set of tools that help in reading and writing hdf5 files for .net environments 13 | 14 | Copyright (c)2021 Leiden University Medical Center 15 | Hdf5 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5DotNetTools.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reyntjesr/Hdf5DotnetTools/d14730d239c167dcd639c02ff3a6bad7dc9d8c34/Hdf5DotNetTools/Hdf5DotNetTools.snk -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Groups.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Hdf5DotNetTools 8 | { 9 | #if HDF5_VER1_10 10 | using hid_t = System.Int64; 11 | #else 12 | using hid_t = System.Int32; 13 | #endif 14 | public static partial class Hdf5 15 | { 16 | 17 | public static int CloseGroup(hid_t groupId) 18 | { 19 | return H5G.close(groupId); 20 | } 21 | 22 | public static hid_t CreateGroup(hid_t groupId, string groupName) 23 | { 24 | hid_t gid; 25 | if (GroupExists(groupId, groupName)) 26 | gid = H5G.open(groupId, groupName); 27 | else 28 | gid = H5G.create(groupId, groupName); 29 | return gid; 30 | } 31 | 32 | /// 33 | /// creates a structure of groups at once 34 | /// 35 | /// 36 | /// 37 | /// 38 | public static hid_t CreateGroupRecursively(hid_t groupOrfileId, string groupName) 39 | { 40 | IEnumerable grps = groupName.Split('/'); 41 | hid_t gid = groupOrfileId; 42 | groupName = ""; 43 | foreach (var name in grps) 44 | { 45 | groupName = string.Concat(groupName, "/", name); 46 | gid = CreateGroup(gid, groupName); 47 | } 48 | return gid; 49 | } 50 | 51 | public static bool GroupExists(hid_t groupId, string groupName) 52 | { 53 | bool exists = false; 54 | try 55 | { 56 | H5G.info_t info = new H5G.info_t(); 57 | var gid = H5G.get_info_by_name(groupId, groupName, ref info); 58 | exists = gid == 0; 59 | } 60 | catch (Exception) 61 | { 62 | } 63 | return exists; 64 | } 65 | 66 | public static string[] GroupGroups(hid_t groupId) 67 | { 68 | string[] grps = new string[0]; 69 | try 70 | { 71 | var buf_size = H5I.get_name(groupId, (StringBuilder)null, IntPtr.Zero) + 1; 72 | int len = buf_size.ToInt32() - 1; 73 | H5G.info_t g_info = new H5G.info_t(); 74 | H5O.info_t o_info = new H5O.info_t(); 75 | o_info = Hdf5.GroupInfo(groupId); 76 | 77 | var gid = H5G.get_info_by_name(groupId, ".", ref g_info); 78 | for (ulong i = 0; i < g_info.nlinks; i++) 79 | { 80 | H5O.info_t info = new H5O.info_t(); 81 | gid = H5O.get_info_by_idx(groupId, ".", H5.index_t.NAME, H5.iter_order_t.NATIVE, i, ref info); 82 | if (info.type == H5O.type_t.GROUP) 83 | { 84 | Trace.WriteLine(""); 85 | } 86 | //H5G.get_info_by_idx 87 | } 88 | StringBuilder nameBuilder = new StringBuilder(buf_size.ToInt32()); 89 | IntPtr size = H5I.get_name(groupId, nameBuilder, buf_size); 90 | 91 | 92 | Trace.WriteLine(nameBuilder.ToString()); 93 | } 94 | catch (Exception) 95 | { 96 | } 97 | return grps; 98 | } 99 | 100 | 101 | public static ulong NumberOfAttributes(long groupId, string groupName) 102 | { 103 | H5O.info_t info = new H5O.info_t(); 104 | var gid = H5O.get_info(groupId, ref info); 105 | return info.num_attrs; 106 | } 107 | 108 | public static H5O.info_t GroupInfo(long groupId) 109 | { 110 | H5O.info_t info = new H5O.info_t(); 111 | var gid = H5O.get_info(groupId, ref info); 112 | return info; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5ReadWrite.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Hdf5DotNetTools 6 | { 7 | #if HDF5_VER1_10 8 | using hid_t = System.Int64; 9 | #else 10 | using hid_t = System.Int32; 11 | #endif 12 | public class Hdf5Dataset : IHdf5ReaderWriter 13 | { 14 | public Array ReadToArray(hid_t groupId, string name) 15 | { 16 | return Hdf5.ReadDatasetToArray(groupId, name); 17 | } 18 | 19 | public (int success, hid_t CreatedgroupId) WriteFromArray(hid_t groupId, string name, Array dset, string datasetName = null) 20 | { 21 | return Hdf5.WriteDatasetFromArray(groupId, name, dset, datasetName); 22 | } 23 | public (int success, hid_t CreatedgroupId) WriteStrings(hid_t groupId, string name, IEnumerable collection, string datasetName = null) 24 | { 25 | return Hdf5.WriteStrings(groupId, name, (string[])collection, datasetName); 26 | } 27 | public void WriteStucts(hid_t groupId, string name, IEnumerable dset, string datasetName = null) 28 | { 29 | Hdf5.WriteCompounds(groupId, name, dset); 30 | } 31 | 32 | public Array ReadStucts(hid_t groupId, string name) where T : struct 33 | { 34 | return Hdf5.ReadCompounds(groupId, name).ToArray(); 35 | } 36 | 37 | public IEnumerable ReadStrings(hid_t groupId, string name) 38 | { 39 | return Hdf5.ReadStrings(groupId, name); 40 | } 41 | 42 | } 43 | 44 | public class Hdf5AttributeRW : IHdf5ReaderWriter 45 | { 46 | public Array ReadToArray(hid_t groupId, string name) 47 | { 48 | return Hdf5.ReadPrimitiveAttributes(groupId, name); 49 | } 50 | 51 | public (int success, hid_t CreatedgroupId) WriteFromArray(hid_t groupId, string name, Array dset, string datasetName = null) 52 | { 53 | return Hdf5.WritePrimitiveAttribute(groupId, name, dset, datasetName); 54 | } 55 | 56 | public (int success, hid_t CreatedgroupId) WriteStrings(hid_t groupId, string name, IEnumerable collection, string datasetName = null) 57 | { 58 | return Hdf5.WriteStringAttributes(groupId, name, (string[])collection, datasetName); 59 | } 60 | 61 | 62 | public IEnumerable ReadStrings(hid_t groupId, string name) 63 | { 64 | return Hdf5.ReadStringAttributes(groupId, name); 65 | } 66 | 67 | } 68 | 69 | public interface IHdf5ReaderWriter 70 | { 71 | (int success, hid_t CreatedgroupId) WriteFromArray(hid_t groupId, string name, Array dset, string datasetName = null); 72 | Array ReadToArray(hid_t groupId, string name); 73 | 74 | (int success, hid_t CreatedgroupId) WriteStrings(hid_t groupId, string name, IEnumerable collection, string datasetName = null); 75 | IEnumerable ReadStrings(hid_t groupId, string name); 76 | 77 | 78 | } 79 | 80 | /* public interface IHdf5ReaderWriter:IHdf5AttributeReaderWriter 81 | { 82 | void WriteStucts(hid_t groupId, string name, IEnumerable dset, string datasetName = null); 83 | Array ReadStucts(hid_t groupId, string name) where T : struct; 84 | 85 | }*/ 86 | 87 | public class Hdf5ReaderWriter 88 | { 89 | IHdf5ReaderWriter rw; 90 | public Hdf5ReaderWriter(IHdf5ReaderWriter _rw) 91 | { 92 | rw = _rw; 93 | } 94 | 95 | public (int success, hid_t CreatedgroupId) WriteArray(hid_t groupId, string name, Array collection, string datasetName = null) 96 | { 97 | 98 | Type type = collection.GetType(); 99 | Type elementType = type.GetElementType(); 100 | TypeCode typeCode = Type.GetTypeCode(elementType); 101 | //Boolean isStruct = type.IsValueType && !type.IsEnum; 102 | (int success, hid_t CreatedgroupId) result; 103 | switch (typeCode) 104 | { 105 | case TypeCode.Boolean: 106 | var bls = collection.ConvertArray(bl => Convert.ToUInt16(bl)); 107 | result = rw.WriteFromArray(groupId, name, bls, datasetName); 108 | Hdf5.WriteStringAttribute(groupId, name, "Boolean", name); 109 | break; 110 | case TypeCode.Byte: 111 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 112 | Hdf5.WriteStringAttribute(groupId, name, "Byte", name); 113 | break; 114 | case TypeCode.Char: 115 | var chrs = collection.ConvertArray(c => Convert.ToUInt16(c)); 116 | result = rw.WriteFromArray(groupId, name, chrs, datasetName); 117 | Hdf5.WriteStringAttribute(groupId, name, "Char", name); 118 | break; 119 | 120 | case TypeCode.DateTime: 121 | var dts = collection.ConvertArray(dt => dt.Ticks); 122 | result = rw.WriteFromArray(groupId, name, dts, datasetName); 123 | Hdf5.WriteStringAttribute(groupId, name, "DateTime", name); 124 | break; 125 | 126 | case TypeCode.Decimal: 127 | var decs = collection.ConvertArray(dc => Convert.ToDouble(dc)); 128 | result = rw.WriteFromArray(groupId, name, decs, datasetName); 129 | Hdf5.WriteStringAttribute(groupId, name, "Decimal", name); 130 | break; 131 | 132 | case TypeCode.Double: 133 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 134 | break; 135 | 136 | case TypeCode.Int16: 137 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 138 | break; 139 | 140 | case TypeCode.Int32: 141 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 142 | break; 143 | 144 | case TypeCode.Int64: 145 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 146 | break; 147 | 148 | case TypeCode.SByte: 149 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 150 | Hdf5.WriteStringAttribute(groupId, name, "SByte", name); 151 | break; 152 | 153 | case TypeCode.Single: 154 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 155 | break; 156 | 157 | case TypeCode.UInt16: 158 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 159 | break; 160 | 161 | case TypeCode.UInt32: 162 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 163 | break; 164 | 165 | case TypeCode.UInt64: 166 | result = rw.WriteFromArray(groupId, name, collection, datasetName); 167 | break; 168 | 169 | case TypeCode.String: 170 | if (collection.Rank > 1 && collection.GetLength(1) > 1) 171 | throw new Exception("Only 1 dimensional string arrays allowed: " + name); 172 | result = rw.WriteStrings(groupId, name, (string[])collection, datasetName); 173 | break; 174 | 175 | default: 176 | if (elementType == typeof(TimeSpan)) 177 | { 178 | var tss = collection.ConvertArray(dt => dt.Ticks); 179 | result = rw.WriteFromArray(groupId, name, tss, datasetName); 180 | Hdf5.WriteStringAttribute(groupId, name, "TimeSpan", name); 181 | 182 | } 183 | //else if (isStruct) { 184 | // rw.WriteStucts(groupId, name, collection); 185 | //} 186 | else 187 | { 188 | string str = "type is not supported: "; 189 | throw new NotSupportedException(str + elementType.FullName); 190 | } 191 | break; 192 | } 193 | return result; 194 | } 195 | 196 | 197 | public Array ReadArray(hid_t groupId, string name) 198 | { 199 | return ReadArray(typeof(T), groupId, name); 200 | } 201 | 202 | public Array ReadArray(Type elementType, hid_t groupId, string name) 203 | { 204 | TypeCode ty = Type.GetTypeCode(elementType); 205 | 206 | switch (ty) 207 | { 208 | case TypeCode.Boolean: 209 | var bls = rw.ReadToArray(groupId, name); 210 | return bls.ConvertArray(Convert.ToBoolean); 211 | 212 | case TypeCode.Byte: 213 | return rw.ReadToArray(groupId, name); 214 | 215 | case TypeCode.Char: 216 | var chrs = rw.ReadToArray(groupId, name); 217 | return chrs.ConvertArray(Convert.ToChar); 218 | 219 | case TypeCode.DateTime: 220 | var ticks = rw.ReadToArray(groupId, name); 221 | return ticks.ConvertArray(tc => new DateTime(tc)); 222 | 223 | case TypeCode.Decimal: 224 | var decs = rw.ReadToArray(groupId, name); 225 | return decs.ConvertArray(Convert.ToDecimal); 226 | 227 | case TypeCode.Double: 228 | return rw.ReadToArray(groupId, name); 229 | 230 | case TypeCode.Int16: 231 | return rw.ReadToArray(groupId, name); 232 | 233 | case TypeCode.Int32: 234 | return rw.ReadToArray(groupId, name); 235 | 236 | case TypeCode.Int64: 237 | return rw.ReadToArray(groupId, name); 238 | 239 | case TypeCode.SByte: 240 | return rw.ReadToArray(groupId, name); 241 | 242 | case TypeCode.Single: 243 | return rw.ReadToArray(groupId, name); 244 | 245 | case TypeCode.UInt16: 246 | return rw.ReadToArray(groupId, name); 247 | 248 | case TypeCode.UInt32: 249 | return rw.ReadToArray(groupId, name); 250 | 251 | case TypeCode.UInt64: 252 | return rw.ReadToArray(groupId, name); 253 | 254 | case TypeCode.String: 255 | return rw.ReadStrings(groupId, name).ToArray(); 256 | 257 | default: 258 | if (elementType == typeof(TimeSpan)) 259 | { 260 | var tss = rw.ReadToArray(groupId, name); 261 | return tss.ConvertArray(tcks => new TimeSpan(tcks)); 262 | } 263 | string str = "type is not supported: "; 264 | throw new NotSupportedException(str + elementType.FullName); 265 | 266 | } 267 | } 268 | 269 | } 270 | } -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Reader.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Hdf5DotNetTools 7 | { 8 | using System.Collections; 9 | using System.Diagnostics; 10 | #if HDF5_VER1_10 11 | using hid_t = System.Int64; 12 | #else 13 | using hid_t = System.Int32; 14 | #endif 15 | public partial class Hdf5 16 | { 17 | 18 | public static T ReadObject(hid_t groupId, T readValue, string groupName) 19 | { 20 | if (readValue == null) 21 | { 22 | throw new ArgumentNullException(nameof(readValue)); 23 | } 24 | bool isGroupName = !string.IsNullOrWhiteSpace(groupName); 25 | if (isGroupName) 26 | groupId = H5G.open(groupId, groupName); 27 | 28 | Type tyObject = readValue.GetType(); 29 | foreach (Attribute attr in Attribute.GetCustomAttributes(tyObject)) 30 | { 31 | if (attr is Hdf5GroupName) 32 | groupName = (attr as Hdf5GroupName).Name; 33 | if (attr is Hdf5SaveAttribute) 34 | { 35 | Hdf5SaveAttribute atLeg = attr as Hdf5SaveAttribute; 36 | if (atLeg.SaveKind == Hdf5Save.DoNotSave) 37 | return readValue; 38 | } 39 | } 40 | 41 | 42 | ReadFields(tyObject, readValue, groupId); 43 | ReadProperties(tyObject, readValue, groupId); 44 | 45 | if (isGroupName) 46 | Hdf5.CloseGroup(groupId); 47 | return readValue; 48 | } 49 | 50 | public static T ReadObject(hid_t groupId, string groupName) where T : new() 51 | { 52 | T readValue = new T(); 53 | return ReadObject(groupId, readValue, groupName); 54 | } 55 | 56 | private static void ReadFields(Type tyObject, object readValue, hid_t groupId) 57 | { 58 | FieldInfo[] miMembers = tyObject.GetFields(BindingFlags.DeclaredOnly | 59 | /*BindingFlags.NonPublic |*/ BindingFlags.Public | BindingFlags.Instance); 60 | 61 | foreach (FieldInfo info in miMembers) 62 | { 63 | bool nextInfo = false; 64 | foreach (Attribute attr in Attribute.GetCustomAttributes(info)) 65 | { 66 | if (attr is Hdf5SaveAttribute attribute) 67 | { 68 | Hdf5Save kind = attribute.SaveKind; 69 | nextInfo = (kind == Hdf5Save.DoNotSave); 70 | } 71 | else 72 | nextInfo = false; 73 | } 74 | if (nextInfo) continue; 75 | 76 | Type ty = info.FieldType; 77 | TypeCode code = Type.GetTypeCode(ty); 78 | 79 | string name = info.Name; 80 | Trace.WriteLine($"groupname: {tyObject.Name}; field name: {name}"); 81 | 82 | if (ty.IsArray) 83 | { 84 | var elType = ty.GetElementType(); 85 | TypeCode elCode = Type.GetTypeCode(elType); 86 | 87 | Array values; 88 | if (elCode != TypeCode.Object) 89 | { 90 | values = dsetRW.ReadArray(ty, groupId, name); 91 | } 92 | else 93 | { 94 | values = CallByReflection(nameof(ReadCompounds), elType, new object[] { groupId, name }); 95 | } 96 | info.SetValue(readValue, values); 97 | } 98 | else if (primitiveTypes.Contains(code) || ty == typeof(TimeSpan)) 99 | { 100 | Array values = dsetRW.ReadArray(ty, groupId, name); 101 | // get first value depending on rank of the matrix 102 | int[] first = new int[values.Rank].Select(f => 0).ToArray(); 103 | info.SetValue(readValue, values.GetValue(first)); 104 | } 105 | else 106 | { 107 | Object value = info.GetValue(readValue); 108 | if (value != null) 109 | ReadObject(groupId, value, name); 110 | } 111 | } 112 | } 113 | 114 | private static void ReadProperties(Type tyObject, object readValue, hid_t groupId) 115 | { 116 | PropertyInfo[] miMembers = tyObject.GetProperties(/*BindingFlags.DeclaredOnly |*/ 117 | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 118 | 119 | foreach (PropertyInfo info in miMembers) 120 | { 121 | bool nextInfo = false; 122 | foreach (Attribute attr in Attribute.GetCustomAttributes(info)) 123 | { 124 | Hdf5Save kind = ((Hdf5SaveAttribute)attr).SaveKind; 125 | nextInfo = (kind == Hdf5Save.DoNotSave); 126 | } 127 | if (nextInfo) continue; 128 | Type ty = info.PropertyType; 129 | TypeCode code = Type.GetTypeCode(ty); 130 | string name = info.Name; 131 | 132 | Trace.WriteLine($"groupname: {tyObject.Name}; property name: {name}"); 133 | 134 | if (ty.IsArray) 135 | { 136 | var elType = ty.GetElementType(); 137 | TypeCode elCode = Type.GetTypeCode(elType); 138 | 139 | Array values; 140 | if (elCode != TypeCode.Object || ty == typeof(TimeSpan[])) 141 | { 142 | values = dsetRW.ReadArray(elType, groupId, name); 143 | } 144 | else 145 | { 146 | var obj = CallByReflection(nameof(ReadCompounds), elType, new object[] { groupId, name }); 147 | var objArr = (obj).Cast().ToArray(); 148 | values = Array.CreateInstance(elType, objArr.Length); 149 | Array.Copy(objArr, values, objArr.Length); 150 | } 151 | info.SetValue(readValue, values); 152 | } 153 | else if (primitiveTypes.Contains(code) || ty == typeof(TimeSpan)) 154 | { 155 | Array values = dsetRW.ReadArray(ty, groupId, name); 156 | int[] first = new int[values.Rank].Select(f => 0).ToArray(); 157 | info.SetValue(readValue, values.GetValue(first)); 158 | } 159 | else 160 | { 161 | Object value = info.GetValue(readValue, null); 162 | if (value != null) 163 | { 164 | value = ReadObject(groupId, value, name); 165 | info.SetValue(readValue, value); 166 | } 167 | } 168 | } 169 | } 170 | 171 | 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Strings.cs: -------------------------------------------------------------------------------- 1 | using HDF.PInvoke; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace Hdf5DotNetTools 10 | { 11 | #if HDF5_VER1_10 12 | using hid_t = System.Int64; 13 | #else 14 | using hid_t = System.Int32; 15 | #endif 16 | public static partial class Hdf5 17 | { 18 | 19 | 20 | public static IEnumerable ReadStrings(hid_t groupId, string name) 21 | { 22 | 23 | hid_t datatype = H5T.create(H5T.class_t.STRING, H5T.VARIABLE); 24 | H5T.set_cset(datatype, H5T.cset_t.UTF8); 25 | H5T.set_strpad(datatype, H5T.str_t.NULLTERM); 26 | 27 | //name = ToHdf5Name(name); 28 | 29 | var datasetId = H5D.open(groupId, name); 30 | hid_t spaceId = H5D.get_space(datasetId); 31 | 32 | long count = H5S.get_simple_extent_npoints(spaceId); 33 | H5S.close(spaceId); 34 | 35 | var strs = new List(); 36 | if (count >= 0) 37 | { 38 | IntPtr[] rdata = new IntPtr[count]; 39 | GCHandle hnd = GCHandle.Alloc(rdata, GCHandleType.Pinned); 40 | H5D.read(datasetId, datatype, H5S.ALL, H5S.ALL, 41 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 42 | 43 | for (int i = 0; i < rdata.Length; ++i) 44 | { 45 | int len = 0; 46 | while (Marshal.ReadByte(rdata[i], len) != 0) { ++len; } 47 | byte[] buffer = new byte[len]; 48 | Marshal.Copy(rdata[i], buffer, 0, buffer.Length); 49 | string s = Encoding.UTF8.GetString(buffer); 50 | 51 | strs.Add(s); 52 | 53 | // H5.free_memory(rdata[i]); 54 | } 55 | hnd.Free(); 56 | } 57 | H5T.close(datatype); 58 | H5D.close(datasetId); 59 | return strs; 60 | } 61 | 62 | 63 | public static (int success, hid_t CreatedgroupId) WriteStrings(hid_t groupId, string name, IEnumerable strs, string datasetName = null) 64 | { 65 | 66 | // create UTF-8 encoded test datasets 67 | 68 | hid_t datatype = H5T.create(H5T.class_t.STRING, H5T.VARIABLE); 69 | H5T.set_cset(datatype, H5T.cset_t.UTF8); 70 | H5T.set_strpad(datatype, H5T.str_t.SPACEPAD); 71 | 72 | int strSz = strs.Count(); 73 | hid_t spaceId = H5S.create_simple(1, 74 | new ulong[] { (ulong)strSz }, null); 75 | 76 | var datasetId = H5D.create(groupId, name, datatype, spaceId); 77 | 78 | GCHandle[] hnds = new GCHandle[strSz]; 79 | IntPtr[] wdata = new IntPtr[strSz]; 80 | 81 | int cntr = 0; 82 | foreach (string str in strs) 83 | { 84 | hnds[cntr] = GCHandle.Alloc( 85 | Encoding.UTF8.GetBytes(str), 86 | GCHandleType.Pinned); 87 | wdata[cntr] = hnds[cntr].AddrOfPinnedObject(); 88 | cntr++; 89 | } 90 | 91 | var hnd = GCHandle.Alloc(wdata, GCHandleType.Pinned); 92 | 93 | var result = H5D.write(datasetId, datatype, H5S.ALL, H5S.ALL, 94 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 95 | hnd.Free(); 96 | 97 | for (int i = 0; i < strSz; ++i) 98 | { 99 | hnds[i].Free(); 100 | } 101 | 102 | H5D.close(datasetId); 103 | H5S.close(spaceId); 104 | H5T.close(datatype); 105 | return (result, datasetId); 106 | } 107 | 108 | public static int WriteAsciiString(hid_t groupId, string name, string str) 109 | { 110 | var spaceNullId = H5S.create(H5S.class_t.NULL); 111 | var spaceScalarId = H5S.create(H5S.class_t.SCALAR); 112 | 113 | // create two datasets of the extended ASCII character set 114 | // store as H5T.FORTRAN_S1 -> space padding 115 | 116 | int strLength = str.Length; 117 | ulong[] dims = { (ulong)strLength, 1 }; 118 | 119 | /* Create the dataset. */ 120 | //name = ToHdf5Name(name); 121 | 122 | var spaceId = H5S.create_simple(1, dims, null); 123 | var datasetId = H5D.create(groupId, name, 124 | H5T.FORTRAN_S1, spaceId); 125 | H5S.close(spaceId); 126 | 127 | // we write from C and must provide null-terminated strings 128 | 129 | byte[] wdata = new byte[strLength * 2]; 130 | //for (int i = 0; i < strLength; ++i) 131 | //{ 132 | // wdata[2 * i] = (byte)i; 133 | //} 134 | for (int i = 0; i < strLength; ++i) 135 | { 136 | wdata[2 * i] = Convert.ToByte(str[i]); 137 | } 138 | 139 | var memId = H5T.copy(H5T.C_S1); 140 | H5T.set_size(memId, new IntPtr(2)); 141 | //H5T.set_strpad(memId, H5T.str_t.NULLTERM); 142 | GCHandle hnd = GCHandle.Alloc(wdata, GCHandleType.Pinned); 143 | int result = H5D.write(datasetId, memId, H5S.ALL, 144 | H5S.ALL, H5P.DEFAULT, hnd.AddrOfPinnedObject()); 145 | hnd.Free(); 146 | H5T.close(memId); 147 | H5D.close(datasetId); 148 | return result; 149 | } 150 | 151 | public static string ReadAsciiString(hid_t groupId, string name) 152 | { 153 | var datatype = H5T.FORTRAN_S1; 154 | 155 | //name = ToHdf5Name(name); 156 | 157 | var datasetId = H5D.open(groupId, name); 158 | var spaceId = H5D.get_space(datasetId); 159 | int rank = H5S.get_simple_extent_ndims(spaceId); 160 | ulong[] maxDims = new ulong[rank]; 161 | ulong[] dims = new ulong[rank]; 162 | ulong[] chunkDims = new ulong[rank]; 163 | var memId_n = H5S.get_simple_extent_dims(spaceId, dims, null); 164 | // we write from C and must provide null-terminated strings 165 | 166 | byte[] wdata = new byte[dims[0] * 2]; 167 | 168 | var memId = H5T.copy(H5T.C_S1); 169 | H5T.set_size(memId, new IntPtr(2)); 170 | //H5T.set_strpad(memId, H5T.str_t.NULLTERM); 171 | GCHandle hnd = GCHandle.Alloc(wdata, GCHandleType.Pinned); 172 | int resultId = H5D.read(datasetId, memId, H5S.ALL, 173 | H5S.ALL, H5P.DEFAULT, hnd.AddrOfPinnedObject()); 174 | hnd.Free(); 175 | 176 | wdata = wdata.Where((b, i) => i % 2 == 0). 177 | Select(b => (b == 0) ? (byte)32 : b).ToArray(); 178 | string result = Encoding.ASCII.GetString(wdata); 179 | 180 | H5T.close(memId); 181 | H5D.close(datasetId); 182 | return result; 183 | } 184 | 185 | public static int WriteUnicodeString(hid_t groupId, string name, string str, H5T.str_t strPad = H5T.str_t.SPACEPAD) 186 | { 187 | byte[] wdata = Encoding.UTF8.GetBytes(str); 188 | 189 | hid_t spaceId = H5S.create(H5S.class_t.SCALAR); 190 | 191 | hid_t dtype = H5T.create(H5T.class_t.STRING, new IntPtr(wdata.Length)); 192 | H5T.set_cset(dtype, H5T.cset_t.UTF8); 193 | H5T.set_strpad(dtype, strPad); 194 | 195 | hid_t datasetId = H5D.create(groupId, name, dtype, spaceId); 196 | 197 | GCHandle hnd = GCHandle.Alloc(wdata, GCHandleType.Pinned); 198 | int result = H5D.write(datasetId, dtype, H5S.ALL, 199 | H5S.ALL, H5P.DEFAULT, hnd.AddrOfPinnedObject()); 200 | hnd.Free(); 201 | 202 | H5T.close(dtype); 203 | H5D.close(datasetId); 204 | H5S.close(spaceId); 205 | return result; 206 | } 207 | 208 | public static string ReadUnicodeString(hid_t groupId, string name) 209 | { 210 | var datasetId = H5D.open(groupId, name); 211 | var typeId = H5D.get_type(datasetId); 212 | 213 | if (H5T.is_variable_str(typeId) > 0) 214 | { 215 | var spaceId = H5D.get_space(datasetId); 216 | hid_t count = H5S.get_simple_extent_npoints(spaceId); 217 | 218 | IntPtr[] rdata = new IntPtr[count]; 219 | 220 | GCHandle hnd = GCHandle.Alloc(rdata, GCHandleType.Pinned); 221 | H5D.read(datasetId, typeId, H5S.ALL, H5S.ALL, 222 | H5P.DEFAULT, hnd.AddrOfPinnedObject()); 223 | 224 | var attrStrings = new List(); 225 | for (int i = 0; i < rdata.Length; ++i) 226 | { 227 | int attrLength = 0; 228 | while (Marshal.ReadByte(rdata[i], attrLength) != 0) 229 | { 230 | ++attrLength; 231 | } 232 | 233 | byte[] buffer = new byte[attrLength]; 234 | Marshal.Copy(rdata[i], buffer, 0, buffer.Length); 235 | 236 | string stringPart = Encoding.UTF8.GetString(buffer); 237 | 238 | attrStrings.Add(stringPart); 239 | 240 | H5.free_memory(rdata[i]); 241 | } 242 | 243 | hnd.Free(); 244 | H5S.close(spaceId); 245 | H5D.close(datasetId); 246 | 247 | return attrStrings[0]; 248 | } 249 | 250 | // Must be a non-variable length string. 251 | int size = H5T.get_size(typeId).ToInt32(); 252 | IntPtr iPtr = Marshal.AllocHGlobal(size); 253 | 254 | int result = H5D.read(datasetId, typeId, H5S.ALL, H5S.ALL, 255 | H5P.DEFAULT, iPtr); 256 | if (result < 0) 257 | { 258 | throw new IOException("Failed to read dataset"); 259 | } 260 | 261 | var strDest = new byte[size]; 262 | Marshal.Copy(iPtr, strDest, 0, size); 263 | Marshal.FreeHGlobal(iPtr); 264 | 265 | H5D.close(datasetId); 266 | 267 | return Encoding.UTF8.GetString(strDest).TrimEnd((Char)0); 268 | } 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Hdf5Writer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace Hdf5DotNetTools 6 | { 7 | #if HDF5_VER1_10 8 | using hid_t = System.Int64; 9 | #else 10 | using hid_t = System.Int32; 11 | #endif 12 | public partial class Hdf5 13 | { 14 | 15 | 16 | public static object WriteObject(hid_t groupId, object writeValue, string groupName = null) 17 | { 18 | if (writeValue == null) 19 | { 20 | throw new ArgumentNullException(nameof(writeValue)); 21 | } 22 | 23 | bool createGroupName = !string.IsNullOrWhiteSpace(groupName); 24 | if (createGroupName) 25 | groupId = Hdf5.CreateGroup(groupId, groupName); 26 | 27 | Type tyObject = writeValue.GetType(); 28 | foreach (Attribute attr in Attribute.GetCustomAttributes(tyObject)) 29 | { 30 | if (attr is Hdf5SaveAttribute legAt) 31 | { 32 | Hdf5Save kind = legAt.SaveKind; 33 | if (kind == Hdf5Save.DoNotSave) 34 | return writeValue; 35 | } 36 | } 37 | 38 | WriteProperties(tyObject, writeValue, groupId); 39 | WriteFields(tyObject, writeValue, groupId); 40 | WriteHdf5Attributes(tyObject, groupId, groupName); 41 | if (createGroupName) 42 | Hdf5.CloseGroup(groupId); 43 | return (writeValue); 44 | } 45 | 46 | private static void WriteHdf5Attributes(Type type, hid_t groupId, string name, string datasetName = null) 47 | { 48 | foreach (Attribute attr in Attribute.GetCustomAttributes(type)) 49 | { 50 | if (attr is Hdf5Attribute) 51 | { 52 | var h5at = attr as Hdf5Attribute; 53 | WriteAttribute(groupId, name, h5at.Name, datasetName); 54 | } 55 | if (attr is Hdf5Attributes) 56 | { 57 | var h5ats = attr as Hdf5Attributes; 58 | WriteAttributes(groupId, name, h5ats.Names, datasetName); 59 | } 60 | } 61 | } 62 | 63 | private static void WriteFields(Type tyObject, object writeValue, hid_t groupId) 64 | { 65 | FieldInfo[] miMembers = tyObject.GetFields(BindingFlags.DeclaredOnly | 66 | /*BindingFlags.NonPublic |*/ BindingFlags.Instance | BindingFlags.Public); 67 | 68 | foreach (FieldInfo info in miMembers) 69 | { 70 | if (NoSavePresent(Attribute.GetCustomAttributes(info))) continue; 71 | object infoVal = info.GetValue(writeValue); 72 | if (infoVal == null) 73 | continue; 74 | string name = info.Name; 75 | //bool isEnumerable = info.FieldType.GetInterface(typeof(IEnumerable<>).FullName) != null; 76 | WriteField(infoVal, info, groupId, name); 77 | } 78 | } 79 | 80 | private static void WriteProperties(Type tyObject, object writeValue, hid_t groupId) 81 | { 82 | PropertyInfo[] miMembers = tyObject.GetProperties( 83 | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); 84 | 85 | foreach (PropertyInfo info in miMembers) 86 | { 87 | if (NoSavePresent(Attribute.GetCustomAttributes(info))) continue; 88 | object infoVal = info.GetValue(writeValue, null); 89 | if (infoVal == null) 90 | continue; 91 | string name = info.Name; 92 | 93 | WriteField(infoVal, info, groupId, name); 94 | } 95 | } 96 | 97 | private static bool NoSavePresent(Attribute[] attributes) 98 | { 99 | bool noSaveAttr = false; 100 | foreach (Attribute attr in attributes) 101 | { 102 | var legAttr = attr as Hdf5SaveAttribute; 103 | var kind = legAttr?.SaveKind; 104 | if (kind == Hdf5Save.DoNotSave) 105 | { 106 | noSaveAttr = true; 107 | break; 108 | } 109 | } 110 | 111 | return noSaveAttr; 112 | } 113 | 114 | private static void WriteField(object infoVal, FieldInfo filedInfo, hid_t groupId, string name) 115 | { 116 | Type ty = infoVal.GetType(); 117 | TypeCode code = Type.GetTypeCode(ty); 118 | 119 | if (ty.IsArray) 120 | { 121 | var elType = ty.GetElementType(); 122 | TypeCode elCode = Type.GetTypeCode(elType); 123 | if (elCode != TypeCode.Object || ty == typeof(TimeSpan[])) 124 | dsetRW.WriteArray(groupId, name, (Array)infoVal); 125 | else 126 | { 127 | CallByReflection<(int, hid_t)>(nameof(WriteCompounds), elType, new object[] { groupId, name, infoVal }); 128 | } 129 | } 130 | else if (primitiveTypes.Contains(code) || ty == typeof(TimeSpan)) 131 | //WriteOneValue(groupId, name, infoVal); 132 | { 133 | (int success, hid_t CreatedgroupId) = CallByReflection<(int, hid_t)>(nameof(WriteOneValue), ty, new object[] { groupId, name, infoVal }); 134 | //todo: fix it 135 | //add its attributes if there are: 136 | //foreach (Attribute attr in Attribute.GetCustomAttributes(filedInfo)) 137 | //{ 138 | // if (attr is Hdf5Attribute) 139 | // { 140 | // var h5at = attr as Hdf5Attribute; 141 | // WriteStringAttribute(groupId, name, h5at.Name, name); 142 | // } 143 | 144 | // if (attr is Hdf5Attributes) 145 | // { 146 | // var h5ats = attr as Hdf5Attributes; 147 | // WriteAttributes(groupId, name, h5ats.Names, attr.); 148 | // } 149 | //} 150 | } 151 | else 152 | WriteObject(groupId, infoVal, name); 153 | } 154 | private static void WriteField(object infoVal, PropertyInfo propertyInfo, hid_t groupId, string name) 155 | { 156 | Type ty = infoVal.GetType(); 157 | TypeCode code = Type.GetTypeCode(ty); 158 | 159 | if (ty.IsArray) 160 | { 161 | var elType = ty.GetElementType(); 162 | TypeCode elCode = Type.GetTypeCode(elType); 163 | if (elCode != TypeCode.Object || ty == typeof(TimeSpan[])) 164 | dsetRW.WriteArray(groupId, name, (Array)infoVal); 165 | else 166 | { 167 | { 168 | CallByReflection(nameof(WriteCompounds), elType, new object[] { groupId, name, infoVal }); 169 | //add its attributes 170 | 171 | } 172 | } 173 | } 174 | else if (primitiveTypes.Contains(code) || ty == typeof(TimeSpan)) 175 | //WriteOneValue(groupId, name, infoVal); 176 | CallByReflection<(int success, hid_t CreatedgroupId)>(nameof(WriteOneValue), ty, new object[] { groupId, name, infoVal }); 177 | else 178 | WriteObject(groupId, infoVal, name); 179 | } 180 | static T CallByReflection(string name, Type typeArg, object[] values) 181 | { 182 | // Just for simplicity, assume it's public etc 183 | MethodInfo method = typeof(Hdf5).GetMethod(name); 184 | MethodInfo generic = method.MakeGenericMethod(typeArg); 185 | return (T)generic.Invoke(null, values); 186 | 187 | } 188 | 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /Hdf5DotNetTools/Knf.Utils.Hdf5.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reyntjesr/Hdf5DotnetTools/d14730d239c167dcd639c02ff3a6bad7dc9d8c34/Hdf5DotNetTools/Knf.Utils.Hdf5.snk -------------------------------------------------------------------------------- /Hdf5DotNetTools/copy nuget.bat: -------------------------------------------------------------------------------- 1 | REM dotnet add package .\bin\Debug\Hdf5DotNetTools.0.2.0.nupkg -s D:\DotNet2017\NuGet\Packages 2 | nuget add .\bin\Debug\Hdf5DotNetTools.0.2.0.nupkg -s D:\DotNet2017\NuGet\Packages -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/FileClosedArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Hdf5DotnetTools.DataTypes 4 | { 5 | public class FileClosedArgs : EventArgs 6 | { 7 | public string ClosedFile { get; } 8 | public bool CancelRequested { get; set; } 9 | 10 | public FileClosedArgs(string fileName) 11 | { 12 | ClosedFile = fileName; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/Hdf5Channel.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotNetTools; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Hdf5DotnetTools.DataTypes 5 | { 6 | [Hdf5GroupName("Channel")] 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct Hdf5Channel 9 | { 10 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] 11 | public string Label; 12 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] 13 | public string Dimension; 14 | public double Amplification; 15 | public double Offset; 16 | public double SamplingRate; 17 | public ulong NrOfSamples; 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/Hdf5Channels.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotNetTools; 2 | 3 | namespace Hdf5DotnetTools.DataTypes 4 | { 5 | 6 | [Hdf5GroupName("Channels")] 7 | public class Hdf5Channels 8 | { 9 | public Hdf5Channels(int length) 10 | { 11 | Labels = new string[length]; 12 | Dimensions = new string[length]; 13 | Amplifications = new double[length]; 14 | Offsets = new double[length]; 15 | SamplingRates = new double[length]; 16 | NrOfSamples = new int[length]; 17 | } 18 | public string[] Labels { get; set; } 19 | public string[] Dimensions { get; set; } 20 | public double[] Amplifications { get; set; } 21 | public double[] Offsets { get; set; } 22 | public double[] SamplingRates { get; set; } 23 | public int[] NrOfSamples { get; set; } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/Hdf5Event.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotNetTools; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace Hdf5DotnetTools.DataTypes 6 | { 7 | /// 8 | /// 9 | /// 10 | [Hdf5GroupName("Event")] 11 | [StructLayout(LayoutKind.Sequential)] 12 | public struct Hdf5Event 13 | { 14 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)] 15 | public string Event; 16 | 17 | /// 18 | /// Time property. Datetimes can't be saved so the TimeTicks field gets saved 19 | /// 20 | [Hdf5Save(Hdf5Save.DoNotSave)] 21 | public DateTime Time 22 | { 23 | get => new DateTime(TimeTicks); 24 | set => TimeTicks = value.Ticks; 25 | } 26 | 27 | public long TimeTicks; 28 | 29 | /// 30 | /// Duration property. Timespans can't be saved so the DurationTicks field gets saved 31 | /// 32 | [Hdf5Save(Hdf5Save.DoNotSave)] 33 | public TimeSpan Duration 34 | { 35 | get => new TimeSpan(DurationTicks); 36 | set => DurationTicks = value.Ticks; 37 | } 38 | 39 | public long DurationTicks; 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/Hdf5Events.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotNetTools; 2 | using System; 3 | 4 | namespace Hdf5DotnetTools.DataTypes 5 | { 6 | [Hdf5GroupName("Events")] 7 | public struct Hdf5Events 8 | { 9 | public string[] Events { get; set; } 10 | public DateTime[] Times { get; set; } 11 | public TimeSpan[] Durations { get; set; } 12 | public Hdf5Events(int length) 13 | { 14 | Events = new string[length]; 15 | Times = new DateTime[length]; 16 | Durations = new TimeSpan[length]; 17 | } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/Hdf5Patient.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotNetTools; 2 | using System; 3 | 4 | namespace Hdf5DotnetTools.DataTypes 5 | { 6 | [Hdf5GroupName("Patient")] 7 | public class Hdf5Patient 8 | { 9 | public string Name = ""; 10 | public string Id = ""; 11 | public int RecId = -1; 12 | public string Gender = ""; 13 | public DateTime BirthDate = DateTime.Now; 14 | public double Height = double.NaN; 15 | public double Weight = double.NaN; 16 | public DateTime EditData = DateTime.Now; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/DataTypes/Hdf5Recording.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotNetTools; 2 | using System; 3 | using System.ComponentModel; 4 | 5 | namespace Hdf5DotnetTools.DataTypes 6 | { 7 | [Hdf5GroupName("Recording")] 8 | public class Hdf5Recording 9 | { 10 | int _nrOfChannels; 11 | 12 | [Hdf5Save(Hdf5Save.DoNotSave)] 13 | public event PropertyChangedEventHandler PropertyChanged; 14 | 15 | public string Id { get; set; } = ""; 16 | public bool ActiveFilter { get; set; } = false; 17 | public DateTime StartTime { get; set; } = DateTime.Now; 18 | public DateTime EndTime { get; set; } = DateTime.Now; 19 | public ulong NrOfSamples { get; set; } = 0; 20 | public double SampleRate { get; set; } = double.NaN; 21 | public string Physician { get; set; } = ""; 22 | public string Laborant { get; set; } = ""; 23 | 24 | public int NrOfChannels 25 | { 26 | get => _nrOfChannels; 27 | set 28 | { 29 | if (_nrOfChannels != value) 30 | { 31 | _nrOfChannels = value; 32 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NrOfChannels))); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Hdf5DotnetTools/Interfaces/IHdf5AcquisitionFile.cs: -------------------------------------------------------------------------------- 1 | using Hdf5DotnetTools.DataTypes; 2 | using System.Collections.Generic; 3 | 4 | namespace Hdf5DotnetTools.Interfaces 5 | { 6 | public interface IHdf5AcquisitionFile 7 | { 8 | Hdf5Patient Patient { get; set; } 9 | Hdf5Recording Recording { get; set; } 10 | Hdf5Channel[] Channels { get; set; } 11 | List EventList { get; } 12 | Hdf5Events Events { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5AcquisitionFileTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Hdf5DotNetTools; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using HDF.PInvoke; 8 | using System.Threading.Tasks; 9 | using System.Threading; 10 | using Hdf5DotnetTools.DataTypes; 11 | 12 | namespace Hdf5UnitTests 13 | { 14 | public partial class Hdf5UnitTests 15 | { 16 | private Hdf5AcquisitionFile FillHeader(Hdf5AcquisitionFile header) 17 | { 18 | header.Patient.Name = "Robert"; 19 | header.Patient.Gender = "Male"; 20 | header.Patient.BirthDate = new DateTime(1969, 1, 12); 21 | header.Patient.Id = "8475805"; 22 | header.Recording.NrOfChannels = 5; 23 | header.Recording.SampleRate = 200; 24 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 25 | { 26 | var chn = header.Channels[i]; 27 | chn.Label = $"DC{(i + 1):D2}"; 28 | chn.Dimension = "V"; 29 | chn.Offset = 0; 30 | chn.Amplification = (double)(10 - -10) / (short.MaxValue - short.MinValue); 31 | chn.SamplingRate = header.Recording.SampleRate; 32 | header.Channels[i] = chn; 33 | } 34 | header.EventList.Add(new Hdf5Event() { Event = "an event", Time = DateTime.Now }); 35 | header.EventList.Add(new Hdf5Event() { Event = "a second event", Time = DateTime.Now + TimeSpan.FromSeconds(2) }); 36 | return header; 37 | 38 | } 39 | 40 | [TestMethod] 41 | public void WriteAndReadNoDataAcquisitionFile() 42 | { 43 | string filename = Path.Combine(folder, "testNoDataAcquisition.H5"); 44 | try 45 | { 46 | using (var writer = new Hdf5AcquisitionFileWriter(filename)) 47 | { 48 | var data = FillHeader(writer.Header); 49 | } 50 | } 51 | catch (Exception ex) 52 | { 53 | CreateExceptionAssert(ex); 54 | } 55 | 56 | try 57 | { 58 | using (var reader = new Hdf5AcquisitionFileReader(filename)) 59 | { 60 | var header = reader.Header; 61 | Assert.IsTrue(header.Patient.Name == "Robert"); 62 | Assert.IsTrue(header.Channels.Select(c => c.Label).SequenceEqual(new string[] { "DC01", "DC02", "DC03", "DC04", "DC05" })); 63 | Assert.IsTrue(header.Events.Events.First() == "an event"); 64 | Assert.IsTrue(header.Events.Events.Last() == "a second event"); 65 | } 66 | } 67 | catch (Exception ex) 68 | { 69 | CreateExceptionAssert(ex); 70 | } 71 | } 72 | 73 | /// 74 | /// an acquisition file is created that has 5 channels of data 75 | /// for each channel 100 samples are written in two steps of 50 samples to the file 76 | /// The total number of samples is written to the channels and recording objects 77 | /// 78 | [TestMethod] 79 | public void WriteAndReadWithSignalsAcquisitionFile() 80 | { 81 | string filename = Path.Combine(folder, "testWithSignalsAcquisition.H5"); 82 | try 83 | { 84 | using (var writer = new Hdf5AcquisitionFileWriter(filename)) 85 | { 86 | var header = FillHeader(writer.Header); 87 | var signals = new List(header.Recording.NrOfChannels); 88 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 89 | { 90 | signals.Add(Enumerable.Range(i * 50, 50).Select(j => j / 50.0).ToArray()); 91 | } 92 | 93 | writer.Write(signals); 94 | signals = new List(header.Recording.NrOfChannels); 95 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 96 | { 97 | signals.Add(Enumerable.Range((i + 1) * 50, 50).Select(j => j / 50.0).ToArray()); 98 | } 99 | writer.Write(signals); 100 | } 101 | } 102 | catch (Exception ex) 103 | { 104 | CreateExceptionAssert(ex); 105 | } 106 | 107 | try 108 | { 109 | using (var reader = new Hdf5AcquisitionFileReader(filename)) 110 | { 111 | var header = reader.Header; 112 | Assert.IsTrue(header.Patient.Name == "Robert"); 113 | Assert.IsTrue(header.Recording.NrOfSamples == 100); 114 | Assert.IsTrue(header.Channels.Select(c => c.Label).SequenceEqual(new string[] { "DC01", "DC02", "DC03", "DC04", "DC05" })); 115 | Assert.IsTrue(header.Channels.Select(c => c.NrOfSamples).SequenceEqual(new ulong[] { 100, 100, 100, 100, 100 })); 116 | var data = reader.ReadDouble(0, 49); 117 | var sig = data.First().Take(5); 118 | Assert.IsTrue(sig.Similar(new double[] { 0, 1 / 50f, 2 / 50f, 3 / 50f, 4 / 50f })); 119 | data = reader.ReadDouble(50, 99); 120 | sig = data.First().Take(5); 121 | Assert.IsTrue(sig.Similar(new double[] { 50 / 50f, 51 / 50f, 52 / 50f, 53 / 50f, 54 / 50f })); 122 | } 123 | } 124 | catch (Exception ex) 125 | { 126 | CreateExceptionAssert(ex); 127 | } 128 | } 129 | 130 | /// 131 | /// an acquisition file is created that has 5 channels of data 132 | /// for each channel 100 samples are written in two steps of 50 samples to the file 133 | /// The total number of samples is written to the channels and recording objects 134 | /// 135 | [TestMethod] 136 | public void WriteAndReadWithDataAcquisitionFile() 137 | { 138 | string filename = Path.Combine(folder, "testWithDataAcquisition.H5"); 139 | try 140 | { 141 | using (var writer = new Hdf5AcquisitionFileWriter(filename)) 142 | { 143 | var header = FillHeader(writer.Header); 144 | var data = new short[50, header.Recording.NrOfChannels]; 145 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 146 | for (int j = 0; j < 50; j++) 147 | { 148 | data[j, i] = writer.Convert2Short(i + j / 50.0, i); 149 | } 150 | 151 | writer.Write(data); 152 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 153 | for (int j = 0; j < 50; j++) 154 | { 155 | data[j, i] = writer.Convert2Short(i + 1 + j / 50.0, i); 156 | } 157 | writer.Write(data); 158 | /*header.Recording.NrOfSamples = 100; 159 | for (int i = 0; i < header.Channels.Length; i++) 160 | { 161 | header.Channels[i].NrOfSamples = header.Recording.NrOfSamples; 162 | }*/ 163 | } 164 | } 165 | catch (Exception ex) 166 | { 167 | CreateExceptionAssert(ex); 168 | } 169 | 170 | try 171 | { 172 | using (var reader = new Hdf5AcquisitionFileReader(filename)) 173 | { 174 | var header = reader.Header; 175 | Assert.IsTrue(header.Patient.Name == "Robert"); 176 | Assert.IsTrue(header.Recording.NrOfSamples == 100); 177 | Assert.IsTrue(header.Channels.Select(c => c.Label).SequenceEqual(new string[] { "DC01", "DC02", "DC03", "DC04", "DC05" })); 178 | Assert.IsTrue(header.Channels.Select(c => c.NrOfSamples).SequenceEqual(new ulong[] { 100, 100, 100, 100, 100 })); 179 | var data = reader.ReadDouble(0, 49); 180 | var sig = data.First().Take(5); 181 | Assert.IsTrue(sig.Similar(new double[] { 0, 1 / 50f, 2 / 50f, 3 / 50f, 4 / 50f })); 182 | data = reader.ReadDouble(50, 99); 183 | sig = data.First().Take(5); 184 | Assert.IsTrue(sig.Similar(new double[] { 50 / 50f, 51 / 50f, 52 / 50f, 53 / 50f, 54 / 50f })); 185 | } 186 | } 187 | catch (Exception ex) 188 | { 189 | CreateExceptionAssert(ex); 190 | } 191 | // File Delete gives exception 192 | //try 193 | //{ 194 | // File.Delete(filename); // Cannot delete the file. Error being used by another process. 195 | 196 | //} 197 | //catch (Exception ex) 198 | //{ 199 | // CreateExceptionAssert(ex); 200 | //} 201 | } 202 | 203 | /// 204 | /// an acquisition file is created that has 5 channels of data 205 | /// for each channel 100 samples are written in two steps of 50 samples to the file 206 | /// The total number of samples is written to the channels and recording objects 207 | /// 208 | [TestMethod] 209 | public void WriteAndReadThreadSafeDataToAcquisitionFile() 210 | { 211 | string filename = Path.Combine(folder, "testWithThreadsDataAcquisition.H5"); 212 | int N = 50; 213 | int nrNsamples = 1000; 214 | try 215 | { 216 | using (var writer = new Hdf5AcquisitionFileWriter(filename)) 217 | { 218 | void saveChunck(IEnumerable d) 219 | { 220 | writer.Write(d); 221 | } 222 | var pc = new DataProducerConsumer>(saveChunck); 223 | var header = FillHeader(writer.Header); 224 | var data = new List(); 225 | 226 | for (int j = 0; j < nrNsamples; j++) 227 | { 228 | 229 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 230 | { 231 | var row = Enumerable.Range(0, N).Select(x => i + j + x / (double)N).ToArray(); 232 | data.Add(row); 233 | } 234 | pc.Produce(data); 235 | Thread.Sleep(10); 236 | data.Clear(); 237 | } 238 | //for (int i = 0; i < header.Recording.NrOfChannels; i++) 239 | //{ 240 | // var row = Enumerable.Range(0, 50).Select(x => i + 1 + x / 50.0).ToArray(); 241 | // data.Add(row); 242 | //} 243 | //pc.Produce(data); 244 | Thread.Sleep(1000); 245 | pc.Done(); 246 | } 247 | } 248 | catch (Exception ex) 249 | { 250 | CreateExceptionAssert(ex); 251 | } 252 | 253 | try 254 | { 255 | using (var reader = new Hdf5AcquisitionFileReader(filename)) 256 | { 257 | var header = reader.Header; 258 | Assert.IsTrue(header.Patient.Name == "Robert"); 259 | ulong samps = Convert.ToUInt64(N * nrNsamples); 260 | Assert.IsTrue(header.Recording.NrOfSamples == samps); 261 | Assert.IsTrue(header.Channels.Select(c => c.Label).SequenceEqual(new string[] { "DC01", "DC02", "DC03", "DC04", "DC05" })); 262 | Assert.IsTrue(header.Channels.Select(c => c.NrOfSamples).SequenceEqual(new ulong[] { samps, samps, samps, samps, samps })); 263 | var data = reader.ReadDouble(0, (ulong)N - 1); 264 | var sig = data.First().Take(5); 265 | var sim = Enumerable.Range(0, 5).Select(d => d / (double)N); 266 | Assert.IsTrue(sig.Similar(sim)); 267 | data = reader.ReadDouble((ulong)N, Convert.ToUInt64(2 * N - 1)); 268 | sig = data.First().Take(5); 269 | sim = Enumerable.Range(N, 5).Select(d => d / (double)N); 270 | Assert.IsTrue(sig.Similar(sim)); 271 | } 272 | } 273 | catch (Exception ex) 274 | { 275 | CreateExceptionAssert(ex); 276 | } 277 | } 278 | 279 | /// 280 | /// an acquisition file is created that has 5 channels of data 281 | /// for each channel 100 samples are written in two steps of 50 samples to the file 282 | /// The total number of samples is written to the channels and recording objects 283 | /// 284 | [TestMethod] 285 | public void WriteAndReadShortThreadSafeDataToAcquisitionFile() 286 | { 287 | string filename = Path.Combine(folder, "testWithThreadsDataAcquisition.H5"); 288 | int N = 50; 289 | int nrNsamples = 1000; 290 | try 291 | { 292 | using (var writer = new Hdf5AcquisitionFileWriter(filename)) 293 | { 294 | void saveChunck(short[,] d) 295 | { 296 | writer.Write(d); 297 | } 298 | var pc = new DataProducerConsumer(saveChunck); 299 | var header = FillHeader(writer.Header); 300 | var data = new List(); 301 | 302 | for (int j = 0; j < nrNsamples; j++) 303 | { 304 | 305 | short[,] shData = new short[N, header.Recording.NrOfChannels]; 306 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 307 | { 308 | var row = Enumerable.Range(0, N).Select(x => i + j + x / (double)N).ToArray(); 309 | data.Add(row); 310 | for (int k = 0; k < N; k++) 311 | { 312 | shData[k, i] = writer.Convert2Short(row[k], i); 313 | } 314 | } 315 | 316 | pc.Produce(shData); 317 | Thread.Sleep(10); 318 | data.Clear(); 319 | } 320 | //for (int i = 0; i < header.Recording.NrOfChannels; i++) 321 | //{ 322 | // var row = Enumerable.Range(0, 50).Select(x => i + 1 + x / 50.0).ToArray(); 323 | // data.Add(row); 324 | //} 325 | //pc.Produce(data); 326 | Thread.Sleep(1000); 327 | pc.Done(); 328 | } 329 | } 330 | catch (Exception ex) 331 | { 332 | CreateExceptionAssert(ex); 333 | } 334 | 335 | try 336 | { 337 | using (var reader = new Hdf5AcquisitionFileReader(filename)) 338 | { 339 | var header = reader.Header; 340 | Assert.IsTrue(header.Patient.Name == "Robert"); 341 | ulong samps = Convert.ToUInt64(N * nrNsamples); 342 | Assert.IsTrue(header.Recording.NrOfSamples == samps); 343 | Assert.IsTrue(header.Channels.Select(c => c.Label).SequenceEqual(new string[] { "DC01", "DC02", "DC03", "DC04", "DC05" })); 344 | Assert.IsTrue(header.Channels.Select(c => c.NrOfSamples).SequenceEqual(new ulong[] { samps, samps, samps, samps, samps })); 345 | var data = reader.ReadDouble(0, (ulong)N - 1); 346 | var sig = data.First().Take(5); 347 | var sim = Enumerable.Range(0, 5).Select(d => d / (double)N); 348 | Assert.IsTrue(sig.Similar(sim)); 349 | data = reader.ReadDouble((ulong)N, Convert.ToUInt64(2 * N - 1)); 350 | sig = data.First().Take(5); 351 | sim = Enumerable.Range(N, 5).Select(d => d / (double)N); 352 | Assert.IsTrue(sig.Similar(sim)); 353 | } 354 | } 355 | catch (Exception ex) 356 | { 357 | CreateExceptionAssert(ex); 358 | } 359 | } 360 | 361 | 362 | /// 363 | /// an acquisition file is created that has 5 channels of data 364 | /// for each channel 100 samples are written in two steps of 50 samples to the file 365 | /// The total number of samples is written to the channels and recording objects 366 | /// 367 | [TestMethod] 368 | public void WriteAndReadWithCloseThreadSafeDataToAcquisitionFile() 369 | { 370 | string filename = Path.Combine(folder, "testWithThreadsDataAcquisition.H5"); 371 | int N = 50; 372 | int nrNsamples = 1000; 373 | try 374 | { 375 | var writer = new Hdf5AcquisitionFileWriter(filename); 376 | void SaveChunck(IEnumerable d) 377 | { 378 | writer.Write(d); 379 | } 380 | var pc = new DataProducerConsumer>(SaveChunck); 381 | var header = FillHeader(writer.Header); 382 | var data = new List(); 383 | 384 | for (int j = 0; j < nrNsamples; j++) 385 | { 386 | 387 | for (int i = 0; i < header.Recording.NrOfChannels; i++) 388 | { 389 | var row = Enumerable.Range(0, N).Select(x => i + j + x / (double)N).ToArray(); 390 | data.Add(row); 391 | } 392 | pc.Produce(data); 393 | Thread.Sleep(10); 394 | data.Clear(); 395 | } 396 | //for (int i = 0; i < header.Recording.NrOfChannels; i++) 397 | //{ 398 | // var row = Enumerable.Range(0, 50).Select(x => i + 1 + x / 50.0).ToArray(); 399 | // data.Add(row); 400 | //} 401 | //pc.Produce(data); 402 | Thread.Sleep(1000); 403 | pc.Done(); 404 | writer.Dispose(); 405 | } 406 | catch (Exception ex) 407 | { 408 | CreateExceptionAssert(ex); 409 | } 410 | 411 | try 412 | { 413 | using (var reader = new Hdf5AcquisitionFileReader(filename)) 414 | { 415 | var header = reader.Header; 416 | Assert.IsTrue(header.Patient.Name == "Robert"); 417 | ulong samps = Convert.ToUInt64(N * nrNsamples); 418 | Assert.IsTrue(header.Recording.NrOfSamples == samps); 419 | Assert.IsTrue(header.Channels.Select(c => c.Label).SequenceEqual(new string[] { "DC01", "DC02", "DC03", "DC04", "DC05" })); 420 | Assert.IsTrue(header.Channels.Select(c => c.NrOfSamples).SequenceEqual(new ulong[] { samps, samps, samps, samps, samps })); 421 | var data = reader.ReadDouble(0, (ulong)N - 1); 422 | var sig = data.First().Take(5); 423 | var sim = Enumerable.Range(0, 5).Select(d => d / (double)N); 424 | Assert.IsTrue(sig.Similar(sim)); 425 | data = reader.ReadDouble((ulong)N, Convert.ToUInt64(2 * N - 1)); 426 | sig = data.First().Take(5); 427 | sim = Enumerable.Range(N, 5).Select(d => d / (double)N); 428 | Assert.IsTrue(sig.Similar(sim)); 429 | } 430 | } 431 | catch (Exception ex) 432 | { 433 | CreateExceptionAssert(ex); 434 | } 435 | 436 | } 437 | 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5AttributeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Hdf5DotNetTools; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using HDF.PInvoke; 8 | using System.Reflection; 9 | using System.Diagnostics; 10 | 11 | namespace Hdf5UnitTests 12 | { 13 | #if HDF5_VER1_10 14 | using hid_t = System.Int64; 15 | #else 16 | using hid_t = System.Int32; 17 | #endif 18 | public partial class Hdf5UnitTests 19 | { 20 | [TestMethod] 21 | public void WriteAndReadAttribute() 22 | { 23 | string filename = Path.Combine(folder, "testAttribute.H5"); 24 | try 25 | { 26 | var fileId = Hdf5.CreateFile(filename); 27 | Assert.IsTrue(fileId > 0); 28 | var groupId = Hdf5.CreateGroup(fileId, "test"); 29 | DateTime nowTime = DateTime.Now; 30 | Hdf5.WriteAttribute(groupId, "time", nowTime); 31 | DateTime readTime = Hdf5.ReadAttribute(groupId, "time"); 32 | Assert.IsTrue(readTime == nowTime); 33 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 34 | } 35 | catch (Exception ex) 36 | { 37 | CreateExceptionAssert(ex); 38 | } 39 | } 40 | 41 | [TestMethod] 42 | public void WriteAndReadStringAttribute() 43 | { 44 | string filename = Path.Combine(folder, "testAttributeString.H5"); 45 | try 46 | { 47 | var fileId = Hdf5.CreateFile(filename); 48 | Assert.IsTrue(fileId > 0); 49 | var groupId = Hdf5.CreateGroup(fileId, "test"); 50 | string attrStr = "this is an attribute"; 51 | Hdf5.WriteAttribute(groupId, "time", attrStr); 52 | string readStr = Hdf5.ReadAttribute(groupId, "time"); 53 | Assert.IsTrue(readStr == attrStr); 54 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 55 | } 56 | catch (Exception ex) 57 | { 58 | CreateExceptionAssert(ex); 59 | } 60 | } 61 | 62 | [TestMethod] 63 | public void WriteAndReadAttributes() 64 | { 65 | string filename = Path.Combine(folder, "testAttributes.H5"); 66 | int[] intValues = new int[] { 1, 2 }; 67 | double dblValue = 1.1; 68 | string strValue = "test"; 69 | string[] strValues = new string[2] { "test", "another test" }; 70 | bool boolValue = true; 71 | DateTime dateValue = new DateTime(1969, 1, 12); 72 | var groupStr = "/test"; 73 | 74 | //string concatFunc(string x) => string.Concat(groupStr, "/", x); 75 | string intName = nameof(intValues); 76 | string dblName = nameof(dblValue); 77 | string strName = nameof(strValue); 78 | string strNames = nameof(strValues); 79 | string boolName = nameof(boolValue); 80 | string dateName = nameof(dateValue); 81 | 82 | try 83 | { 84 | var fileId = Hdf5.CreateFile(filename); 85 | Assert.IsTrue(fileId > 0); 86 | var groupId = Hdf5.CreateGroup(fileId, groupStr); 87 | Hdf5.WriteAttributes(groupId, intName, intValues); 88 | Hdf5.WriteAttribute(groupId, dblName, dblValue); 89 | Hdf5.WriteAttribute(groupId, strName, strValue); 90 | Hdf5.WriteAttributes(groupId, strNames, strValues); 91 | Hdf5.WriteAttribute(groupId, boolName, boolValue); 92 | Hdf5.WriteAttribute(groupId, dateName, dateValue); 93 | H5G.close(groupId); 94 | Hdf5.CloseFile(fileId); 95 | } 96 | catch (Exception ex) 97 | { 98 | CreateExceptionAssert(ex); 99 | } 100 | 101 | try 102 | { 103 | var fileId = Hdf5.OpenFile(filename); 104 | Assert.IsTrue(fileId > 0); 105 | var groupId = H5G.open(fileId, groupStr); 106 | IEnumerable readInts = (int[])Hdf5.ReadAttributes(groupId, intName); 107 | Assert.IsTrue(intValues.SequenceEqual(readInts)); 108 | double readDbl = Hdf5.ReadAttribute(groupId, dblName); 109 | Assert.IsTrue(dblValue == readDbl); 110 | string readStr = Hdf5.ReadAttribute(groupId, strName); 111 | Assert.IsTrue(strValue == readStr); 112 | IEnumerable readStrs = (string[])Hdf5.ReadAttributes(groupId, strNames); 113 | Assert.IsTrue(strValues.SequenceEqual(readStrs)); 114 | bool readBool = Hdf5.ReadAttribute(groupId, boolName); 115 | Assert.IsTrue(boolValue == readBool); 116 | DateTime readDate = Hdf5.ReadAttribute(groupId, dateName); 117 | Assert.IsTrue(dateValue == readDate); 118 | H5G.close(groupId); 119 | Hdf5.CloseFile(fileId); 120 | } 121 | catch (Exception ex) 122 | { 123 | CreateExceptionAssert(ex); 124 | } 125 | } 126 | 127 | [TestMethod] 128 | public void WriteAndReadObjectWithHdf5Attributes() 129 | { 130 | string groupName = "anObject"; 131 | string filename = Path.Combine(folder, "testHdf5Attribute.H5"); 132 | var attObject = new AttributeClass(); 133 | try 134 | { 135 | var fileId = Hdf5.CreateFile(filename); 136 | Assert.IsTrue(fileId > 0); 137 | Hdf5.WriteObject(fileId, attObject, groupName); 138 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 139 | } 140 | catch (Exception ex) 141 | { 142 | CreateExceptionAssert(ex); 143 | } 144 | 145 | var OpenFileId = Hdf5.OpenFile(filename); 146 | var data = Hdf5.ReadObject(OpenFileId, groupName); 147 | Type tyObject = data.aDatetime.GetType(); 148 | //TODO: Have to find out why this doesn't work 149 | foreach (Attribute attr in Attribute.GetCustomAttributes(tyObject)) 150 | { 151 | if (attr is Hdf5Attribute h5att) 152 | { 153 | Assert.IsTrue(h5att.Name == "birthdate"); 154 | } 155 | } 156 | 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5CompoundTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using Hdf5DotNetTools; 7 | using System.IO; 8 | 9 | namespace Hdf5UnitTests 10 | { 11 | public partial class Hdf5UnitTests 12 | { 13 | 14 | [TestMethod] 15 | public void WriteAndReadObjectWithStructs() 16 | { 17 | string filename = Path.Combine(folder, "testObjectWithStructArray.H5"); 18 | 19 | 20 | try 21 | { 22 | 23 | var fileId = Hdf5.CreateFile(filename); 24 | Assert.IsTrue(fileId > 0); 25 | var status = Hdf5.WriteObject(fileId, classWithStructs, "test"); 26 | Hdf5.CloseFile(fileId); 27 | } 28 | catch (Exception ex) 29 | { 30 | CreateExceptionAssert(ex); 31 | } 32 | 33 | try 34 | { 35 | TestClassWithStructs objWithStructs; 36 | var fileId = Hdf5.OpenFile(filename); 37 | Assert.IsTrue(fileId > 0); 38 | objWithStructs = Hdf5.ReadObject(fileId, "test"); 39 | CollectionAssert.AreEqual(wDataList, objWithStructs.DataList); 40 | Hdf5.CloseFile(fileId); 41 | 42 | 43 | } 44 | catch (Exception ex) 45 | { 46 | CreateExceptionAssert(ex); 47 | } 48 | 49 | } 50 | 51 | 52 | 53 | [TestMethod] 54 | public void WriteAndReadStructs() 55 | { 56 | string filename = Path.Combine(folder, "testCompounds.H5"); 57 | 58 | try 59 | { 60 | 61 | var fileId = Hdf5.CreateFile(filename); 62 | Assert.IsTrue(fileId > 0); 63 | var status = Hdf5.WriteCompounds(fileId, "/test", wData2List); 64 | Hdf5.CloseFile(fileId); 65 | } 66 | catch (Exception ex) 67 | { 68 | CreateExceptionAssert(ex); 69 | } 70 | 71 | try 72 | { 73 | var fileId = Hdf5.OpenFile(filename); 74 | Assert.IsTrue(fileId > 0); 75 | var cmpList = Hdf5.ReadCompounds(fileId, "/test").ToArray(); 76 | Hdf5.CloseFile(fileId); 77 | CollectionAssert.AreEqual(wData2List, cmpList); 78 | 79 | } 80 | catch (Exception ex) 81 | { 82 | CreateExceptionAssert(ex); 83 | } 84 | 85 | } 86 | 87 | [TestMethod] 88 | public void WriteAndReadStructsWithDatetime() 89 | { 90 | string filename = Path.Combine(folder, "testCompounds.H5"); 91 | 92 | try 93 | { 94 | 95 | var fileId = Hdf5.CreateFile(filename); 96 | Assert.IsTrue(fileId > 0); 97 | var status = Hdf5.WriteCompounds(fileId, "/test", wDataList); 98 | Hdf5.CloseFile(fileId); 99 | } 100 | catch (Exception ex) 101 | { 102 | CreateExceptionAssert(ex); 103 | } 104 | 105 | try 106 | { 107 | var fileId = Hdf5.OpenFile(filename); 108 | Assert.IsTrue(fileId > 0); 109 | var cmpList = Hdf5.ReadCompounds(fileId, "/test").ToArray(); 110 | Hdf5.CloseFile(fileId); 111 | CollectionAssert.AreEqual(wDataList, cmpList); 112 | 113 | } 114 | catch (Exception ex) 115 | { 116 | CreateExceptionAssert(ex); 117 | } 118 | 119 | } 120 | 121 | [TestMethod] 122 | public void WriteAndReadStructsWithArray() 123 | { 124 | string filename = Path.Combine(folder, "testArrayCompounds.H5"); 125 | 126 | try 127 | { 128 | 129 | var fileId = Hdf5.CreateFile(filename); 130 | Assert.IsTrue(fileId > 0); 131 | var status = Hdf5.WriteCompounds(fileId, "/test", responseList); 132 | Hdf5.CloseFile(fileId); 133 | } 134 | catch (Exception ex) 135 | { 136 | CreateExceptionAssert(ex); 137 | } 138 | 139 | try 140 | { 141 | var fileId = Hdf5.OpenFile(filename); 142 | Assert.IsTrue(fileId > 0); 143 | Responses[] cmpList = Hdf5.ReadCompounds(fileId, "/test").ToArray(); 144 | Hdf5.CloseFile(fileId); 145 | var isSame = responseList.Zip(cmpList, (r, c) => 146 | { 147 | return r.MCID == c.MCID && 148 | r.PanelIdx == c.PanelIdx && 149 | r.ResponseValues.Zip(c.ResponseValues, (rr, cr) => rr == cr).All(v => v == true); 150 | }); 151 | Assert.IsTrue(isSame.All(s => s == true)); 152 | 153 | } 154 | catch (Exception ex) 155 | { 156 | CreateExceptionAssert(ex); 157 | } 158 | 159 | } 160 | 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5DatasetTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Hdf5DotNetTools; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using HDF.PInvoke; 8 | using System.Reflection; 9 | 10 | namespace Hdf5UnitTests 11 | { 12 | public partial class Hdf5UnitTests 13 | { 14 | [TestMethod] 15 | public void WriteAndReadDatetimeDataset() 16 | { 17 | string filename = Path.Combine(folder, "testDatetime.H5"); 18 | var times = new DateTime[10, 5]; 19 | var offset = new DateTime(2000, 1, 1, 12, 0, 0); 20 | for (var i = 0; i < 10; i++) 21 | for (var j = 0; j < 5; j++) 22 | { 23 | times[i, j] = offset.AddDays(i + j * 5); 24 | } 25 | 26 | try 27 | { 28 | var fileId = Hdf5.CreateFile(filename); 29 | Assert.IsTrue(fileId > 0); 30 | Hdf5.WriteDataset(fileId, "/test", times); 31 | 32 | var timesRead = (DateTime[,])Hdf5.ReadDataset(fileId, "/test"); 33 | CompareDatasets(times, timesRead); 34 | 35 | Hdf5.CloseFile(fileId); 36 | } 37 | catch (Exception ex) 38 | { 39 | CreateExceptionAssert(ex); 40 | } 41 | } 42 | 43 | [TestMethod] 44 | public void WriteAndReadTimespanDataset() 45 | { 46 | string filename = Path.Combine(folder, "testTimespan.H5"); 47 | var times = new TimeSpan[10, 5]; 48 | var offset = new TimeSpan(1, 0, 0, 0, 0); 49 | for (var i = 0; i < 10; i++) 50 | for (var j = 0; j < 5; j++) 51 | { 52 | times[i, j] = offset.Add(new TimeSpan(i + j * 5, 0, 0)); 53 | } 54 | 55 | try 56 | { 57 | var fileId = Hdf5.CreateFile(filename); 58 | Assert.IsTrue(fileId > 0); 59 | Hdf5.WriteDataset(fileId, "/test", times); 60 | 61 | TimeSpan[,] timesRead = (TimeSpan[,])Hdf5.ReadDataset(fileId, "/test"); 62 | CompareDatasets(times, timesRead); 63 | 64 | Hdf5.CloseFile(fileId); 65 | } 66 | catch (Exception ex) 67 | { 68 | CreateExceptionAssert(ex); 69 | } 70 | } 71 | 72 | [TestMethod] 73 | public void WriteAndReadDataset() 74 | { 75 | string filename = Path.Combine(folder, "testDataset.H5"); 76 | var dset = dsets.First(); 77 | 78 | try 79 | { 80 | var fileId = Hdf5.CreateFile(filename); 81 | Assert.IsTrue(fileId > 0); 82 | Hdf5.WriteDataset(fileId, "/test", dset); 83 | Hdf5.CloseFile(fileId); 84 | } 85 | catch (Exception ex) 86 | { 87 | CreateExceptionAssert(ex); 88 | } 89 | 90 | try 91 | { 92 | var fileId = Hdf5.OpenFile(filename); 93 | Assert.IsTrue(fileId > 0); 94 | double[,] dset2 = (double[,])Hdf5.ReadDataset(fileId, "/test"); 95 | CompareDatasets(dset, dset2); 96 | bool same = dset == dset2; 97 | 98 | Hdf5.CloseFile(fileId); 99 | } 100 | catch (Exception ex) 101 | { 102 | CreateExceptionAssert(ex); 103 | } 104 | } 105 | 106 | [TestMethod] 107 | public void WriteAndReadPrimitives() 108 | { 109 | string filename = Path.Combine(folder, "testPrimitives.H5"); 110 | int intValue = 2; 111 | double dblValue = 1.1; 112 | string strValue = "test"; 113 | bool boolValue = true; 114 | var groupStr = "/test"; 115 | string concatFunc(string x) => string.Concat(groupStr, "/", x); 116 | 117 | try 118 | { 119 | var fileId = Hdf5.CreateFile(filename); 120 | Assert.IsTrue(fileId > 0); 121 | var groupId = Hdf5.CreateGroup(fileId, groupStr); 122 | Hdf5.WriteOneValue(groupId, concatFunc(nameof(intValue)), intValue); 123 | Hdf5.WriteOneValue(groupId, concatFunc(nameof(dblValue)), dblValue); 124 | Hdf5.WriteOneValue(groupId, concatFunc(nameof(strValue)), strValue); 125 | Hdf5.WriteOneValue(groupId, concatFunc(nameof(boolValue)), boolValue); 126 | Hdf5.CloseGroup(groupId); 127 | Hdf5.CloseFile(fileId); 128 | } 129 | catch (Exception ex) 130 | { 131 | CreateExceptionAssert(ex); 132 | } 133 | 134 | try 135 | { 136 | var fileId = Hdf5.OpenFile(filename); 137 | Assert.IsTrue(fileId > 0); 138 | var groupId = H5G.open(fileId, groupStr); 139 | int readInt = Hdf5.ReadOneValue(groupId, concatFunc(nameof(intValue))); 140 | Assert.IsTrue(intValue == readInt); 141 | double readDbl = Hdf5.ReadOneValue(groupId, concatFunc(nameof(dblValue))); 142 | Assert.IsTrue(dblValue == readDbl); 143 | string readStr = Hdf5.ReadOneValue(groupId, concatFunc(nameof(strValue))); 144 | Assert.IsTrue(strValue == readStr); 145 | bool readBool = Hdf5.ReadOneValue(groupId, concatFunc(nameof(boolValue))); 146 | Assert.IsTrue(boolValue == readBool); 147 | H5G.close(groupId); 148 | Hdf5.CloseFile(fileId); 149 | } 150 | catch (Exception ex) 151 | { 152 | CreateExceptionAssert(ex); 153 | } 154 | } 155 | 156 | [TestMethod] 157 | public void WriteAndReadAllPrimitives() 158 | { 159 | 160 | string filename = Path.Combine(folder, "testAllPrimitives.H5"); 161 | //var groupStr = "/test"; 162 | //string concatFunc(string x) => string.Concat(groupStr, "/", x); 163 | try 164 | { 165 | var fileId = Hdf5.CreateFile(filename); 166 | Assert.IsTrue(fileId > 0); 167 | Hdf5.WriteObject(fileId, allTypesObject, "/test"); 168 | 169 | var readObject = Hdf5.ReadObject(fileId, "/test"); 170 | Assert.IsTrue(allTypesObject.PublicInstanceFieldsEqual(readObject)); 171 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 172 | } 173 | catch (Exception ex) 174 | { 175 | CreateExceptionAssert(ex); 176 | } 177 | } 178 | 179 | [TestMethod] 180 | public void WriteAndReadChunckedDataset() 181 | { 182 | string filename = Path.Combine(folder, "testChunks.H5"); 183 | string groupName = "/test"; 184 | string datasetName = "Data"; 185 | 186 | try 187 | { 188 | var fileId = Hdf5.CreateFile(filename); 189 | Assert.IsTrue(fileId > 0); 190 | var groupId = Hdf5.CreateGroup(fileId, groupName); 191 | Assert.IsTrue(groupId >= 0); 192 | //var chunkSize = new ulong[] { 5, 5 }; 193 | using (var chunkedDset = new ChunkedDataset(datasetName, groupId, dsets.First())) 194 | { 195 | foreach (var ds in dsets.Skip(1)) 196 | { 197 | chunkedDset.AppendDataset(ds); 198 | }; 199 | 200 | } 201 | Hdf5.CloseFile(fileId); 202 | } 203 | catch (Exception ex) 204 | { 205 | CreateExceptionAssert(ex); 206 | } 207 | 208 | try 209 | { 210 | var fileId = Hdf5.OpenFile(filename); 211 | //var groupId = H5G.open(fileId, groupName); 212 | //var dset = Hdf5.ReadDatasetToArray(groupId, datasetName); 213 | var dset = Hdf5.ReadDatasetToArray(fileId, string.Concat(groupName,"/",datasetName)); 214 | 215 | Assert.IsTrue(dset.Rank == dsets.First().Rank); 216 | var xSum = dsets.Select(d => d.GetLength(0)).Sum(); 217 | Assert.IsTrue(xSum == dset.GetLength(0)); 218 | var testRange = Enumerable.Range(0, 30).Select(t => (double)t); 219 | 220 | // get every 5th element in the matrix 221 | var x0Range = dset.Cast().Where((d, i) => i % 5 == 0); 222 | Assert.IsTrue(testRange.SequenceEqual(x0Range)); 223 | 224 | Hdf5.CloseFile(fileId); 225 | } 226 | catch (Exception ex) 227 | { 228 | CreateExceptionAssert(ex); 229 | } 230 | } 231 | 232 | [TestMethod] 233 | public void WriteAndReadSubsetOfDataset() 234 | { 235 | string filename = Path.Combine(folder, "testSubset.H5"); 236 | try 237 | { 238 | var fileId = Hdf5.CreateFile(filename); 239 | Assert.IsTrue(fileId > 0); 240 | var chunkSize = new ulong[] { 5, 5 }; 241 | using (var chunkedDset = new ChunkedDataset("/test", fileId, dsets.First())) 242 | { 243 | foreach (var ds in dsets.Skip(1)) 244 | { 245 | chunkedDset.AppendDataset(ds); 246 | }; 247 | 248 | } 249 | 250 | Hdf5.CloseFile(fileId); 251 | } 252 | catch (Exception ex) 253 | { 254 | CreateExceptionAssert(ex); 255 | } 256 | 257 | try 258 | { 259 | var fileId = Hdf5.OpenFile(filename); 260 | ulong begIndex = 8; 261 | ulong endIndex = 21; 262 | var dset = Hdf5.ReadDataset(fileId, "/test", begIndex, endIndex); 263 | Hdf5.CloseFile(fileId); 264 | 265 | 266 | Assert.IsTrue(dset.Rank == dsets.First().Rank); 267 | int count = Convert.ToInt32(endIndex - begIndex + 1); 268 | Assert.IsTrue(count == dset.GetLength(0)); 269 | // Creat a range from number 8 to 21 270 | var testRange = Enumerable.Range((int)begIndex, count).Select(t => (double)t); 271 | 272 | // Get the first column from row index number 8 (the 9th row) to row index number 21 (22th row) 273 | var x0Range = dset.Cast().Where((d, i) => i % 5 == 0); 274 | Assert.IsTrue(testRange.SequenceEqual(x0Range)); 275 | } 276 | catch (Exception ex) 277 | { 278 | CreateExceptionAssert(ex); 279 | } 280 | 281 | } 282 | 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5GroupTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Hdf5DotNetTools; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using HDF.PInvoke; 8 | 9 | namespace Hdf5UnitTests 10 | { 11 | public partial class Hdf5UnitTests 12 | { 13 | [TestMethod] 14 | public void WriteAndReadGroupsWithDataset() 15 | { 16 | string filename = Path.Combine(folder, "testGroups.H5"); 17 | 18 | try 19 | { 20 | var fileId = Hdf5.CreateFile(filename); 21 | Assert.IsTrue(fileId > 0); 22 | var dset = dsets.First(); 23 | 24 | var groupId = H5G.create(fileId, "/A"); ///B/C/D/E/F/G/H 25 | Hdf5.WriteDataset(groupId, "test", dset); 26 | var subGroupId = Hdf5.CreateGroup(groupId, "C"); 27 | var subGroupId2 = Hdf5.CreateGroup(groupId, "/D"); // will be saved at the root location 28 | dset = dsets.Skip(1).First(); 29 | Hdf5.WriteDataset(subGroupId, "test2", dset); 30 | Hdf5.CloseGroup(subGroupId); 31 | Hdf5.CloseGroup(subGroupId2); 32 | Hdf5.CloseGroup(groupId); 33 | groupId = H5G.create(fileId, "/A/B"); ///B/C/D/E/F/G/H 34 | dset = dsets.Skip(1).First(); 35 | Hdf5.WriteDataset(groupId, "test", dset); 36 | Hdf5.CloseGroup(groupId); 37 | 38 | groupId = Hdf5.CreateGroupRecursively(fileId, "A/B/C/D/E/F/I"); 39 | Hdf5.CloseGroup(groupId); 40 | Hdf5.CloseFile(fileId); 41 | 42 | 43 | fileId = Hdf5.OpenFile(filename); 44 | Assert.IsTrue(fileId > 0); 45 | groupId = H5G.open(fileId, "/A/B"); 46 | double[,] dset2 = (double[,])Hdf5.ReadDataset(groupId, "test"); 47 | CompareDatasets(dset, dset2); 48 | Assert.IsTrue(Hdf5.CloseGroup(groupId) >= 0); 49 | groupId = H5G.open(fileId, "/A/C"); 50 | dset2 = (double[,])Hdf5.ReadDataset(groupId, "test2"); 51 | CompareDatasets(dset, dset2); 52 | Assert.IsTrue(Hdf5.CloseGroup(groupId) >= 0); 53 | bool same = dset == dset2; 54 | dset = dsets.First(); 55 | dset2 = (double[,])Hdf5.ReadDataset(fileId, "/A/test"); 56 | CompareDatasets(dset, dset2); 57 | Assert.IsTrue(Hdf5.GroupExists(fileId, "A/B/C/D/E/F/I")); 58 | 59 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 60 | 61 | } 62 | catch (Exception ex) 63 | { 64 | CreateExceptionAssert(ex); 65 | } 66 | } 67 | 68 | [TestMethod] 69 | public void WriteAndReadGroupsInGroup() 70 | { 71 | string filename = Path.Combine(folder, "testGroupWithGroups.H5"); 72 | 73 | try 74 | { 75 | var fileId = Hdf5.CreateFile(filename); 76 | Assert.IsTrue(fileId > 0); 77 | var groupId = H5G.create(fileId, "/A"); 78 | var subGroupId1 = Hdf5.CreateGroup(groupId, "B"); 79 | var subGroupId2 = Hdf5.CreateGroup(groupId, "C"); 80 | Hdf5.CloseGroup(groupId); 81 | Hdf5.CloseGroup(subGroupId1); 82 | Hdf5.CloseGroup(subGroupId2); 83 | Hdf5.CloseFile(fileId); 84 | 85 | fileId = Hdf5.OpenFile(filename); 86 | Assert.IsTrue(fileId > 0); 87 | Assert.IsTrue(Hdf5.GroupExists(fileId, "A/B")); 88 | Assert.IsTrue(Hdf5.GroupExists(fileId, "A/C")); 89 | groupId = H5G.open(fileId, "/A"); 90 | string[] groups = Hdf5.GroupGroups(groupId); 91 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 92 | } 93 | catch (Exception ex) 94 | { 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5ObjectTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Hdf5DotNetTools; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using HDF.PInvoke; 8 | 9 | namespace Hdf5UnitTests 10 | { 11 | public partial class Hdf5UnitTests 12 | { 13 | [TestMethod] 14 | public void WriteAndReadObjectWithPropertiesTest() 15 | { 16 | string filename = Path.Combine(folder, "testObjects.H5"); 17 | try 18 | { 19 | testClass.TestInteger = 2; 20 | testClass.TestDouble = 1.1; 21 | testClass.TestBoolean = true; 22 | testClass.TestString = "test string"; 23 | // 31-Oct-2003, 18:00 is 731885.75 in matlab 24 | testClass.TestTime = new DateTime(2003, 10, 31, 18, 0, 0); 25 | 26 | var fileId = Hdf5.CreateFile(filename); 27 | Assert.IsTrue(fileId > 0); 28 | 29 | Hdf5.WriteObject(fileId, testClass, "objectWithProperties"); 30 | Hdf5.CloseFile(fileId); 31 | } 32 | catch (Exception ex) 33 | { 34 | CreateExceptionAssert(ex); 35 | } 36 | 37 | try 38 | { 39 | var fileId = Hdf5.OpenFile(filename); 40 | Assert.IsTrue(fileId > 0); 41 | 42 | TestClass readObject = new TestClass(); 43 | readObject = Hdf5.ReadObject(fileId, readObject, "objectWithProperties"); 44 | Assert.IsTrue(testClass.Equals(readObject)); 45 | 46 | readObject = Hdf5.ReadObject(fileId, "objectWithProperties"); 47 | Assert.IsTrue(testClass.Equals(readObject)); 48 | 49 | Assert.IsTrue(Hdf5.CloseFile(fileId) >= 0); 50 | } 51 | catch (Exception ex) 52 | { 53 | CreateExceptionAssert(ex); 54 | } 55 | } 56 | 57 | [TestMethod] 58 | public void WriteAndReadObjectWithPropertiesAndArrayPropertyTest() 59 | { 60 | try 61 | { 62 | var testClass = new TestClassWithArray() { 63 | TestInteger = 2, 64 | TestDouble = 1.1, 65 | TestBoolean = true, 66 | TestString = "test string", 67 | TestDoubles = new double[] { 1.1, 1.2, -1.1, -1.2 }, 68 | TestStrings = new string[] { "one", "two", "three", "four" } 69 | }; 70 | testClassWithArrays.TestInteger = 2; 71 | testClassWithArrays.TestDouble = 1.1; 72 | testClassWithArrays.TestBoolean = true; 73 | testClassWithArrays.TestString = "test string"; 74 | testClassWithArrays.TestDoubles = new double[] { 1.1, 1.2, -1.1, -1.2 }; 75 | testClassWithArrays.TestStrings = new string[] { "one", "two", "three", "four" }; 76 | 77 | string filename = Path.Combine(folder, "testArrayObjects.H5"); 78 | 79 | var fileId = Hdf5.CreateFile(filename); 80 | Assert.IsTrue(fileId >= 0); 81 | 82 | Hdf5.WriteObject(fileId, testClassWithArrays, "objectWithTwoArrays"); 83 | 84 | TestClassWithArray readObject = new TestClassWithArray 85 | { 86 | TestStrings = new string[0], 87 | TestDoubles = null, 88 | TestDouble = double.NaN 89 | }; 90 | 91 | readObject = Hdf5.ReadObject(fileId, readObject, "objectWithTwoArrays"); 92 | Assert.IsTrue(testClassWithArrays.Equals(readObject)); 93 | 94 | readObject = Hdf5.ReadObject(fileId, "objectWithTwoArrays"); 95 | Assert.IsTrue(testClassWithArrays.Equals(readObject)); 96 | 97 | Assert.IsTrue(Hdf5.CloseFile(fileId) >= 0); 98 | } 99 | catch (Exception ex) 100 | { 101 | CreateExceptionAssert(ex); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5StringsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Hdf5DotNetTools; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | 8 | namespace Hdf5UnitTests 9 | { 10 | public partial class Hdf5UnitTests 11 | { 12 | 13 | [TestMethod] 14 | public void WriteAndReadOneString() 15 | { 16 | try 17 | { 18 | string[] str = new string[] { "test" }; 19 | 20 | string filename = Path.Combine(folder, "testOneStringList.H5"); 21 | 22 | 23 | // Open file and write the strings 24 | var fileId = Hdf5.CreateFile(filename); 25 | Assert.IsTrue(fileId > 0); 26 | Hdf5.WriteStrings(fileId, "/test", str); 27 | 28 | // Read the strings and close file 29 | Assert.IsTrue(fileId > 0); 30 | IEnumerable strs2 = Hdf5.ReadStrings(fileId, "/test"); 31 | Assert.IsTrue(strs2.Count()==1); 32 | foreach (var s in strs2) 33 | { 34 | Assert.IsTrue(str[0] == s); 35 | }; 36 | 37 | Hdf5.CloseFile(fileId); 38 | } 39 | catch (Exception ex) 40 | { 41 | CreateExceptionAssert(ex); 42 | } 43 | } 44 | 45 | [TestMethod] 46 | public void WriteAndReadListOfStrings() 47 | { 48 | try 49 | { 50 | List strs = new List 51 | { 52 | "t", 53 | "tst", 54 | "test1", 55 | "small test" 56 | }; 57 | 58 | string filename = Path.Combine(folder, "testStringList.H5"); 59 | 60 | 61 | // Open file and write the strings 62 | var fileId = Hdf5.CreateFile(filename); 63 | Assert.IsTrue(fileId > 0); 64 | Hdf5.WriteStrings(fileId, "/test", strs); 65 | 66 | // Read the strings and close file 67 | Assert.IsTrue(fileId > 0); 68 | IEnumerable strs2 = Hdf5.ReadStrings(fileId, "/test"); 69 | Assert.IsTrue(strs.Count() == strs2.Count()); 70 | foreach (var item in strs2.Select((str, i) => new { i, str })) 71 | { 72 | Assert.IsTrue(item.str == strs[item.i]); 73 | } 74 | 75 | Hdf5.CloseFile(fileId); 76 | } 77 | catch (Exception ex) 78 | { 79 | CreateExceptionAssert(ex); 80 | } 81 | } 82 | 83 | [TestMethod] 84 | public void WriteAndReadOneAsciiString() 85 | { 86 | try 87 | { 88 | string test = "This is a test string"; 89 | string filename = Path.Combine(folder, "testOneString.H5"); 90 | 91 | 92 | var fileId = Hdf5.CreateFile(filename); 93 | Hdf5.WriteAsciiString(fileId, "/test", test); 94 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 95 | 96 | fileId = Hdf5.OpenFile(filename); 97 | string readStr = Hdf5.ReadAsciiString(fileId, "/test"); 98 | Assert.IsTrue(test == readStr); 99 | Assert.IsTrue(Hdf5.CloseFile(fileId) == 0); 100 | Hdf5.CloseFile(fileId); 101 | } 102 | catch (Exception ex) 103 | { 104 | CreateExceptionAssert(ex); 105 | } 106 | } 107 | 108 | [TestMethod] 109 | public void WriteAndReadOneUnicodeString() 110 | { 111 | try 112 | { 113 | string test = "Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο"; 114 | string filename = Path.Combine(folder, "testUnicodeString.H5"); 115 | 116 | 117 | var fileId = Hdf5.CreateFile(filename); 118 | Hdf5.WriteUnicodeString(fileId, "/test", test); 119 | Assert.IsTrue(Hdf5.CloseFile(fileId) >= 0); 120 | 121 | 122 | fileId = Hdf5.OpenFile(filename); 123 | string readStr = Hdf5.ReadUnicodeString(fileId, "/test"); 124 | //var readStr = Hdf5.ReadStrings(fileId, "/test"); 125 | Assert.IsTrue(test == readStr); 126 | Assert.IsTrue(Hdf5.CloseFile(fileId) >= 0); 127 | Hdf5.CloseFile(fileId); 128 | } 129 | catch (Exception ex) 130 | { 131 | CreateExceptionAssert(ex); 132 | } 133 | } 134 | 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using Hdf5DotNetTools; 8 | 9 | namespace Hdf5UnitTests 10 | { 11 | [TestClass] 12 | public partial class Hdf5UnitTests 13 | { 14 | [Hdf5Attributes(new string[] { "some info", "more info" })] 15 | class AttributeClass 16 | { 17 | public class NestedInfo 18 | { 19 | public int noAttribute = 10; 20 | 21 | [Hdf5Attribute("some money")] 22 | public decimal money = 100.12M; 23 | } 24 | 25 | [Hdf5Attribute("birthdate")] 26 | public DateTime aDatetime = new DateTime(1969, 12, 01, 12, 00, 00, DateTimeKind.Local); 27 | 28 | public double noAttribute = 10.0; 29 | 30 | public NestedInfo nested = new NestedInfo(); 31 | } 32 | 33 | class AllTypesClass 34 | { 35 | public Boolean aBool = true; 36 | public Byte aByte = 10; 37 | public Char aChar = 'a'; 38 | public DateTime aDatetime = new DateTime(1969, 12, 01, 12, 00, 00, DateTimeKind.Local); 39 | public Decimal aDecimal = new decimal(2.344); 40 | public Double aDouble = 2.1; 41 | public Int16 aInt16 = 10; 42 | public Int32 aInt32 = 100; 43 | public Int64 aInt64 = 1000; 44 | public SByte aSByte = 10; 45 | public Single aSingle = 100; 46 | public UInt16 aUInt16 = 10; 47 | public UInt32 aUInt32 = 100; 48 | public UInt64 aUInt64 = 1000; 49 | public String aString = "test"; 50 | public TimeSpan aTimeSpan = TimeSpan.FromHours(1); 51 | } 52 | 53 | [StructLayout(LayoutKind.Sequential)] 54 | private struct WData 55 | { 56 | public int serial_no; 57 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] 58 | public string location; 59 | public double temperature; 60 | public double pressure; 61 | 62 | public DateTime Time 63 | { 64 | get => new DateTime(timeTicks); 65 | set => timeTicks = value.Ticks; 66 | } 67 | 68 | public long timeTicks; 69 | } 70 | 71 | [StructLayout(LayoutKind.Sequential)] 72 | private struct WData2 73 | { 74 | public int serial_no; 75 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] 76 | public string location; 77 | public double temperature; 78 | public double pressure; 79 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] 80 | public string label; 81 | } 82 | 83 | [StructLayout(LayoutKind.Sequential)] 84 | public struct Responses 85 | { 86 | public Int64 MCID; 87 | public int PanelIdx; 88 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 89 | public short[] ResponseValues; 90 | } 91 | 92 | private class TestClass : IEquatable 93 | { 94 | public int TestInteger { get; set; } 95 | public double TestDouble { get; set; } 96 | public bool TestBoolean { get; set; } 97 | public string TestString { get; set; } 98 | 99 | public DateTime TestTime { get; set; } 100 | 101 | public bool Equals(TestClass other) 102 | { 103 | return other.TestInteger == TestInteger && 104 | other.TestDouble == TestDouble && 105 | other.TestBoolean == TestBoolean && 106 | other.TestString == TestString && 107 | other.TestTime == TestTime; 108 | } 109 | } 110 | private class TestClassWithArray : TestClass 111 | { 112 | public double[] TestDoubles { get; set; } 113 | public string[] TestStrings { get; set; } 114 | 115 | public bool Equals(TestClassWithArray other) 116 | { 117 | return base.Equals(other) && 118 | other.TestDoubles.SequenceEqual(TestDoubles) && 119 | other.TestStrings.SequenceEqual(TestStrings); 120 | 121 | } 122 | } 123 | 124 | class TestClassWithStructs 125 | { 126 | public TestClassWithStructs() 127 | { 128 | } 129 | public WData[] DataList { get; set; } 130 | } 131 | 132 | static private TestClass testClass; 133 | static private TestClassWithArray testClassWithArrays; 134 | static private List dsets; 135 | static private WData[] wDataList; 136 | static private WData2[] wData2List; 137 | static private Responses[] responseList; 138 | static private AllTypesClass allTypesObject; 139 | static private TestClassWithStructs classWithStructs; 140 | 141 | static private string folder; 142 | 143 | [ClassInitialize()] 144 | public static void ClassInitialize(TestContext context) 145 | { 146 | //folder = System.IO.Path.GetTempPath(); 147 | folder = AppDomain.CurrentDomain.BaseDirectory; 148 | dsets = new List { 149 | CreateDataset(), 150 | CreateDataset(10), 151 | CreateDataset(20) }; 152 | 153 | wDataList = new WData[4] { 154 | new WData() { serial_no = 1153, location = "Exterior (static)", temperature = 53.23, pressure = 24.57, Time=new DateTime(2000,1,1) }, 155 | new WData() { serial_no = 1184, location = "Intake", temperature = 55.12, pressure = 22.95, Time=new DateTime(2000,1,2) }, 156 | new WData() { serial_no = 1027, location = "Intake manifold", temperature = 103.55, pressure = 31.23, Time=new DateTime(2000,1,3) }, 157 | new WData() { serial_no = 1313, location = "Exhaust manifold", temperature = 1252.89, pressure = 84.11, Time=new DateTime(2000,1,4) } 158 | }; 159 | 160 | wData2List = new WData2[4] { 161 | new WData2() { serial_no = 1153, location = "Exterior (static)", label="V",temperature = 53.23, pressure = 24.57 }, 162 | new WData2() { serial_no = 1184, location = "Intake", label="uV", temperature = 55.12, pressure = 22.95 }, 163 | new WData2() { serial_no = 1027, location = "Intake manifold", label="V",temperature = 103.55, pressure = 31.23 }, 164 | new WData2() { serial_no = 1313, location = "Exhaust manifold", label="mV", temperature = 1252.89, pressure = 84.11 } 165 | }; 166 | responseList = new Responses[4] { 167 | new Responses() { MCID=1,PanelIdx=5,ResponseValues=new short[4]{ 1,2,3,4} }, 168 | new Responses() { MCID=2,PanelIdx=6,ResponseValues=new short[4]{ 5,6,7,8}}, 169 | new Responses() { MCID=3,PanelIdx=7,ResponseValues=new short[4]{ 1,2,3,4}}, 170 | new Responses() { MCID=4,PanelIdx=8,ResponseValues=new short[4]{ 5,6,7,8}} 171 | }; 172 | 173 | classWithStructs = new TestClassWithStructs { DataList = wDataList }; 174 | testClass = new TestClass(); 175 | testClassWithArrays = new TestClassWithArray(); 176 | allTypesObject = new AllTypesClass(); 177 | 178 | var files = Directory.GetFiles(folder, "*.H5"); 179 | foreach (var file in files) 180 | { 181 | try 182 | { 183 | File.Delete(file); 184 | } 185 | catch (Exception) 186 | { 187 | 188 | throw; 189 | } 190 | 191 | } 192 | } 193 | 194 | /// 195 | /// create a matrix and fill it with numbers 196 | /// 197 | /// 198 | /// the matrix 199 | private static double[,] CreateDataset(int offset = 0) 200 | { 201 | var dset = new double[10, 5]; 202 | for (var i = 0; i < 10; i++) 203 | for (var j = 0; j < 5; j++) 204 | { 205 | double x = i + j * 5 + offset; 206 | dset[i, j] = (j == 0) ? x : x / 10; 207 | } 208 | return dset; 209 | } 210 | 211 | 212 | private static void CompareDatasets(T[,] dset, T[,] dset2) 213 | { 214 | Assert.IsTrue(dset.Rank == dset2.Rank); 215 | Assert.IsTrue( 216 | Enumerable.Range(0, dset.Rank).All(dimension => 217 | dset.GetLength(dimension) == dset2.GetLength(dimension))); 218 | Assert.IsTrue(dset.Cast().SequenceEqual(dset2.Cast())); 219 | } 220 | 221 | private void CreateExceptionAssert(Exception ex) 222 | { 223 | Console.WriteLine(ex.ToString()); 224 | var failStr = "Unexpected exception of type {0} caught: {1}"; 225 | failStr = string.Format(failStr, ex.GetType(), ex.Message); 226 | Assert.Fail(failStr); 227 | 228 | } 229 | } 230 | 231 | } -------------------------------------------------------------------------------- /Hdf5Tests/Hdf5UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {8F907AB2-9A4F-4B79-A60C-35E3367C1CA7} 7 | Library 8 | Properties 9 | Hdf5UnitTests 10 | Hdf5UnitTests 11 | v4.8 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | TRACE;DEBUG;HDF5_VER1_10 29 | prompt 30 | 4 31 | AnyCPU 32 | true 33 | 34 | 35 | pdbonly 36 | true 37 | bin\Release\ 38 | TRACE 39 | prompt 40 | 4 41 | 42 | 43 | true 44 | bin\x64\Debug\ 45 | DEBUG;TRACE 46 | true 47 | full 48 | x64 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | 52 | 53 | bin\x64\Release\ 54 | TRACE 55 | true 56 | pdbonly 57 | x64 58 | prompt 59 | MinimumRecommendedRules.ruleset 60 | 61 | 62 | false 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | False 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | {7e7b1235-2d9c-4696-9ffb-cf7e14c7202a} 98 | Hdf5DotnetTools 99 | 100 | 101 | 102 | 103 | 1.10.6.1 104 | 105 | 106 | 1.10.502 107 | 108 | 109 | 110 | 111 | 112 | 113 | False 114 | 115 | 116 | False 117 | 118 | 119 | False 120 | 121 | 122 | False 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | rem if not exist "$(TargetDir)bin32" md "$(TargetDir)bin32" 131 | rem xcopy /s /y "$(SolutionDir)packages\HDF.PInvoke.1.10.5.1\build\bin32\*.*" "$(TargetDir)bin32" 132 | rem if not exist "$(TargetDir)bin64" md "$(TargetDir)bin64" 133 | rem xcopy /s /y "$(SolutionDir)packages\HDF.PInvoke.1.10.5.1\build\bin64\*.*" "$(TargetDir)bin64" 134 | 135 | 136 | 137 | 138 | 139 | 146 | -------------------------------------------------------------------------------- /Hdf5Tests/KamaTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Hdf5DotNetTools; 8 | using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | 10 | namespace Hdf5UnitTests 11 | { 12 | public class KamaTests 13 | { 14 | private string folder = AppDomain.CurrentDomain.BaseDirectory; 15 | [TestMethod] 16 | public void CreateFile() 17 | { 18 | //string filename = Path.Combine(folder, "Kama.H5"); 19 | //try 20 | //{ 21 | // using (var writer = new Hdf5AcquisitionFileWriter(filename)) 22 | // { 23 | // var header = null; 24 | // var signals = new List(header.Recording.NrOfChannels); 25 | // for (int i = 0; i < header.Recording.NrOfChannels; i++) 26 | // { 27 | // signals.Add(Enumerable.Range(i * 50, 50).Select(j => j / 50.0).ToArray()); 28 | // } 29 | 30 | // writer.Write(signals); 31 | // signals = new List(header.Recording.NrOfChannels); 32 | // for (int i = 0; i < header.Recording.NrOfChannels; i++) 33 | // { 34 | // signals.Add(Enumerable.Range((i + 1) * 50, 50).Select(j => j / 50.0).ToArray()); 35 | // } 36 | // writer.Write(signals); 37 | // //header.Recording.NrOfSamples = 100; 38 | // //for (int i = 0; i < header.Channels.Length; i++) 39 | // //{ 40 | // // header.Channels[i].NrOfSamples = header.Recording.NrOfSamples; 41 | // //} 42 | // } 43 | //} 44 | //catch (Exception ex) 45 | //{ 46 | // //CreateExceptionAssert(ex); 47 | //} 48 | 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Hdf5Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Hdf5Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("LUMC")] 12 | [assembly: AssemblyProduct("Hdf5Test")] 13 | [assembly: AssemblyCopyright("Copyright © LUMC 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8f907ab2-9a4f-4b79-a60c-35e3367c1ca7")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Hdf5Tests/TestUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Hdf5UnitTests 9 | { 10 | public static class UtilsExtensions 11 | { 12 | /// 13 | /// Compares each property of an object with the fields of another object to see if they are the same 14 | /// 15 | /// generic class type 16 | /// The object to compare 17 | /// The second object 18 | /// names of fields to ignore 19 | /// 20 | public static bool PublicInstancePropertiesEqual(this T self, T to, params string[] ignore) where T : class 21 | { 22 | var equal = false; 23 | if (self != null && to != null) 24 | { 25 | var type = typeof(T); 26 | var ignoreList = new List(ignore); 27 | var unequalProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance). 28 | Where(pi => !ignoreList.Contains(pi.Name)); 29 | foreach (var pi in unequalProperties) 30 | { 31 | var selfValue = type.GetField(pi.Name).GetValue(self); 32 | var toValue = type.GetField(pi.Name).GetValue(to); 33 | Type selfType = selfValue.GetType(); 34 | TypeCode code = Type.GetTypeCode(selfType); 35 | if (code == TypeCode.DateTime) 36 | if (DateTime.Compare((DateTime)selfValue, (DateTime)toValue) != 0) 37 | return false; 38 | if (selfType == typeof(TimeSpan)) 39 | if (TimeSpan.Compare((TimeSpan)selfValue, (TimeSpan)toValue) != 0) 40 | return false; 41 | 42 | if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) 43 | { 44 | return false; 45 | } 46 | } 47 | equal = true; 48 | } 49 | return equal; 50 | } 51 | 52 | /// 53 | /// Compares each field of an object with the fields of another object to see if they are the same 54 | /// 55 | /// generic class type 56 | /// The object to compare 57 | /// The second object 58 | /// names of fields to ignore 59 | /// 60 | public static bool PublicInstanceFieldsEqual(this T self, T to, params string[] ignore) where T : class 61 | { 62 | var equal = false; 63 | if (self != null && to != null) 64 | { 65 | var type = typeof(T); 66 | var ignoreList = new List(ignore); 67 | var unequalProperties = type.GetFields(BindingFlags.Public | BindingFlags.Instance). 68 | Where(pi => !ignoreList.Contains(pi.Name)); 69 | foreach (var pi in unequalProperties) 70 | { 71 | var selfValue = type.GetField(pi.Name).GetValue(self); 72 | var toValue = type.GetField(pi.Name).GetValue(to); 73 | Type selfType = selfValue.GetType(); 74 | TypeCode code = Type.GetTypeCode(selfType); 75 | if (code == TypeCode.DateTime) 76 | if (DateTime.Compare((DateTime)selfValue, (DateTime)toValue) != 0) 77 | return false; 78 | if (selfType == typeof(TimeSpan)) 79 | if (TimeSpan.Compare((TimeSpan)selfValue, (TimeSpan)toValue) != 0) 80 | return false; 81 | 82 | if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))) 83 | { 84 | return false; 85 | } 86 | } 87 | equal = true; 88 | } 89 | return equal; 90 | } 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hdf5DotnetTools 2 | Set of tools that help in reading and writing hdf5 files for .net environments 3 | 4 | ## Introduction 5 | At the neurology department of the Leiden University Medical Centre in the Netherlands we need to convert large medical data files to a format that could easily be used in programs like Matlab, R and Python. 6 | 7 | ## Usage 8 | 9 | ### write an object to an HDF5 file 10 | In the example below an object is created with some arrays and other variables 11 | The object is written to a file and than read back in a new object. 12 | 13 | private class TestClassWithArray 14 | { 15 | public double[] TestDoubles { get; set; } 16 | public string[] TestStrings { get; set; } 17 | public int TestInteger { get; set; } 18 | public double TestDouble { get; set; } 19 | public bool TestBoolean { get; set; } 20 | public string TestString { get; set; } 21 | } 22 | var testClass = new TestClassWithArray() { 23 | TestInteger = 2, 24 | TestDouble = 1.1, 25 | TestBoolean = true, 26 | TestString = "test string", 27 | TestDoubles = new double[] { 1.1, 1.2, -1.1, -1.2 }, 28 | TestStrings = new string[] { "one", "two", "three", "four" } 29 | }; 30 | int fileId = Hdf5.CreateFile("testFile.H5"); 31 | 32 | Hdf5.WriteObject(fileId, testClass, "testObject"); 33 | 34 | TestClassWithArray readObject = new TestClassWithArray(); 35 | 36 | readObject = Hdf5.ReadObject(fileId, readObject, "testObject"); 37 | 38 | Hdf5.CloseFile(fileId); 39 | 40 | ## Write a dataset and append new data to it 41 | 42 | /// 43 | /// create a matrix and fill it with numbers 44 | /// 45 | /// 46 | /// the matrix 47 | private static double[,]createDataset(int offset = 0) 48 | { 49 | var dset = new double[10, 5]; 50 | for (var i = 0; i < 10; i++) 51 | for (var j = 0; j < 5; j++) 52 | { 53 | double x = i + j * 5 + offset; 54 | dset[i, j] = (j == 0) ? x : x / 10; 55 | } 56 | return dset; 57 | } 58 | 59 | // create a list of matrices 60 | dsets = new List { 61 | createDataset(), 62 | createDataset(10), 63 | createDataset(20) }; 64 | 65 | string filename = Path.Combine(folder, "testChunks.H5"); 66 | int fileId = Hdf5.CreateFile(filename); 67 | 68 | // create a dataset and append two more datasets to it 69 | using (var chunkedDset = new ChunkedDataset("/test", fileId, dsets.First())) 70 | { 71 | foreach (var ds in dsets.Skip(1)) 72 | chunkedDset.AppendDataset(ds); 73 | } 74 | 75 | // read rows 9 to 22 of the dataset 76 | ulong begIndex = 8; 77 | ulong endIndex = 21; 78 | var dset = Hdf5.ReadDataset(fileId, "/test", begIndex, endIndex); 79 | Hdf5.CloseFile(fileId); 80 | 81 | ## ToDo 82 | This is still an early version of the library. If there are any problems please let me know 83 | 84 | ## Other projects that use the HDF.Pinvoke library 85 | Another project on github that uses the [HDF.Pinvoke](https://github.com/HDFGroup/HDF.PInvoke) library to read and write HDF5 files is the [sharpHDF](https://github.com/sharpHDF/sharpHDF) project. I discovered it while I was working on my own library. It has a different approah to writing and reading hdf5 files. You have to create a Hdf5File object and fill it with groups, attributes and datasets. When you close the Hdf5File object it writes the file. 86 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `<2016>` `` 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /misc/HDF.Pinvoke.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reyntjesr/Hdf5DotnetTools/d14730d239c167dcd639c02ff3a6bad7dc9d8c34/misc/HDF.Pinvoke.snk --------------------------------------------------------------------------------