Encoding / Decoding

This module implements parsing and serialization of DNP3 Application Layer objects as defined in section 4.2.2.7 Object Headers of the DNP3 specification.

In DNP3, object headers describe data structures that cannot be conveyed through the application header alone. Each object header specifies:

  • Group: The type of data (e.g., binary inputs, analog outputs).

  • Variation: The representation of the data (e.g., packed, 16-bit, 32-bit).

  • Qualifier fields: Indicate how many objects follow and what range or prefixing mechanism is applied.

  • Objects: The actual data values.

Example: Class0123 Request

# no prefix and no range needed here
objects = DNP3Objects()
objects.add_emtpy(60, 1) # object 60, variation 1 (Class 0)
objects.add_emtpy(60, 2) # object 60, variation 2 (Class 1)
objects.add_emtpy(60, 3) # object 60, variation 3 (Class 2)
objects.add_emtpy(60, 4) # object 60, variation 4 (Class 3)

encoded = pack_objects(objects)

# ...
class icspacket.proto.dnp3.objects.coding.ObjectHeader(group: int, variation: int, reserved: int = 0, obj_prefix: ObjectPrefixCode = ObjectPrefixCode.NONE, range_spec: RangeSpecifierCode = RangeSpecifierCode.NONE)[source]

Represents a DNP3 Object Header.

Object headers provide additional context about a set of objects transmitted in the Application Layer. They define the group, variation, and addressing/range information for associated data objects.

group: int

/4.2.2.7.1 Object group The object group specifies what data type or values are included in a master request or in an outstation response.

variation: int

/4.2.2.7.2 Object variation Specifies the data format of DNP3 objects. The object group and object variation together uniquely specify the structure of a DNP3 object and the type of data that it references.

obj_prefix: ObjectPrefixCode = 0

/4.2.2.7.3.2 Object prefix code Specifies what, if any, prefix value appears before each of the DNP3 objects that follow the object header. Prefixes are either an index number or an object size.

range_spec: RangeSpecifierCode = 6

/4.2.2.7.3.3 Range specifier codes

class icspacket.proto.dnp3.objects.coding.DNP3Object(prefix: int | None, index: int, instance: Any)[source]

Represents a single transmitted DNP3 object instance.

A DNP3 object may optionally include a prefix (such as an index), followed by its data value.

prefix: int | None

Parsed prefix value (if present). This may represent an index or size.

index: int

Sequential index of this object within its variation.

instance: Any

The parsed data payload for this object (actual value).

class icspacket.proto.dnp3.objects.coding.DNP3ObjectVariations[source]

Represents all objects of a given variation within a group.

Extends list[DNP3Object] to hold multiple instances of the same group/variation. Additional metadata about range and prefixing is stored here for proper serialization.

>>> v = DNP3ObjectVariations() # generic interface
>>> v.add(DNP3ObjectG2V1(state=1))
range_type: RangeSpecifierCode

Indicates the range encoding used for this variation (count or start/end).

range: tuple[int, int] | int | None

The decoded range value. May be a tuple (start, stop) for index ranges, an integer count, or None if not specified.

prefix_type: ObjectPrefixCode | None

Prefix encoding used for the objects (e.g., none, 1-byte, 2-byte index).

get_range() tuple[int, int] | int | None[source]

Compute the effective range of objects for this variation.

If an explicit range is already set, it is returned. Otherwise, derives the range based on the range type.

Returns:

The start/end tuple, object count, or None.

Return type:

tuple[int, int] | int | None

add(value: Any, /, prefix: int | None = None, index: int | None = None) None[source]

Add a new object instance to this variation.

Parameters:
  • value (Any) – The parsed object payload.

  • prefix (int | None) – Optional prefix (e.g., index) associated with this object.

  • index (int | None) – Explicit index. Defaults to sequential numbering.

class icspacket.proto.dnp3.objects.coding.DNP3Objects[source]

A container for all DNP3 objects in a fragment.

Maps group numbers to variations, which then contain one or more object instances.

Structure:

DNP3Objects[group][variation] -> DNP3ObjectVariations
get_variation(group: int, variation: int, /) DNP3ObjectVariations | None[source]

Retrieve or create a variation container for the given group/variation.

Parameters:
  • group (int) – DNP3 object group number.

  • variation (int) – DNP3 object variation number.

Returns:

A variation container if known, otherwise None.

Return type:

DNP3ObjectVariations | None

icspacket.proto.dnp3.objects.coding.unpack_objects(object_data: bytes) DNP3Objects[source]

Parse raw DNP3 object data into structured representations.

Reads one or more object headers and associated objects from a byte sequence. Automatically handles range specifiers, prefix codes, and object variations.

Parameters:

object_data (bytes) – Encoded DNP3 object data.

Returns:

A structured mapping of groups, variations, and parsed objects.

Return type:

DNP3Objects

Raises:

ValueError – If an unknown object variation is encountered.

icspacket.proto.dnp3.objects.coding.pack_objects(objects: DNP3Objects, prefix_type: ObjectPrefixCode | None = None, range_type: RangeSpecifierCode | None = None) bytes[source]

Serialize structured DNP3 objects into raw bytes.

Iterates over all object groups and variations in the provided container, generating object headers, range/prefix encodings, and packed values.

Parameters:
  • objects (DNP3Objects) – Structured DNP3 objects to serialize.

  • prefix_type (ObjectPrefixCode | None) – Default prefix type to apply when none is specified.

  • range_type (RangeSpecifierCode | None) – Default range type to apply when none is specified.

Returns:

Encoded object data suitable for transmission.

Return type:

bytes

Raises:

ValueError – If an unknown object variation is encountered.