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 let’s you create this mapping easily:

>>> from oser import ByteStruct
>>> from oser import UBInt8, BFloat
>>> from oser import RegularExpressionMatch
>>> from oser import Array
>>> from oser import CRCB32
>>> from oser import to_hex
>>>
>>> class EEPROM(ByteStruct):
...     def __init__(self, *args, **kwargs):
...         ByteStruct.__init__(self, *args, **kwargs)
...
...         self.type = UBInt8(0)
...         self.serial_number = RegularExpressionMatch(pattern="[A-Z]{4}-[0-9]{4}", length=9, value="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("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 realizies a frame for this protocol:

>>> from oser import ByteStruct
>>> from oser import Constant
>>> from oser import UBInt8, BFloat, UBInt16, UBInt32
>>> from oser import Enum
>>> from oser import Array
>>> from oser import Switch
>>> from oser import String
>>> from oser import CRCB32
>>> from oser import to_hex

>>> 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="", context_data=""):
...         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: 'abcdefghij'
    crc: 1472812111 (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: 1472812111 (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: 575351022 (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: 575351022 (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: 10 (UBInt16)
    payload: Payload():
        type: 'f32list' (UBInt32)
        length: 2 (UBInt16)
        data: Array():
        [
            @0: 0.0 (UBInt16)
            @1: 1.5 (UBInt16)
        ]
    crc: 3834901926 (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
\x23\x00\x00\x00\x00\x00\x00\x05\x39\x00\x0A\x00\x00\x00\x02\x00\x02\x00\x00\x00\x01\xE4\x93\xF5\xA6
>>> 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: 10 (UBInt16)
  10 \x0a
   -    -      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 (UBInt16)
  18 \x00
  19 \x00              @1: 1.5 (UBInt16)
  20 \x01
   -    -          ]
  21 \xe4      crc: 3834901926 (CRCB32)
  22 \x93
  23 \xf5
  24 \xa6

>>> #decode data
... data = "\x23\x00\x00\x00\x00\x00\x00\x05\x39\x00\x1A\x00\x00\x00\x02\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xA4\x58\x03\x64"
>>> frame.decode(data)
>>> 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: 2757231460 (CRCB32)

   -    -  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: 2757231460 (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 x: hex(ord(x))[2:].upper(), 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 = "f4ec38c9b85438607713fa070800450003162ac740004006fabbc0a80003c1639050ac2f005062b7938d1e3de4bb80187210156800000101080a0173bd0013d6d56f474554202f20485454502f312e310d0a486f73743a2068656973652e64650d0a436f6e6e656374696f6e3a206b6565702d616c6976650d0a557365722d4167656e743a204d6f7a696c6c612f352e3020285831313b204c696e7578207838365f363429204170706c655765624b69742f3533352e313920284b48544d4c2c206c696b65204765636b6f29204368726f6d652f31382e302e313032352e313632205361666172692f3533352e31390d0a4163636570743a20746578742f68746d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c69636174696f6e2f786d6c3b713d302e392c2a2f2a3b713d302e380d0a4163636570742d456e636f64696e673a20677a69702c6465666c6174652c736463680d0a4163636570742d4c616e67756167653a2064652d44452c64653b713d302e382c656e2d55533b713d302e362c656e3b713d302e340d0a4163636570742d436861727365743a2049534f2d383835392d312c7574662d383b713d302e372c2a3b713d302e330d0a436f6f6b69653a2077745f76743d37763030302533423676333574313430393539373433353731383b207774335f6569643d253342323838363839363336393230313734253743323134323531313732373230303232303339313b206f7074696d697a656c79456e645573657249643d6f65753134323837353732313735343972302e393837303834313034393132333535353b206f7074696d697a656c795365676d656e74733d2537422532323232383632353038353825323225334125323267632532322532432532323233303131313037333225323225334125323266616c7365253232253243253232323331363235303236342532322533412532327365617263682532322537443b206f7074696d697a656c794275636b6574733d25374225323232373435383230343039253232253341253232302532322537443b207774335f7369643d2533423238383638393633363932303137340d0a0d0a"
>>> data = data.decode("hex")
>>> frame.decode(data)
804
>>> print(frame.introspect())
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)
            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: 0 (Flag)
                acknowledgement_flag: 1 (Flag)
                psh_flag: 1 (Flag)
                rst_flag: 0 (Flag)
                syn_flag: 0 (Flag)
                fin_flag: 0 (Flag)
            window: 29200 (UBInt16)
            checksum: 5480 (UBInt16)
            urgent_pointer: 0 (UBInt16)
            options: Array():
            [
                @0: 16844810 (UBInt32)
                @1: 24362240 (UBInt32)
                @2: 332846447 (UBInt32)
            ]
            payload:
GET / HTTP/1.1
Host: heise.de
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: 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



>>> print(frame.introspect())
   -    -  EthernetFrame():
   -    -      destination: MacAddress():
   0 \xf4          '\xf4'
   1 \xec          '\xec'
   2 \x38          '8'
   3 \xc9          '\xc9'
   4 \xb8          '\xb8'
   5 \x54          'T'
   -    -      source: MacAddress():
   6 \x38          '8'
   7 \x60          '`'
   8 \x77          'w'
   9 \x13          '\x13'
  10 \xfa          '\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
  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: 0 (Flag)
  47.4  1                  acknowledgement_flag: 1 (Flag)
  47.3  1                  psh_flag: 1 (Flag)
  47.2  0                  rst_flag: 0 (Flag)
  47.1  0                  syn_flag: 0 (Flag)
  47.0  0                  fin_flag: 0 (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 __future__ import \
...     absolute_import, division, print_function, unicode_literals

>>> 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