1

I've followed C-like structures in Python, and i defined a class CommandHeader that inherits ctypes.Structure.

class CommandHeader(Structure):
    """
    struct {
        uint8_t    key;
        uint8_t    flag;
        uint16_t   command;
        uint16_t   length;
        uint16_t   req_id;
    }
    """
    _fields_ = [
        ('key', c_uint8),
        ('flag', c_uint8),
        ('command', c_uint16),
        ('length', c_uint16),
        ('req_id', c_uint16),
    ]

But i have another requirement that is convert this C-like structure into bytes.

So i have to use struct module and define two member function that is pack and unpack.

class CommandHeader(Structure):
    """
    struct {
        uint8_t    key;
        uint8_t    flag;
        uint16_t   command;
        uint16_t   length;
        uint16_t   req_id;
    }
    """
    _fields_ = [
        ('key', c_uint8),
        ('flag', c_uint8),
        ('command', c_uint16),
        ('length', c_uint16),
        ('req_id', c_uint16),
    ]

    def pack(self):
        return struct.pack('!BBHHH', self.key, self.flag, self.command, self.length, self.req_id)

    def unpack(self, data):
        self.key, self.flag, self.command, self.length, self.req_id = struct.unpack('!BBHHH', data)

This code can work normally.

But i think pack and unpack member function is not well, because i have to define this two member function for every class that like CommandHeader.

In addition, if a class has a lot of members, then the pack and unpack member function will be very long, which looks very ugly.

class DIS_ACK(Structure):
    ack_fmt = '!HHIHHIIIIIIIIIIIIIII32s32s32s48s16s16s'
    ack_size = 248
    _fields_ = [
        ('header', ACK_HEADER),
        ('spec_version_major', c_ushort),
        ('spec_version_minor', c_ushort),
        ('device_mode', c_uint),
        ('reserved_0', c_ushort),
        ('device_mac_address_high', c_ushort),
        ('device_mac_address_low', c_uint),
        ('ip_config_options', c_uint),
        ('ip_config_current', c_uint),
        ('reserved_1', c_uint),
        ('reserved_2', c_uint),
        ('reserved_3', c_uint),
        ('current_ip', c_uint),
        ('reserved_4', c_uint),
        ('reserved_5', c_uint),
        ('reserved_6', c_uint),
        ('current_subnet_mask', c_uint),
        ('reserved_7', c_uint),
        ('reserved_8', c_uint),
        ('reserved_9', c_uint),
        ('default_gateway', c_uint),
        ('manufacturer_name', c_char_p),  # 32
        ('model_name', c_char_p),  # 32
        ('device_version', c_char_p),  # 32
        ('manufacturer_spec_info', c_char_p),  # 48
        ('serial_number', c_char_p),  # 16
        ('user_defined_name', c_char_p)  # 16
    ]

    def unpack(self, sock_data):
        self.header.unpack(sock_data)
        if self.header.length == self.ack_size:
            (self.spec_version_major,
             self.spec_version_minor,
             self.device_mode,
             self.reserved_0,
             self.device_mac_address_high,
             self.device_mac_address_low,
             self.ip_config_options,
             self.ip_config_current,
             self.reserved_1,
             self.reserved_2,
             self.reserved_3,
             self.current_ip,
             self.reserved_4,
             self.reserved_5,
             self.reserved_6,
             self.current_subnet_mask,
             self.reserved_7,
             self.reserved_8,
             self.reserved_9,
             self.default_gateway,
             self.manufacturer_name,
             self.model_name,
             self.device_version,
             self.manufacturer_spec_info,
             self.serial_number,
             self.user_defined_name) = struct.unpack(self.ack_fmt,
                                                     sock_data[self.header.header_size:])

    def pack(self):
        self.header.command = 0x0003
        if self.header.length != self.ack_size:
            return self.header.pack()
        return self.header.pack() + struct.pack(self.ack_fmt,
                                                self.spec_version_major,
                                                self.spec_version_minor,
                                                self.device_mode,
                                                self.reserved_0,
                                                self.device_mac_address_high,
                                                self.device_mac_address_low,
                                                self.ip_config_options,
                                                self.ip_config_current,
                                                self.reserved_1,
                                                self.reserved_2,
                                                self.reserved_3,
                                                self.current_ip,
                                                self.reserved_4,
                                                self.reserved_5,
                                                self.reserved_6,
                                                self.current_subnet_mask,
                                                self.reserved_7,
                                                self.reserved_8,
                                                self.reserved_9,
                                                self.default_gateway,
                                                self.manufacturer_name,
                                                self.model_name,
                                                self.device_version,
                                                self.manufacturer_spec_info,
                                                self.serial_number,
                                                self.user_defined_name

So is there some better way to do this?

Wonter
  • 293
  • 1
  • 5
  • 15

0 Answers0