7 votes

Comment convertir un tableau d'octets C# en données structurées ?

Je fais passer des paquets de données de 64 octets par USB à un microcontrôleur. Dans le code C du microcontrôleur, les paquets ont la structure suivante,

typedef union
{
    unsigned char data[CMD_SIZE];
    cmd_get_t get;
    // plus more union options
} cmd_t;

avec

typedef struct
{
    unsigned char cmd;          //!< Command ID
    unsigned char id;           //!< Packet ID
    unsigned char get_id;       //!< Get identifier
    unsigned char rfu[3];       //!< Reserved for future use
    union
    {
        unsigned char data[58];     //!< Generic data
        cmd_get_adc_t adc;          //!< ADC data
        // plus more union options
    } data;                     //!< Response data
} cmd_get_t;

y

typedef struct
{
    int16_t supply;
    int16_t current[4];
} cmd_get_adc_t;

Du côté du PC, en C#, on m'a fourni une fonction qui renvoie le paquet de 64 octets sous la forme d'un Byte[]. La fonction utilise Marshal.Copy pour copier les données reçues dans le tableau Byte[]. J'ai ensuite utilisé une structure C# de la forme

[StructLayout(LayoutKind.Sequential, Pack=1)]
    public struct COMMAND_GET_ADC
    {
        public byte CommandID;
        public byte PacketID;
        public byte GetID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
            public byte[] RFU;
        public short Supply;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
            public short[] Current;
    }

et j'ai de nouveau utilisé Marshal.Copy pour copier le tableau d'octets dans le struct afin de pouvoir travailler avec des données structurées, par exemple

COMMAND_GET_ADC cmd = (COMMAND_GET_ADC)RawDeserialize(INBuffer, 1, typeof(COMMAND_GET_ADC));
short supply = cmd.Supply;

avec

public static object RawDeserialize(Byte[] rawData, int position, Type anyType)
{
    int rawsize = Marshal.SizeOf(anyType);
    if(rawsize > rawData.Length)
    {
        return null;
    }
    IntPtr buffer = Marshal.AllocHGlobal(rawsize);
    Marshal.Copy(rawData, position, buffer, rawsize);
    object retobj = Marshal.PtrToStructure(buffer, anyType);
    Marshal.FreeHGlobal(buffer);
    return retobj;
}

J'ai l'impression que je fais beaucoup de copies des données et que ce n'est peut-être pas le moyen le plus productif d'obtenir ce que je veux. Je dois également reconvertir les données structurées en un tableau d'octets pour les commandes au dispositif. J'ai une méthode qui utilise le même processus (c'est-à-dire utiliser une structure, puis la sérialiser en un tableau d'octets et passer le tableau d'octets à la fonction d'écriture).

Existe-t-il de meilleures alternatives ?

2voto

logicnp Points 4509

Si vous pouvez utiliser du code non sécurisé, vous pouvez convertir le tableau d'octets en un pointeur vers votre structure en utilisant le mot-clé "fixed".

2voto

Mihailo Points 635

Si vous appelez vous-même une dll native, vous pouvez définir vos DllImport-s de manière à ce qu'elles renvoient et acceptent COMMAND_GET_ADC directement - à condition que les structures soient représentées correctement. Le framework lui-même devrait s'en charger.

Si vous devez utiliser un tableau d'octets imposé par l'utilisation des méthodes qui vous sont fournies - alors je ne sais pas, je n'ai jamais eu une telle contrainte. J'ai toujours essayé de représenter mes données d'interopérabilité de la même manière que dans les dlls natives et je ne me souviens pas avoir eu de problèmes majeurs à ce sujet.

EDIT :

[StructLayout(LayoutKind.Explicit)]
public struct COMMAND_GET
{
    [FieldOffset(0)]
    public byte CommandID;
    [FieldOffset(1)]
    public byte PacketID;
    [FieldOffset(2)]
    public byte GetID;
    [FieldOffset(3)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
    public byte[] RFU;
    [FieldOffset(6)]
    public ADC_Data Adc_data;
    [FieldOffset(6)]
    public SomeOther_Data other_data;
    [FieldOffset(6)]
    ....
}

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct ADC_Data
{
    public short Supply;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
    public short[] Current;
}

Fondamentalement, lorsque vous avez FieldOffset(6), vous créez une union comme l'union de données dans cmd_get_t

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X