Main Site Documentation

Converting a struct to byte array


#1

Here is something that should be brain dead simple that I am pulling my hair out trying to figure out how to do…

I have a structure that is defined like this.


        struct DataFrame
        {
            byte origin;
            byte num;
            UInt16 ver;
            UInt32 type;
            float field0;
            float field1;
            float field2;
            float field3;
            UInt32 crc;
        }

Now, I would like to feed this to a function that transmits this over the wire and the function is declared like this.


       public void SendData ( byte [] datablock, int size) {


}


Well, how do I actually turn this structure into byte [] so that I can pass it to the function? I know how to do this day in and day out in C / C++.

Thanks in advance.

Hiroo


#2

This is fairly simple struct. You can allocate a byte array, since you know the size and serialize each field manually. Have another method to deserialize.

Edit.

Check this link too:

https://www.ghielectronics.com/community/forum/topic?id=4647


#3

Thank you for a quick response. I started reading the thread and look up some related samples and documentations. It seems that the process is fairly convoluted and will take a fair amount of computational cycles.

The structure in my post is fairly simplified for example purposes. In the real application, each structure has 16 data fields of 32bit each and there are up to 8 of the structures packed into a data frame in an array. We need to do this 15 to 20 times a second so efficiency is pretty important.

I am now thinking that I might have to do this in RLP since I can do a simple native C structure casting and memory copying operation.

A bit like this… (not an exact code but you get the idea…)



        struct DataFrame
        {
            public byte origin;
            public byte num;
            public UInt16 ver;
            public UInt32 type;
            public float field_0;
//   more datafields...
            public float field_12;
            public UInt32 crc;
        }

        DataFrame[] dataframes;
        int32    ptr_frame;
        byte[]   databuffer;

        databuffer = new byte[4096]
        dataframes = new DataFrame[8];
        ptr_frame = &dataframes;

       RLP_fillbuffer(databuffer, ptr_frame, 4096);

Well, except, as you might have already suspected, it pukes at the second from last step where I am trying to get a memory address to the structure array. Since this is a transmit only application, I do not need to be able to go back the other way.


#4

Two steps forward and one step back…

I coded up a quick RLP code to handle the actual copying of the buffer…


int RLP_CopyBuffer(void* par0, void **args, unsigned int argc, unsigned char *argsz)
{
	unsigned char *src;
	int length;
	unsigned char * ByteArray;

	src = (unsigned char *)args[1];
	length = *(int *)args[2];

	ByteArray = (unsigned char *)args[0];
	memcpy(ByteArray, src, length);
	
	return length;
} 

To call it, I do this in the managed side of the code…


            unsafe
            {
                fixed (void * frameptr = &dataframes[0])
                {
                    int ptr = (int)frameptr;  // Exception happens here
                    ret = RLP_CopyBuffer.Invoke(buffer, ptr, (int)framesize);
                }
            }


Almost seems good. Except, when the code executes the pointer casting line, it throws an exception CLR_E_WRONG_TYPE (1) .

I am still scratching my head…


#5

@ humeno - Here is a link that may help:

https://www.ghielectronics.com/community/forum/jump/3098/29891

My knowledge of C is a little rusty, but couldn’t you do something like this:

On native side:


int rlpFillBuffer(void *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
     struct dataFrame
     {
           unsigned char origin;
           unsigned char num;
           unsigned short ver;
           unsigned int type;
           float field_0;
           //   more datafields...
     }

     struct dataFrame *frames = (struct dataFrame*)generalArray;
     unsigned char *byteArray = (unsigned char*)args[0];
     int bufSize = *(int*)args[1];

     // use the frames array to fill the byteArray...
}

On the managed side:


struct DataFrame
{
     public byte origin;
     public byte num;
     public UInt16 ver;
     public UInt32 type;
     public float field_0;
     //   more datafields...
     public float field_12;
      public UInt32 crc;
}
 
DataFrame[] dataFrames = new DataFrame[8];
byte[]   databuffer = new byte[4096]

RLP_FillBuffer.InvokeEx(dataFrames, databuffer, 4096);


#6

Tried it out. The RLP Invoke method causes a runtime error saying parameter 0 is not supported. Presumably, it is choking because it does not know how to cast Dataframe [] to byte [].

This is quite frustrating since I can see in debugger that the I am able to get the 32bit address that is correct. Just that I cannot cast it into form that will be accepted by the managed code methods…

I would imagine there are plenty of others who has ran into this problem when interfacing with external equipments through SPI, UDP, or any data stream that is structured (what data stream isn’t!)

How do others package structured data to transmit over “the wire”?

Hiroo


#7

@ humeno - Are you defining your rlp function like this, with the first parameter as void *generalArray, and calling it with the InvokeEx method? I’ve never tried this, but according to the RLP help file, you should be able to pass your array as the first parameter, and then cast it to whatever you like within the native function.


int rlpFillBuffer(void *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
    ...
}


#8

You can pass “any array” as long as it is an array of system defined data like Byte, Int, etc. In this case, it is an array of my user defined structure. I am guessing that is what it is choking on.

The key issue here is that I need to pass two arrays to the RLP. I thought I would pass one array plus pointer as a 32bit integer but I am not able to get a pointer to cast to an Int to be able to do that.


#9

@ humeno - you can try to play with the unsafe keyword. You need to enable “Allow unsafe code” on the project property.build page.


namespace MyNameSpace
{
    public struct MyStruct
    {
        public int A;
    }

    class UnsafeTest
    {
        private static unsafe void Main()
        {
            MyStruct sStruct;
            sStruct.A = 10;

            DoSomething((byte*) &sStruct);
        }

        private static unsafe void DoSomething(byte* p)
        {
            int a = *p;
        }
    }
}


#10

@ RobvanSchelven - I believe there is no support for unsafe in MF.


#11

@ Mike - I have tested it in the emulator so i might expect that it works as well on a real device.

Here’s an working example:


using Microsoft.SPOT;

namespace MyNameSpace
{
    public struct MyStruct
    {
        public int A;
        public int B;
    }

    class UnsafeTest
    {
        private static unsafe void Main()
        {
            MyStruct sStruct;
            sStruct.A = 10;
            sStruct.B = 20;

            int c = DoSomething((byte*)&sStruct);
            Debug.Print(c.ToString());
        }

        private static unsafe int DoSomething(byte* p)
        {
            MyStruct b = *(MyStruct *) p;
            return b.A + b.B;
        }
    }
}


#12

@ RobvanSchelven - I’d verify that on a real device. I believe Mike is correct.


#13

@ ianlee74 - Thanks for testing, interesting to know that there’s a difference between the emulator and a real device.

Later I realized that even having a byte pointer does not help much since its still pointing to a managed object. And the internal data structure can’t been seen as a a piece of memory with variables as in unmanaged code.


#14

Sorry, I was suggesting that you test it. I didn’t actually test it today. I have tried this many months ago and it didn’t work. I don’t believe anything has changed that would now allow it to work.


#15

@ ianlee74 - I guess things have changed. It seems to work now on a real device (G120)


#16

Wow! That’s really interesting. I wonder if this also applies to OSHW devices.