Побайтная запись данных в структуру С#

205
21 марта 2018, 02:32

Есть структура данных (С#) необходимо принимая данные из SerialPort записать их в структуру побайтно.

Код объявления структуры (VS2010) Структура упакована побайтно, размер соответствует.

:

// ------------------ telemetry data structures

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BMS //  BMS telemetry data
    {
        public ushort voltage;
        public short current;
        public sbyte temperature;
        public byte status;
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct MOTOR //  Traction MOTOR telemetry data
    {
        sbyte acceleration;
        short current;
        short speed;
        sbyte motor_temp;
        sbyte controller_temp;
        byte status;
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct GENERATOR //  Gasoline GENERATOR telemetry data
    {
        byte fuel_level;
        short speed; 
        short current;
        ushort voltage;
        sbyte motor_temp;
        sbyte generator_temp;
        sbyte controller_temp;
        byte status;
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct CABLE_REEL //  CABLE REEL telemetry data
    {
        sbyte force;
        short speed;
        sbyte motor_temp;
        sbyte controller_temp;
        byte status;
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct UTP_TELEMETRY // UTP-1 telemetry packet struct
    {
        public BMS bms1;
        public BMS bms2;
        public MOTOR left_motor;
        public MOTOR right_motor;
        public GENERATOR generator;
        public CABLE_REEL reel;
    }
    //UTP_TELEMETRY packet;
    int packet_data_len = Marshal.SizeOf(typeof(UTP_TELEMETRY));  // number of bytes for data (45 bytes at the moment)
    // defines exactly as union in C++
    [StructLayout(LayoutKind.Explicit, Size=45)]
    public struct UNION_BUFF
    {
        [FieldOffset(0)]
        public byte buff;
        //public byte[] buff;
        [FieldOffset(0)]
        public UTP_TELEMETRY data_packet;
    }
   public UNION_BUFF data_buffer;

проект unsafe, разные способы испытал, но не знаю тонкостей работы с указателями С#, ничего не получается. пробовал запись по указателю, по индексу buff[index], еще несколько разных идей и все никак. Как необходимо организовать запись побайтно в данную структуру?

Answer 1

Ну например, для структур, состоящих только из примитивных полей, подойдёт такой хелпер:

static void FillFromStream<T>(ref T t, Stream stream) where T : struct
{
    object boxed = t;
    using (var br = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
    {
        var fields = typeof(T).GetFields().OrderBy(f => f.MetadataToken);
        foreach (var field in fields)
        {
            if (!field.FieldType.IsPrimitive)
                throw new NotImplementedException("nested compound types");
            // for primitive types:
            if (!primitiveExtractors.TryGetValue(field.FieldType, out var extractor))
                throw new NotImplementedException("unsupported primitive type");
            var value = extractor(br);
            field.SetValue(boxed, value);
        }
    }
    t = (T)boxed;
}

Вспомогательная таблица:

static Dictionary<Type, Func<BinaryReader, object>> primitiveExtractors =
    new Dictionary<Type, Func<BinaryReader, object>>()
    {
        [typeof(sbyte)]  = br => br.ReadSByte(),
        [typeof(byte)]   = br => br.ReadByte(),
        [typeof(short)]  = br => br.ReadInt16(),
        [typeof(ushort)] = br => br.ReadUInt16(),
        [typeof(int)]    = br => br.ReadInt32(),
        [typeof(uint)]   = br => br.ReadUInt32(),
        [typeof(long)]   = br => br.ReadInt64(),
        [typeof(ulong)]  = br => br.ReadUInt64(),
        [typeof(float)]  = br => br.ReadSingle(),
        [typeof(double)] = br => br.ReadDouble()
    };

Как вы видите, без unsafe и прочих небезопасных методов можно обойтись.

Не забудьте открыть доступ у полям.

.OrderBy(f => f.MetadataToken) нужно для того, чтобы порядок полей соответствовал текстуальному порядку.

Answer 2

Например, используя Marshal.PtrToStructure:

object ByteArrayToStructure(byte[] bytearray, Type t)
{
    if (t.IsValueType == false) throw new ArgumentException("Type is not structure");
    IntPtr p = IntPtr.Zero;
    object obj = null;
    try
    {
        int len = Marshal.SizeOf(t);
        p = Marshal.AllocHGlobal(len);
        Marshal.Copy(bytearray, 0, p, len);
        obj = Marshal.PtrToStructure(p, t);
    }
    finally
    {
        if (p != IntPtr.Zero) Marshal.FreeHGlobal(p);
    }
    return obj;
}
/*...*/
byte[] arr = /*Считать массив байт из потока*/;
MyStruct s = (MyStruct)ByteArrayToStructure(arr,typeof(MyStruct));

Или как-то так (извращенный способ, но не требует выделения промежуточного блока памяти):

using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyStruct
{
    /*Список полей*/
    short x;
    short y;
    sbyte a;
    byte b;
    /*Статические методы*/
    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    private static extern void CopyMemory(
        [In, Out] ref MyStruct dest,
        [MarshalAs(UnmanagedType.LPArray)] byte[] src,
        int count);
    public static MyStruct ByteArrayToStructure(byte[] bytearray)
    {
        MyStruct obj = new MyStruct();
        CopyMemory(ref obj, bytearray, bytearray.Length);
        return obj;
    }
}
/*...*/
byte[] arr = /*Считать массив байт из потока*/;
MyStruct s = MyStruct.ByteArrayToStructure(arr);
READ ALSO
Компилятор не видит класс внутри класса

Компилятор не видит класс внутри класса

Есть 2 класса в 2-х файлах:

203
Полосатый textBox

Полосатый textBox

Использую RichTextBoxНужно, что бы каждая новая строка была своего цвета, например:

182
Получить данные из SQL в код c#

Получить данные из SQL в код c#

Я новичек в програмировании, не до конца понимаю методы классы и обьектыМне нужно: Подключитесь к локальному SQL-серверу с помощью C # и загрузите...

207
БД не отображается в обозревателе SQL Server

БД не отображается в обозревателе SQL Server

Программа работает, сохраняет в БД и достает данные из, в вкладке Обозреватель SQL Server не отображаетсяКак и где можно ее найти?

202