Examples

EEPROM-Content

Let’s say you want to create an eeprom content using OSER.

The content has the following structure:

Entry

Type

Position

Description

type

U8

0

the module type

serial number

String[9]

1 .. 9

serial number: four upper case letters, a minus and four digits

hardware-type

U8

10

the hardware-type

calibration

F32[2]

11 .. 18

calibration factors

crc

U32

12 .. 22

32-bit CRC, polynomial 0x1EDC6F41

All values are stored in big endian.

The following abstraction lets you create this mapping easily:

>>> from oser import ByteStruct, UBInt8, BFloat, RegularExpressionMatch, \
    Array, CRCB32, to_hex

>>> class EEPROM(ByteStruct):
...     def __init__(self, *args, **kwargs):
...         ByteStruct.__init__(self, *args, **kwargs)
...
...         self.type = UBInt8(0)
...         self.serial_number = RegularExpressionMatch(pattern=r"[A-Z]{4}-[0-9]{4}", length=9, value=b"AAAA-0000")
...         self.hardware_type = UBInt8(0)
...         self.calibration = Array(length=2, prototype=BFloat)
...         self.crc = CRCB32(strict=True, polynomial=0x1EDC6F41)
...
>>> eeprom = EEPROM()
>>> eeprom.type.set(3)
>>> eeprom.serial_number.set(b"ASDF-1337")
>>> eeprom.hardware_type.set(23)
>>> for ii in range(2):
...     eeprom.calibration[ii].set((.1+ii)**3)
...
>>> binary = eeprom.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22
\x03\x41\x53\x44\x46\x2D\x31\x33\x33\x37\x17\x3A\x83\x12\x6F\x3F\xAA\x5E\x35\x3D\x10\xBB\xCB
>>> print(eeprom.introspect())
   -    -  EEPROM():
   0 \x03      type: 3 (UBInt8)
   -    -      serial_number: RegularExpressionMatch():
   1 \x41          'A'
   2 \x53          'S'
   3 \x44          'D'
   4 \x46          'F'
   5 \x2d          '-'
   6 \x31          '1'
   7 \x33          '3'
   8 \x33          '3'
   9 \x37          '7'
  10 \x17      hardware_type: 23 (UBInt8)
   -    -      calibration: Array():
   -    -      [
  11 \x3a          @0: 0.001 (BFloat)
  12 \x83
  13 \x12
  14 \x6f
  15 \x3f          @1: 1.331 (BFloat)
  16 \xaa
  17 \x5e
  18 \x35
   -    -      ]
  19 \x3d      crc: 1024506827 (CRCB32)
  20 \x10
  21 \xbb
  22 \xcb

You can now write this data into your eeprom.

Custom Network Protocol

You may want to create a custom network protocol for you device or software. Because the message is transmitted via any serial connection it starts with a magic number 0x23. This is a U8.

This magic number is followed by the destination address and the source address. Both are U16.

Additionally there is a service-id as U32.

The service-id is followed by the total payload length as U16 without crc. This way the transport-layer must not be aware of the protocol itself.

The payload consists of a type, a length and the data itself. The type has symbolic names:

  • 0: string

  • 1: list of 16-bit integers

  • 2: list of floats

A frame ends with a 32-bit crc with polynomial 0x1EDC6F41.

All values are transmitted in big endian.

The following abstraction creates a frame for this protocol:

>>> from oser import ByteStruct, Constant, UBInt8, BFloat, UBInt16, UBInt32, \
...     Enum, Array, Switch, String, CRCB32, to_hex
>>>
>>> a1 = Array(length=1, prototype=UBInt16)
>>> a2 = Array(length=2, prototype=BFloat)
>>>
>>> class SerialProtocol(ByteStruct):
...     def __init__(self, *args, **kwargs):
...         ByteStruct.__init__(self, *args, **kwargs)
...
...         self.start_of_frame = Constant(UBInt8, 0x23)
...         self.destination = UBInt16(0)
...         self.source = UBInt16(0)
...         self.service_id = UBInt32(0)
...         self.payload_length = UBInt16(0)
...         self.payload = Payload()
...         self.crc = CRCB32(strict=True, polynomial=0x1EDC6F41)
...
...     def encode(self, full_data=b"", context_data=b""):
...         self.payload_length.set(self.payload.size())
...         return super(SerialProtocol, self).encode(full_data, context_data)
...
>>> class Payload(ByteStruct):
...     def __init__(self, *args, **kwargs):
...         ByteStruct.__init__(self, *args, **kwargs)
...
...         self.type = Enum(UBInt32,
...                          values={
...                              "string": 0,
...                              "u16list": 1,
...                              "f32list": 2,
...                          }, value="string")
...         self.length = UBInt16(0)
...         self.data = Switch(
...             condition=lambda self: self.type.get(),
...             values={
...                 "string": String(length=lambda self: self.length.get()),
...                 "u16list": Array(length=lambda self: self.length.get(), prototype=UBInt16),
...                 "f32list": Array(length=lambda self: self.length.get(), prototype=BFloat),
...             })
...
>>> frame = SerialProtocol()
>>> frame.service_id.set(1337)
>>>
>>> # string
>>> frame.payload.type.set("string")
>>> frame.payload.length.set(10)
>>> frame.payload.data.set("abcdefghij")
>>>
>>> binary = frame.encode()
>>> print(frame)
SerialProtocol():
    start_of_frame: 35 (UBInt8)
    destination: 0 (UBInt16)
    source: 0 (UBInt16)
    service_id: 1337 (UBInt32)
    payload_length: 16 (UBInt16)
    payload: Payload():
        type: 'string' (UBInt32)
        length: 10 (UBInt16)
        data: b'abcdefghij'
    crc: 0x57c9544f (CRCB32)

>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26| 27| 28| 29| 30
\x23\x00\x00\x00\x00\x00\x00\x05\x39\x00\x10\x00\x00\x00\x00\x00\x0a\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x57\xc9\x54\x4f
>>> print(frame.introspect())
   -    -  SerialProtocol():
   0 \x23      start_of_frame: 35 (UBInt8)
   1 \x00      destination: 0 (UBInt16)
   2 \x00
   3 \x00      source: 0 (UBInt16)
   4 \x00
   5 \x00      service_id: 1337 (UBInt32)
   6 \x00
   7 \x05
   8 \x39
   9 \x00      payload_length: 16 (UBInt16)
  10 \x10
   -    -      payload: Payload():
  11 \x00          type: 0 (UBInt32)
  12 \x00
  13 \x00
  14 \x00
  15 \x00          length: 10 (UBInt16)
  16 \x0a
   -    -          data: String():
  17 \x61            'a'
  18 \x62            'b'
  19 \x63            'c'
  20 \x64            'd'
  21 \x65            'e'
  22 \x66            'f'
  23 \x67            'g'
  24 \x68            'h'
  25 \x69            'i'
  26 \x6a            'j'
  27 \x57      crc: 0x57c9544f (CRCB32)
  28 \xc9
  29 \x54
  30 \x4f

>>> # list of u16
>>> frame.payload.type.set("u16list")
>>> frame.payload.length.set(3)
>>> for ii in range(3):
...     frame.payload.data[ii].set(ii)
...
>>> binary = frame.encode()
>>> print(frame)
SerialProtocol():
    start_of_frame: 35 (UBInt8)
    destination: 0 (UBInt16)
    source: 0 (UBInt16)
    service_id: 1337 (UBInt32)
    payload_length: 12 (UBInt16)
    payload: Payload():
        type: 'u16list' (UBInt32)
        length: 3 (UBInt16)
        data: Array():
        [
            @0: 0 (UBInt16)
            @1: 1 (UBInt16)
            @2: 2 (UBInt16)
        ]
    crc: 0x224b28ee (CRCB32)

>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26
\x23\x00\x00\x00\x00\x00\x00\x05\x39\x00\x0c\x00\x00\x00\x01\x00\x03\x00\x00\x00\x01\x00\x02\x22\x4b\x28\xee
>>> print(frame.introspect())
   -    -  SerialProtocol():
   0 \x23      start_of_frame: 35 (UBInt8)
   1 \x00      destination: 0 (UBInt16)
   2 \x00
   3 \x00      source: 0 (UBInt16)
   4 \x00
   5 \x00      service_id: 1337 (UBInt32)
   6 \x00
   7 \x05
   8 \x39
   9 \x00      payload_length: 12 (UBInt16)
  10 \x0c
   -    -      payload: Payload():
  11 \x00          type: 1 (UBInt32)
  12 \x00
  13 \x00
  14 \x01
  15 \x00          length: 3 (UBInt16)
  16 \x03
   -    -          data: Array():
   -    -          [
  17 \x00              @0: 0 (UBInt16)
  18 \x00
  19 \x00              @1: 1 (UBInt16)
  20 \x01
  21 \x00              @2: 2 (UBInt16)
  22 \x02
   -    -          ]
  23 \x22      crc: 0x224b28ee (CRCB32)
  24 \x4b
  25 \x28
  26 \xee

>>> # list of float
>>> frame.payload.type.set("f32list")
>>> frame.payload.length.set(2)
>>> for ii in range(2):
...     frame.payload.data[ii].set(float(ii*1.5))
...
>>> binary = frame.encode()
>>> print(frame)
SerialProtocol():
    start_of_frame: 35 (UBInt8)
    destination: 0 (UBInt16)
    source: 0 (UBInt16)
    service_id: 1337 (UBInt32)
    payload_length: 14 (UBInt16)
    payload: Payload():
        type: 'f32list' (UBInt32)
        length: 2 (UBInt16)
        data: Array():
        [
            @0: 0.0 (BFloat)
            @1: 1.5 (BFloat)
        ]
    crc: 0x4db8bf4 (CRCB32)

>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26| 27| 28
\x23\x00\x00\x00\x00\x00\x00\x05\x39\x00\x0e\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x3f\xc0\x00\x00\x04\xdb\x8b\xf4
>>> print(frame.introspect())
   -    -  SerialProtocol():
   0 \x23      start_of_frame: 35 (UBInt8)
   1 \x00      destination: 0 (UBInt16)
   2 \x00
   3 \x00      source: 0 (UBInt16)
   4 \x00
   5 \x00      service_id: 1337 (UBInt32)
   6 \x00
   7 \x05
   8 \x39
   9 \x00      payload_length: 14 (UBInt16)
  10 \x0e
   -    -      payload: Payload():
  11 \x00          type: 2 (UBInt32)
  12 \x00
  13 \x00
  14 \x02
  15 \x00          length: 2 (UBInt16)
  16 \x02
   -    -          data: Array():
   -    -          [
  17 \x00              @0: 0.0 (BFloat)
  18 \x00
  19 \x00
  20 \x00
  21 \x3f              @1: 1.5 (BFloat)
  22 \xc0
  23 \x00
  24 \x00
   -    -          ]
  25 \x04      crc: 0x4db8bf4 (CRCB32)
  26 \xdb
  27 \x8b
  28 \xf4

>>> #decode data
>>> data = b"\x23\x00\x00\x00\x00\x00\x00\x05\x39\x00\x1A\x00\x00\x00\x02\x00" \
...        b"\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
...        b"\x00\x00\x00\x00\x00\xA4\x58\x03\x64"
>>> frame.decode(data)
41
>>> print(frame)
SerialProtocol():
    start_of_frame: 35 (UBInt8)
    destination: 0 (UBInt16)
    source: 0 (UBInt16)
    service_id: 1337 (UBInt32)
    payload_length: 26 (UBInt16)
    payload: Payload():
        type: 'f32list' (UBInt32)
        length: 5 (UBInt16)
        data: Array():
        [
            @0: 0.0 (BFloat)
            @1: 0.0 (BFloat)
            @2: 0.0 (BFloat)
            @3: 0.0 (BFloat)
            @4: 0.0 (BFloat)
        ]
    crc: 0xa4580364 (CRCB32)

>>> print(frame.introspect())
   -    -  SerialProtocol():
   0 \x23      start_of_frame: 35 (UBInt8)
   1 \x00      destination: 0 (UBInt16)
   2 \x00
   3 \x00      source: 0 (UBInt16)
   4 \x00
   5 \x00      service_id: 1337 (UBInt32)
   6 \x00
   7 \x05
   8 \x39
   9 \x00      payload_length: 26 (UBInt16)
  10 \x1a
   -    -      payload: Payload():
  11 \x00          type: 2 (UBInt32)
  12 \x00
  13 \x00
  14 \x02
  15 \x00          length: 5 (UBInt16)
  16 \x05
   -    -          data: Array():
   -    -          [
  17 \x00              @0: 0.0 (BFloat)
  18 \x00
  19 \x00
  20 \x00
  21 \x00              @1: 0.0 (BFloat)
  22 \x00
  23 \x00
  24 \x00
  25 \x00              @2: 0.0 (BFloat)
  26 \x00
  27 \x00
  28 \x00
  29 \x00              @3: 0.0 (BFloat)
  30 \x00
  31 \x00
  32 \x00
  33 \x00              @4: 0.0 (BFloat)
  34 \x00
  35 \x00
  36 \x00
   -    -          ]
  37 \xa4      crc: 0xa4580364 (CRCB32)
  38 \x58
  39 \x03
  40 \x64

HTTP-Request

This example shows how to abstract a http request transmitted via TCP and IPv4.

First an ethernet frame must be constructed:

class EthernetFrame(ByteStruct):
    def __init__(self, *args, **kwargs):
        ByteStruct.__init__(self, *args, **kwargs)

        self.destination = MacAddress(6)
        self.source = MacAddress(6)
        self.type = Enum(prototype=UBInt16, values={"IP" : 0x0800})

        self.payload = Switch(condition=lambda self: self.type.get(),
                              values={
                                  "IP" : InternetProtocolPayload(),
                              },
                              default=Null())

The MAC address is a oser.String with little modifications for printing:

class MacAddress(String):
    def get(self):
        return ":".join(map(lambda c: f"{c:X}", super(MacAddress, self).get()))

Now the IPv4 payload must be constructed:

class InternetProtocolPayload(ByteStruct):
    def __init__(self, *args, **kwargs):
        ByteStruct.__init__(self, *args, **kwargs)

        self.header = BitStruct()
        self.header.version = Nibble(0)
        self.header.ihl = Nibble(0)  # ip header type
        self.header.tos = BitStruct()  # type of service
        self.header.dscp = BitField(length=6)  # differentiated service code point
        self.header.ecn = BitField(length=2)  # explicit congestion notification (ip flowcontrol)
        self.header.total_length = BitField(length=16)
        self.header.identification = BitField(length=16)
        self.header.flags = BitField(length=3)
        self.header.fragment_offset = BitField(length=13)
        self.header.ttl = BitField(length=8)
        self.protocol = Enum(UBInt8, values={"TCP" : 0x06})
        self.header_checksum = UBInt16(0)

        self.payload = Switch(condition=lambda self: self.protocol.get(),
                              values={
                                  "TCP" : TransmissionControlProtocolPayload(),
                              })

And for TCP a payload must be constructed, too:

class TransmissionControlProtocolPayload(ByteStruct):
    def __init__(self, *args, **kwargs):
        ByteStruct.__init__(self, *args, **kwargs)

        self.source_address = UBInt32(0)
        self.destination_address = UBInt32(0)
        self.source_port = UBInt16(0)
        self.destination_port = UBInt16(0)
        self.sequence_number = UBInt32(0)
        self.acknowledgement_number = UBInt32(0)

        self.offset_and_flags = BitStruct()
        self.offset_and_flags.offset = BitField(length=4)
        self.offset_and_flags.reserved = BitField(length=6)
        self.offset_and_flags.urgent_flag = Flag()
        self.offset_and_flags.acknowledgement_flag = Flag()
        self.offset_and_flags.psh_flag = Flag()
        self.offset_and_flags.rst_flag = Flag()
        self.offset_and_flags.syn_flag = Flag()
        self.offset_and_flags.fin_flag = Flag()

        self.window = UBInt16()
        self.checksum = UBInt16()
        self.urgent_pointer = UBInt16()

        self.options = Array(length=3, prototype=UBInt32)

        self.payload = HTTPPayload(length=lambda self: self.up().header.total_length.get()-52)

The HTTP payload is a oser.String with little modifications too. That helps to show the plain text payload in a human readable way:

class HTTPPayload(String):
    def _toString(self):
        return "\n" + "\n".join(self.get().split("\r\n"))

To decode an http request use the following code (the data came from Wireshark. Right-click a packet -> Copy -> Bytes -> Hex Stream):

>>> frame = EthernetFrame()

>>> data = binascii.unhexlify(
...     b"f4ec38c9b85438607713fa070800450003162ac740004006fabbc0a80003c163"
...     b"9050ac2f005062b7938d1e3de4bb80187210156800000101080a0173bd0013d6"
...     b"d56f474554202f20485454502f312e310d0a486f73743a2068656973652e6465"
...     b"0d0a436f6e6e656374696f6e3a206b6565702d616c6976650d0a557365722d41"
...     b"67656e743a204d6f7a696c6c612f352e3020285831313b204c696e7578207838"
...     b"365f363429204170706c655765624b69742f3533352e313920284b48544d4c2c"
...     b"206c696b65204765636b6f29204368726f6d652f31382e302e313032352e3136"
...     b"32205361666172692f3533352e31390d0a4163636570743a20746578742f6874"
...     b"6d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c696361"
...     b"74696f6e2f786d6c3b713d302e392c2a2f2a3b713d302e380d0a416363657074"
...     b"2d456e636f64696e673a20677a69702c6465666c6174652c736463680d0a4163"
...     b"636570742d4c616e67756167653a2064652d44452c64653b713d302e382c656e"
...     b"2d55533b713d302e362c656e3b713d302e340d0a4163636570742d4368617273"
...     b"65743a2049534f2d383835392d312c7574662d383b713d302e372c2a3b713d30"
...     b"2e330d0a436f6f6b69653a2077745f76743d3776303030253342367633357431"
...     b"3430393539373433353731383b207774335f6569643d25334232383836383936"
...     b"3336393230313734253743323134323531313732373230303232303339313b20"
...     b"6f7074696d697a656c79456e645573657249643d6f6575313432383735373231"
...     b"3735343972302e393837303834313034393132333535353b206f7074696d697a"
...     b"656c795365676d656e74733d2537422532323232383632353038353825323225"
...     b"3341253232676325323225324325323232333031313130373332253232253341"
...     b"25323266616c7365253232253243253232323331363235303236342532322533"
...     b"412532327365617263682532322537443b206f7074696d697a656c794275636b"
...     b"6574733d25374225323232373435383230343039253232253341253232302532"
...     b"322537443b207774335f7369643d253342323838363839363336393230313734"
...     b"0d0a0d0a"
... )

>>> frame.decode(data)
804
>>> print(frame)
EthernetFrame():
    destination: 'F4:EC:38:C9:B8:54'
    source: '38:60:77:13:FA:7'
    type: 'IP' (UBInt16)
    payload: InternetProtocolPayload():
        header: BitStruct():
            version: 4 (Nibble)
            ihl: 5 (Nibble)
            tos: BitStruct():
            dscp: 0 (BitField(6))
            ecn: 0 (BitField(2))
            total_length: 790 (BitField(16))
            identification: 10951 (BitField(16))
            flags: 2 (BitField(3))
            fragment_offset: 0 (BitField(13))
            ttl: 64 (BitField(8))
        protocol: 'TCP' (UBInt8)
        header_checksum: 64187 (UBInt16)
        payload: TransmissionControlProtocolPayload():
            source_address: 3232235523 (UBInt32)
            destination_address: 3244527696 (UBInt32)
            source_port: 44079 (UBInt16)
            destination_port: 80 (UBInt16)
            sequence_number: 1656198029 (UBInt32)
            acknowledgement_number: 507372731 (UBInt32)
            offset_and_flags: BitStruct():
                offset: 8 (BitField(4))
                reserved: 0 (BitField(6))
                urgent_flag: False (Flag)
                acknowledgement_flag: True (Flag)
                psh_flag: True (Flag)
                rst_flag: False (Flag)
                syn_flag: False (Flag)
                fin_flag: False (Flag)
            window: 29200 (UBInt16)
            checksum: 5480 (UBInt16)
            urgent_pointer: 0 (UBInt16)
            options: Array():
            [
                @0: 16844810 (UBInt32)
                @1: 24362240 (UBInt32)
                @2: 332846447 (UBInt32)
            ]
            payload: b'GET / HTTP/1.1\r\nHost: heise.de\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: gzip,deflate,sdch\r\nAccept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\nCookie: wt_vt=7v000%3B6v35t1409597435718; wt3_eid=%3B288689636920174%7C2142511727200220391; optimizelyEndUserId=oeu1428757217549r0.9870841049123555; optimizelySegments=%7B%222286250858%22%3A%22gc%22%2C%222301110732%22%3A%22false%22%2C%222316250264%22%3A%22search%22%7D; optimizelyBuckets=%7B%222745820409%22%3A%220%22%7D; wt3_sid=%3B288689636920174\r\n\r\n'

>>> print(frame.introspect())
   -    -  EthernetFrame():
   -    -      destination: MacAddress():
   0 \xf4        'ô'
   1 \xec        'ì'
   2 \x38        '8'
   3 \xc9        'É'
   4 \xb8        '¸'
   5 \x54        'T'
   -    -      source: MacAddress():
   6 \x38        '8'
   7 \x60        '`'
   8 \x77        'w'
   9 \x13        '\x13'
  10 \xfa        'ú'
  11 \x07        '\x07'
  12 \x08      type: 2048 (UBInt16)
  13 \x00
   -    -      payload: InternetProtocolPayload():
   -    -          header: BitStruct():
  14.7  0              version: 4 (Nibble)
  14.6  1
  14.5  0
  14.4  0
  14.3  0              ihl: 5 (Nibble)
  14.2  1
  14.1  0
  14.0  1
   -    -              tos: BitStruct():
  15.7  0              dscp: 0 (BitField(6))
  15.6  0
  15.5  0
  15.4  0
  15.3  0
  15.2  0
  15.1  0              ecn: 0 (BitField(2))
  15.0  0
  16.7  0              total_length: 790 (BitField(16))
  16.6  0
  16.5  0
  16.4  0
  16.3  0
  16.2  0
  16.1  1
  16.0  1
  17.7  0
  17.6  0
  17.5  0
  17.4  1
  17.3  0
  17.2  1
  17.1  1
  17.0  0
  18.7  0              identification: 10951 (BitField(16))
  18.6  0
  18.5  1
  18.4  0
  18.3  1
  18.2  0
  18.1  1
  18.0  0
  19.7  1
  19.6  1
  19.5  0
  19.4  0
  19.3  0
  19.2  1
  19.1  1
  19.0  1
  20.7  0              flags: 2 (BitField(3))
  20.6  1
  20.5  0
  20.4  0              fragment_offset: 0 (BitField(13))
  20.3  0
  20.2  0
  20.1  0
  20.0  0
  21.7  0
  21.6  0
  21.5  0
  21.4  0
  21.3  0
  21.2  0
  21.1  0
  21.0  0
  22.7  0              ttl: 64 (BitField(8))
  22.6  1
  22.5  0
  22.4  0
  22.3  0
  22.2  0
  22.1  0
  22.0  0
  23 \x06          protocol: 6 (UBInt8)
  24 \xfa          header_checksum: 64187 (UBInt16)
  25 \xbb
   -    -          payload: TransmissionControlProtocolPayload():
  26 \xc0              source_address: 3232235523 (UBInt32)
  27 \xa8
  28 \x00
  29 \x03
  30 \xc1              destination_address: 3244527696 (UBInt32)
  31 \x63
  32 \x90
  33 \x50
  34 \xac              source_port: 44079 (UBInt16)
  35 \x2f
  36 \x00              destination_port: 80 (UBInt16)
  37 \x50
  38 \x62              sequence_number: 1656198029 (UBInt32)
  39 \xb7
  40 \x93
  41 \x8d
  42 \x1e              acknowledgement_number: 507372731 (UBInt32)
  43 \x3d
  44 \xe4
  45 \xbb
   -    -              offset_and_flags: BitStruct():
  46.7  1                  offset: 8 (BitField(4))
  46.6  0
  46.5  0
  46.4  0
  46.3  0                  reserved: 0 (BitField(6))
  46.2  0
  46.1  0
  46.0  0
  47.7  0
  47.6  0
  47.5  0                  urgent_flag: False (Flag)
  47.4  1                  acknowledgement_flag: True (Flag)
  47.3  1                  psh_flag: True (Flag)
  47.2  0                  rst_flag: False (Flag)
  47.1  0                  syn_flag: False (Flag)
  47.0  0                  fin_flag: False (Flag)
  48 \x72              window: 29200 (UBInt16)
  49 \x10
  50 \x15              checksum: 5480 (UBInt16)
  51 \x68
  52 \x00              urgent_pointer: 0 (UBInt16)
  53 \x00
   -    -              options: Array():
   -    -              [
  54 \x01                  @0: 16844810 (UBInt32)
  55 \x01
  56 \x08
  57 \x0a
  58 \x01                  @1: 24362240 (UBInt32)
  59 \x73
  60 \xbd
  61 \x00
  62 \x13                  @2: 332846447 (UBInt32)
  63 \xd6
  64 \xd5
  65 \x6f
   -    -              ]
   -    -              payload: HTTPPayload():
  66 \x47                'G'
  67 \x45                'E'
  68 \x54                'T'
  69 \x20                ' '
  70 \x2f                '/'
  71 \x20                ' '
  72 \x48                'H'
  73 \x54                'T'
  74 \x54                'T'
  75 \x50                'P'
  76 \x2f                '/'
  77 \x31                '1'
  78 \x2e                '.'
  79 \x31                '1'
 %< %< %< %< %< %< %<
 800 \x0d                '\r'
 801 \x0a                '\n'
 802 \x0d                '\r'
 803 \x0a                '\n'

>>> binary = frame.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26| 27| 28| 29| 30| 31| 32| 33| 34| 35| 36| 37| 38| 39| 40| 41| 42| 43| 44| 45| 46| 47| 48| 49| 50| 51| 52| 53| 54| 55| 56| 57| 58| 59| 60| 61| 62| 63| 64| 65| 66| 67| 68| 69| 70| 71| 72| 73| 74| 75| 76| 77| 78| 79| 80| 81| 82| 83| 84| 85| 86| 87| 88| 89| 90| 91| 92| 93| 94| 95| 96| 97| 98| 99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255|256|257|258|259|260|261|262|263|264|265|266|267|268|269|270|271|272|273|274|275|276|277|278|279|280|281|282|283|284|285|286|287|288|289|290|291|292|293|294|295|296|297|298|299|300|301|302|303|304|305|306|307|308|309|310|311|312|313|314|315|316|317|318|319|320|321|322|323|324|325|326|327|328|329|330|331|332|333|334|335|336|337|338|339|340|341|342|343|344|345|346|347|348|349|350|351|352|353|354|355|356|357|358|359|360|361|362|363|364|365|366|367|368|369|370|371|372|373|374|375|376|377|378|379|380|381|382|383|384|385|386|387|388|389|390|391|392|393|394|395|396|397|398|399|400|401|402|403|404|405|406|407|408|409|410|411|412|413|414|415|416|417|418|419|420|421|422|423|424|425|426|427|428|429|430|431|432|433|434|435|436|437|438|439|440|441|442|443|444|445|446|447|448|449|450|451|452|453|454|455|456|457|458|459|460|461|462|463|464|465|466|467|468|469|470|471|472|473|474|475|476|477|478|479|480|481|482|483|484|485|486|487|488|489|490|491|492|493|494|495|496|497|498|499|500|501|502|503|504|505|506|507|508|509|510|511|512|513|514|515|516|517|518|519|520|521|522|523|524|525|526|527|528|529|530|531|532|533|534|535|536|537|538|539|540|541|542|543|544|545|546|547|548|549|550|551|552|553|554|555|556|557|558|559|560|561|562|563|564|565|566|567|568|569|570|571|572|573|574|575|576|577|578|579|580|581|582|583|584|585|586|587|588|589|590|591|592|593|594|595|596|597|598|599|600|601|602|603|604|605|606|607|608|609|610|611|612|613|614|615|616|617|618|619|620|621|622|623|624|625|626|627|628|629|630|631|632|633|634|635|636|637|638|639|640|641|642|643|644|645|646|647|648|649|650|651|652|653|654|655|656|657|658|659|660|661|662|663|664|665|666|667|668|669|670|671|672|673|674|675|676|677|678|679|680|681|682|683|684|685|686|687|688|689|690|691|692|693|694|695|696|697|698|699|700|701|702|703|704|705|706|707|708|709|710|711|712|713|714|715|716|717|718|719|720|721|722|723|724|725|726|727|728|729|730|731|732|733|734|735|736|737|738|739|740|741|742|743|744|745|746|747|748|749|750|751|752|753|754|755|756|757|758|759|760|761|762|763|764|765|766|767|768|769|770|771|772|773|774|775|776|777|778|779|780|781|782|783|784|785|786|787|788|789|790|791|792|793|794|795|796|797|798|799|800|801|802|803
\xf4\xec\x38\xc9\xb8\x54\x38\x60\x77\x13\xfa\x07\x08\x00\x45\x00\x03\x16\x2a\xc7\x40\x00\x40\x06\xfa\xbb\xc0\xa8\x00\x03\xc1\x63\x90\x50\xac\x2f\x00\x50\x62\xb7\x93\x8d\x1e\x3d\xe4\xbb\x80\x18\x72\x10\x15\x68\x00\x00\x01\x01\x08\x0a\x01\x73\xbd\x00\x13\xd6\xd5\x6f\x47\x45\x54\x20\x2f\x20\x48\x54\x54\x50\x2f\x31\x2e\x31\x0d\x0a\x48\x6f\x73\x74\x3a\x20\x68\x65\x69\x73\x65\x2e\x64\x65\x0d\x0a\x43\x6f\x6e\x6e\x65\x63\x74\x69\x6f\x6e\x3a\x20\x6b\x65\x65\x70\x2d\x61\x6c\x69\x76\x65\x0d\x0a\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x58\x31\x31\x3b\x20\x4c\x69\x6e\x75\x78\x20\x78\x38\x36\x5f\x36\x34\x29\x20\x41\x70\x70\x6c\x65\x57\x65\x62\x4b\x69\x74\x2f\x35\x33\x35\x2e\x31\x39\x20\x28\x4b\x48\x54\x4d\x4c\x2c\x20\x6c\x69\x6b\x65\x20\x47\x65\x63\x6b\x6f\x29\x20\x43\x68\x72\x6f\x6d\x65\x2f\x31\x38\x2e\x30\x2e\x31\x30\x32\x35\x2e\x31\x36\x32\x20\x53\x61\x66\x61\x72\x69\x2f\x35\x33\x35\x2e\x31\x39\x0d\x0a\x41\x63\x63\x65\x70\x74\x3a\x20\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x2c\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x68\x74\x6d\x6c\x2b\x78\x6d\x6c\x2c\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x6d\x6c\x3b\x71\x3d\x30\x2e\x39\x2c\x2a\x2f\x2a\x3b\x71\x3d\x30\x2e\x38\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x45\x6e\x63\x6f\x64\x69\x6e\x67\x3a\x20\x67\x7a\x69\x70\x2c\x64\x65\x66\x6c\x61\x74\x65\x2c\x73\x64\x63\x68\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x4c\x61\x6e\x67\x75\x61\x67\x65\x3a\x20\x64\x65\x2d\x44\x45\x2c\x64\x65\x3b\x71\x3d\x30\x2e\x38\x2c\x65\x6e\x2d\x55\x53\x3b\x71\x3d\x30\x2e\x36\x2c\x65\x6e\x3b\x71\x3d\x30\x2e\x34\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x43\x68\x61\x72\x73\x65\x74\x3a\x20\x49\x53\x4f\x2d\x38\x38\x35\x39\x2d\x31\x2c\x75\x74\x66\x2d\x38\x3b\x71\x3d\x30\x2e\x37\x2c\x2a\x3b\x71\x3d\x30\x2e\x33\x0d\x0a\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x77\x74\x5f\x76\x74\x3d\x37\x76\x30\x30\x30\x25\x33\x42\x36\x76\x33\x35\x74\x31\x34\x30\x39\x35\x39\x37\x34\x33\x35\x37\x31\x38\x3b\x20\x77\x74\x33\x5f\x65\x69\x64\x3d\x25\x33\x42\x32\x38\x38\x36\x38\x39\x36\x33\x36\x39\x32\x30\x31\x37\x34\x25\x37\x43\x32\x31\x34\x32\x35\x31\x31\x37\x32\x37\x32\x30\x30\x32\x32\x30\x33\x39\x31\x3b\x20\x6f\x70\x74\x69\x6d\x69\x7a\x65\x6c\x79\x45\x6e\x64\x55\x73\x65\x72\x49\x64\x3d\x6f\x65\x75\x31\x34\x32\x38\x37\x35\x37\x32\x31\x37\x35\x34\x39\x72\x30\x2e\x39\x38\x37\x30\x38\x34\x31\x30\x34\x39\x31\x32\x33\x35\x35\x35\x3b\x20\x6f\x70\x74\x69\x6d\x69\x7a\x65\x6c\x79\x53\x65\x67\x6d\x65\x6e\x74\x73\x3d\x25\x37\x42\x25\x32\x32\x32\x32\x38\x36\x32\x35\x30\x38\x35\x38\x25\x32\x32\x25\x33\x41\x25\x32\x32\x67\x63\x25\x32\x32\x25\x32\x43\x25\x32\x32\x32\x33\x30\x31\x31\x31\x30\x37\x33\x32\x25\x32\x32\x25\x33\x41\x25\x32\x32\x66\x61\x6c\x73\x65\x25\x32\x32\x25\x32\x43\x25\x32\x32\x32\x33\x31\x36\x32\x35\x30\x32\x36\x34\x25\x32\x32\x25\x33\x41\x25\x32\x32\x73\x65\x61\x72\x63\x68\x25\x32\x32\x25\x37\x44\x3b\x20\x6f\x70\x74\x69\x6d\x69\x7a\x65\x6c\x79\x42\x75\x63\x6b\x65\x74\x73\x3d\x25\x37\x42\x25\x32\x32\x32\x37\x34\x35\x38\x32\x30\x34\x30\x39\x25\x32\x32\x25\x33\x41\x25\x32\x32\x30\x25\x32\x32\x25\x37\x44\x3b\x20\x77\x74\x33\x5f\x73\x69\x64\x3d\x25\x33\x42\x32\x38\x38\x36\x38\x39\x36\x33\x36\x39\x32\x30\x31\x37\x34\x0d\x0a\x0d\x0a
>>> print(len(binary))
804

Access Members By Name

To access members by name call getattr() to get the member and call oser.ByteType.set() on the result.

>>> from oser import ByteStruct, ULInt8

>>> class Foo(ByteStruct):
...     def __init__(self):
...         super(Foo, self).__init__()
...         self.a = ULInt8(1)
...
>>> foo = Foo()
>>> name = "a"
>>> getattr(foo, name).set(24)
>>> print(foo.introspect())
   -    -  Foo():
   0 \x18      a: 24 (ULInt8)

>>> print(foo.a == 24)
True