Как создать составной тип данных с HDF5DOTNET?

У меня возникают проблемы, когда я пишу структуру с массивами в ней в набор данных HDF5. Во-первых, форма окна не начинается со строки:

H5T.insert(typeStruct, "string", 0, H5T.create_array(new H5DataTypeId(H5T.H5Type.C_S1), dims2));

Форма окна, по крайней мере, начинается без строки, поэтому я думаю, что что-то не так с определением составного типа данных. Я просмотрел руководства и множество примеров, но все еще не могу решить проблемы. Могу ли я получить пример использования составных типов данных для написания структуры с несколькими массивами на C#?

using HDF5DotNet;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;


namespace WindowsFormsApplication1
{
    public unsafe partial class Form1 : Form
    {

        public unsafe struct struct_TR
        {
            public string[] arr_currentLong;

            public struct_TR(byte size_currentTime)
            {
                arr_currentLong = new string[size_currentTime];
            }
        }


        public Form1()
        {
            InitializeComponent();

            long ARRAY_SIZE = 255;
            struct_TR structMade = new struct_TR(255);

            for (int i = 0; i < 255; i++)
            {
                structMade.arr_currentLong[i] = i.ToString();
            }

            string currentPath = Path.GetDirectoryName(Application.ExecutablePath);     
            Directory.SetCurrentDirectory(currentPath);                                 


            H5FileId fileId = H5F.create(@"weights.h5", H5F.CreateMode.ACC_TRUNC);

            long[] dims1 = { 1 };
            long[] dims2 = { 1, ARRAY_SIZE };

            H5DataSpaceId myDataSpace = H5S.create_simple(1, dims1);

            H5DataTypeId string_type = H5T.copy(H5T.H5Type.C_S1);
            H5DataTypeId array_tid1 = H5T.create_array(string_type, dims2);

            H5DataTypeId typeStruct = H5T.create(H5T.CreateClass.COMPOUND, Marshal.SizeOf(typeof(struct_TR)));
            H5T.insert(typeStruct, "string", 0, H5T.create_array(new H5DataTypeId(H5T.H5Type.C_S1), dims2));

            H5DataSetId myDataSet = H5D.create(fileId, "/dset", typeStruct, myDataSpace);

            H5D.writeScalar<struct_TR>(myDataSet, typeStruct, ref structMade);

        }
    }
}

1 ответ

Решение

Единственный способ, которым я знаю, как сохранять структуры с массивами, - это создать массив, который является постоянным. Так, например, это структура с массивом длины 4.

    [StructLayout(LayoutKind.Sequential)]
    public struct Responses
    {
        public Int64 MCID;
        public int PanelIdx;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public short[] ResponseValues;
    }

Здесь создается массив из 4 структур, содержащих массив:

       responseList = new Responses[4] {
            new Responses() { MCID=1,PanelIdx=5,ResponseValues=new short[4]{ 1,2,3,4} },
            new Responses() { MCID=2,PanelIdx=6,ResponseValues=new short[4]{ 5,6,7,8}},
            new Responses() { MCID=3,PanelIdx=7,ResponseValues=new short[4]{ 1,2,3,4}},
            new Responses() { MCID=4,PanelIdx=8,ResponseValues=new short[4]{ 5,6,7,8}}
        };

Следующие строки кода записывают массив структур в файл HDF5:

        string filename = "testArrayCompounds.H5";
        var fileId =H5F.create(filename, H5F.ACC_TRUNC);
        var status = WriteCompounds(fileId, "/test", responseList);
        H5F.close(fileId);

Метод WriteCompounds выглядит так:

    public static int WriteCompounds<T>(hid_t groupId, string name, IEnumerable<T> list) //where T : struct
    {
        Type type = typeof(T);
        var size = Marshal.SizeOf(type);
        var cnt = list.Count();

        var typeId = CreateType(type);

        var log10 = (int)Math.Log10(cnt);
        ulong pow = (ulong)Math.Pow(10, log10);
        ulong c_s = Math.Min(1000, pow);
        ulong[] chunk_size = new ulong[] { c_s };

        ulong[] dims = new ulong[] { (ulong)cnt };

        long dcpl = 0;
        if (!list.Any() || log10 == 0) { }
        else
        {
            dcpl = CreateProperty(chunk_size);
        }

        // Create dataspace.  Setting maximum size to NULL sets the maximum
        // size to be the current size.
        var spaceId = H5S.create_simple(dims.Length, dims, null);

        // Create the dataset and write the compound data to it.
        var datasetId = H5D.create(groupId, name, typeId, spaceId, H5P.DEFAULT, dcpl);

        IntPtr p = Marshal.AllocHGlobal(size * (int)dims[0]);

        var ms = new MemoryStream();
        BinaryWriter writer = new BinaryWriter(ms);
        foreach (var strct in list)
            writer.Write(getBytes(strct));
        var bytes = ms.ToArray();

        GCHandle hnd = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        var statusId = H5D.write(datasetId, typeId, spaceId, H5S.ALL,
            H5P.DEFAULT, hnd.AddrOfPinnedObject());

        hnd.Free();
        /*
         * Close and release resources.
         */
        H5D.close(datasetId);
        H5S.close(spaceId);
        H5T.close(typeId);
        H5P.close(dcpl);
        Marshal.FreeHGlobal(p);
        return statusId;
    }

Требуются три дополнительные справочные функции, две из которых показаны здесь:

    private static long CreateType(Type t)
    {
        var size = Marshal.SizeOf(t);
        var float_size = Marshal.SizeOf(typeof(float));
        var int_size = Marshal.SizeOf(typeof(int));
        var typeId = H5T.create(H5T.class_t.COMPOUND, new IntPtr(size));

        var compoundInfo = Hdf5.GetCompoundInfo(t);
        foreach (var cmp in compoundInfo)
        {
            H5T.insert(typeId, cmp.name, Marshal.OffsetOf(t, cmp.name), cmp.datatype);
        }
        return typeId;
    }

    private static long CreateProperty(ulong[] chunk_size)
    {
        var dcpl = H5P.create(H5P.DATASET_CREATE);
        H5P.set_layout(dcpl, H5D.layout_t.CHUNKED);
        H5P.set_chunk(dcpl, chunk_size.Length, chunk_size);
        H5P.set_deflate(dcpl, 6);
        return dcpl;
    }

У меня также есть ReadCompounds для чтения файла hdf5. Метод Hdf5.GetCompoundInfo, используемый в методе CreateType, также очень длинный. Поэтому я не буду показывать здесь эти методы.

Так что довольно много кода только для написания некоторых структур. Я создал библиотеку под названием HDF5DotnetTools, которая позволяет вам намного легче читать и писать классы и структуры. Там вы также можете найти методы ReadCompounds и GetCompoundInfo.

В модульных тестах HDF5DotnetTools вы также можете найти примеры того, как писать классы с массивами.

Другие вопросы по тегам