Search code examples
pythonc++roscode-translation

Python (rospy) to C++ (roscpp) struct.unpack


I am currently translating a rospy IMU-driver to roscpp and have difficulites figuring out what this piece of code does and how I can translate it.

def ReqConfiguration(self):
    """Ask for the current configuration of the MT device.
    Assume the device is in Config state."""
    try:
        masterID, period, skipfactor, _, _, _, date, time, num, deviceID,\
                length, mode, settings =\
                struct.unpack('!IHHHHI8s8s32x32xHIHHI8x', config)
    except struct.error:
        raise MTException("could not parse configuration.")
    conf = {'output-mode': mode,
            'output-settings': settings,
            'length': length,
            'period': period,
            'skipfactor': skipfactor,
            'Master device ID': masterID,
            'date': date,
            'time': time,
            'number of devices': num,
            'device ID': deviceID}
    return conf

I have to admit that I never ever worked with neither ros nor python before. This is no 1:1 code from the source, I removed the lines I think I know what they do, but especially the try-block is what I don't understand. I would really appreciate help, because I am under great preasure of time.

If someone is curious(context reasons): The files I have to translate are mtdevice.py , mtnode.py and mtdef.py and can be found googleing for the filesnames + the keyword ROS IMU Driver

Thanks a lot in advance.


Solution

  • To do the same thing with C++, you'd declare a struct with the various parameters:

    struct DeviceRecord {
        uint32_t masterId;
        uint16_t period, skipfactor, _a, _b;
        uint32_t _c;
        char date[8];
        char time[8];
        char padding[64];
        uint16_t num;
        uint32_t deviceID;
        uint16_t length, mode;
        uint32_t settings;
        char padding[8];
    };
    

    (It's possible this struct is already declared somewhere; it might also use "unsigned int" instead of "uint32_t" and "unsigned short" instead of "uint16_t", and _a, _b, _c would probably have real names.)

    Once you have your struct, the question is how to get the data. That depends on where the data is. If it's in a file, you'd do something like this:

    DeviceRecord rec; // An instance of the struct, whatever it's called
    std::ifstream fin("yourfile.txt", std::ios::binary);
    fin.read(reinterpret_cast<char*>(&rec), sizeof(rec));
    // Now you can access rec.masterID etc
    

    On the other hand, if it's somewhere in memory (ie, you have a char* or void* to it), then you just need to cast it:

    void* data_source = get_data(...); // You'd get this from somewhere
    DeviceRecord* rec_ptr = reinterpret_cast<DeviceRecord*>(stat_source);
    // Now you can access rec_ptr->masterID etc
    

    If you have a std::vector, you can easily get such a pointer:

    std::vector<uint8_t> data_source = get_data(...); // As above
    DeviceRecord* rec_ptr = reinterpret_cast<DeviceRecord*>(data_source.data());
    // Now you can access rec_ptr->masterID etc, provided data_source remains in scope. You should probably also avoid modifying data_source.
    

    There's one more issue here. The data you've received is in big-endian, but unless you have a PowerPC or other unusual processor, you're probably on a little-endian machine. So you need to do a little byte-swapping before you access the data. You can use the following function to do this.

    template<typename Int>
    Int swap_int(Int n) {
        if(sizeof(Int) == 2) {
            union {char c[2]; Int i;} swapper;
            swapper.i = n;
            std::swap(swapper.c[0], swapper.c[1]);
            n = swapper.i;
        } else if(sizeof(Int) == 4) {
            union {char c[4]; Int i;} swapper;
            swapper.i = n;
            std::swap(swapper.c[0], swapper.c[3]);
            std::swap(swapper.c[1], swapper.c[2]);
            n = swapper.i;
        }
        return n;
    }
    

    These return the swapped value rather than changing it in-place, so now you'd access your data with something like swap_int(rec->num). NB: The above byte-swapping code is untested; I'll try compiling it a bit later and fix it if necessary.

    Without more information, I can't give you a definitive way of doing this, but perhaps this will be enough to help you work it out on your own.