Search code examples
javajnaunions

How to map a union in a JNA Structure


I need to convert a couple C structures to java using JNA. My structures are so composed:

struct s_xl_event {
XLeventTag tag;
unsigned char chanIndex;
unsigned short transId;
unsigned short portHandle;
unsigned char flags;
unsigned char reserved;
XLuint64 timeStamp;
union s_xl_tag_data tagData;
};

union s_xl_tag_data {
struct s_xl_can_msg msg;
struct s_xl_chip_state chipState;
union s_xl_lin_msg_api linMsgApi;
struct s_xl_sync_pulse syncPulse;
struct s_xl_daio_data daioData;
struct s_xl_transceiver transceiver;
};

struct s_xl_can_msg {
unsigned long id;
unsigned short flags;
unsigned short dlc;
XLuint64 res1;
unsigned char data[MAX_MSG_LEN];
XLuint64 res2;
};

I added only the main structures that I needed but also because all the related structure are similar to s_xl_can_msg.

In Java I did this using JNA:

public static class s_xl_event extends Structure {
    public byte tag;
    public byte chanIndex;
    public short transId;
    public short portHandle;
    public byte flags;
    public byte reserved;
    public long timeStamp;
    public s_xl_tag_data tagData;

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("tag","chanIndex","transId","portHandle","flags","reserved","timeStamp","tagData");
    }
}

public static class s_xl_tag_data extends Union {
    public s_xl_can_msg msg;
    public s_xl_chip_state chipState;
    public s_xl_lin_msg_api linMsgApi;
    public s_xl_sync_pulse syncPulse;
    public s_xl_daio_data daioData;
    public s_xl_transceiver transceiver;
}

public static class s_xl_lin_msg extends Structure {        
    public byte id;
    public byte dlc;
    public short flags;
    public byte[] data = new byte[8];
    public byte crc;

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("id","dlc","flags","data","crc");
    }
}

When I use the method

short xlReceive(long portHandle, IntByReference eventCount, s_xl_event eventList);

I obtain the values related to all the fields in s_xl_event except for tagData where all the fields are filled with 0. Now, I'm not sure that I mapped all the sub structures and unions as expected or I forgot something. Has anyone had anything to do with such a thing?


Solution

  • When reading a Union like s_xl_tag_data, you must tell the Union which of its fields is being read by using setType() with either the class or field name, and then call read() on the Union.

    One way of doing this automatically when the parent structure is first populated is by overriding the read() method in the parent structure.

    Add this method to your s_xl_event Structure:

    public void read() {
        // read from native memory, populate tag
        super.read(); 
        // set union type based on tag
        switch(tag) {
        case XL_RECEIVE_MSG:
        case XL_TRANSMIT_MSG: 
            tagData.setType(s_xl_can_msg.class);
            break;
        case XL_CHIP_STATE: 
            tagData.setType(s_xl_chip_state.class);
            break;
        case XL_LIN_MSG: 
            tagData.setType(s_xl_lin_msg_api.class);
            break;
        // ... add other cases here...
        default:
            // add default type or throw exception etc.  
            break;
        }
        // now read tagData from native memory
        tagData.read();
    }
    

    As an alternative to passing the class to setType() for the union element, you could instead pass a String containing the variable name, e.g., "msg", "chipState", or "linMsgApi", etc.

    Also of note, the getFieldOrder() methods you are using in your Structures are deprecated in the latest version of JNA. You should use the @FieldOrder annotation instead.