Master¶
- class icspacket.proto.dnp3.master.DNP3_Task[source]¶
Abstract base class for a DNP3 task.
A DNP3 task represents a unit of work that the Master initiates, such as sending a request and handling its response. Each task is responsible for preparing an APDU for transmission and for processing the received APDU messages once a response arrives.
Subclasses implement specific task behavior, such as blocking until a response is received or executing a callback when a response is available.
- Variables:
sequence (int) – Application layer sequence number associated with the task. Initialized to
-1and updated when the APDU is transmitted.
- prepare_transmit(master: DNP3_Master) APDU[source]¶
Prepare the APDU for transmission.
Subclasses must implement this to return the request APDU that will be sent to the outstation.
- Parameters:
master (DNP3_Master) – The DNP3 master instance initiating the transmission.
- Returns:
The APDU to transmit.
- Return type:
- Raises:
NotImplementedError – If the method is not overridden in a subclass.
- on_message(master: DNP3_Master, message: APDU) None[source]¶
Handle a received APDU message.
This is called when the Master receives a response APDU for the task. The default implementation does nothing.
- Parameters:
master (DNP3_Master) – The DNP3 master instance handling the message.
message (APDU) – The received APDU response.
- class icspacket.proto.dnp3.master.DNP3_Link(src: int, dst: int, sock: socket | None = None, mode: LinkDirection = LinkDirection.MASTER)[source]¶
Implements the DNP3 Link Layer.
The Link Layer provides reliable delivery of Link Protocol Data Units (LPDUs) between a DNP3 master and outstation over a transport medium such as TCP. This class is responsible for:
Encapsulation of octet streams into LPDUs.
Validation of source/destination addresses.
Handling link-layer function codes (e.g., UNCONFIRMED_USER_DATA, REQUEST_LINK_STATUS, LINK_STATUS, NOT_SUPPORTED).
Forwarding valid user data to the upper layer.
The class maintains an internal input queue of received LPDUs that have passed validation and are intended for the upper layers.
- Parameters:
src (int) – The source link-layer address of this endpoint.
dst (int) – The expected destination address of the remote peer.
sock (socket.socket | None) – Optional pre-initialized TCP socket. If
None, a new socket will be created.mode (LinkDirection) – Link direction, either MASTER or OUTSTATION.
- property in_queue: Queue[LPDU]¶
Queue of validated inbound LPDUs.
Only LPDUs that pass address and direction checks are placed into this queue for higher-layer consumption.
- Return type:
Queue[LPDU]
- property mode: LinkDirection¶
Link direction of this endpoint.
- Return type:
- property source: int¶
The source address of this endpoint.
- Return type:
int
- property destination: int¶
The expected destination address of the peer.
- Return type:
int
- send_data(octets: bytes, /) None[source]¶
Send user data as an unconfirmed LPDU.
The octets are wrapped into a Link Layer frame with the function code UNCONFIRMED_USER_DATA and transmitted.
- Parameters:
octets (bytes) – The application/user data to send.
- Raises:
ConnectionError – If the socket is not connected.
- send_lpdu(lpdu: LPDU) None[source]¶
Send a fully constructed LPDU.
This method sets the LPDU’s source and destination before serializing and transmitting it over the socket.
- Parameters:
lpdu (LPDU) – The LPDU to send.
- Raises:
ConnectionError – If the socket is not connected.
- recv_data() bytes[source]¶
Receive raw LPDU octets from the socket.
Reads up to 292 bytes, which is the maximum LPDU size (per Link Layer specification, length field max 255 plus headers).
- Returns:
Raw bytes read from the socket.
- Return type:
bytes
- Raises:
ConnectionError – If the socket is not connected.
- send_link_status(request: LPDU) None[source]¶
Send a link status response.
Constructs an LPDU with function code LINK_STATUS and transmits it.
- Parameters:
request (LPDU) – The received REQUEST_LINK_STATUS LPDU.
- Raises:
ConnectionError – If the socket is not connected.
Changed in version 0.2.2: Added
requestparameter.
- send_not_supported() None[source]¶
Send a ‘Not Supported’ response.
Constructs an LPDU with function code NOT_SUPPORTED and transmits it.
- Raises:
ConnectionError – If the socket is not connected.
- recv_lpdu() LPDU[source]¶
Receive and parse the next valid LPDU.
This method handles fragmented reception (multiple LPDUs in one read) and incomplete frames. Valid frames that pass link validation are placed in the inbound queue.
- Returns:
The next valid LPDU for the upper layer.
- Return type:
- Raises:
ConnectionError – If the socket is not connected.
- class icspacket.proto.dnp3.master.DNP3_Transport(link: DNP3_Link, unsolicited_callback: Callable[[APDU], None] | None = None)[source]¶
DNP3 Transport Layer implementation.
This class implements the transport layer of the DNP3 protocol, providing fragmentation and reassembly of Application Protocol Data Units (APDUs) into Transport Protocol Data Units (TPDUs). It sits above the Link Layer (
DNP3_Link) and ensures that application data is transmitted in compliance with the transport rules defined in the DNP3 standard.Key responsibilities of this layer include:
Fragmenting application data into transport segments (1-249 octets each).
Wrapping each fragment into a TPDU with proper sequence numbering.
Reassembling received TPDUs into complete APDUs.
Detecting and handling unsolicited responses, optionally invoking a user-provided callback.
- Parameters:
- connect(address: tuple[str, int]) None[source]¶
Establish a connection through the link layer.
This method delegates the connection setup to the underlying
DNP3_Linkobject, updating the transport connection state.- Parameters:
address (tuple[str, int]) – A tuple containing the IP address and port number of the outstation.
- close() None[source]¶
Close the transport connection.
This method closes the link layer connection and updates the internal connection status.
- next_sequence() int[source]¶
Get the next transport sequence number.
The transport layer maintains an internal 6-bit sequence counter used for ordering TPDUs. After reaching the maximum value, the counter wraps around to zero.
- Returns:
The current transport sequence number before incrementing.
- Return type:
int
- send_data(octets: bytes, /) None[source]¶
Send application data through the transport layer.
This method fragments the given application data into one or more TPDUs (each up to 249 octets). Each TPDU is assigned sequence numbers and FIR/FIN flags. The fragments are then transmitted via the link layer.
- Parameters:
octets (bytes) – The application data bytes to transmit.
- Raises:
ConnectionError – If the transport connection is not established.
- recv_data() bytes[source]¶
Receive and reassemble application data from the transport layer.
This method collects one or more TPDUs received from the link layer and reconstructs the original APDU. It validates transport sequence numbers to ensure proper ordering. Unsolicited responses are detected and optionally passed to the callback without being returned to the caller.
- Returns:
The fully reassembled APDU bytes.
- Return type:
bytes
- Raises:
ConnectionError – If the transport connection is not established.
- class icspacket.proto.dnp3.master.DNP3_Master(link_addr: int, initial_seq: int | None = None, sock: socket = None)[source]¶
DNP3 Master (Application Layer interface).
This class provides the master-side interface of the DNP3 protocol stack, built on top of the transport and link layers. It manages request/response exchanges with an outstation, schedules tasks, and assigns application sequence numbers. The master does not implement actual application logic — instead, it delegates handling to user-defined
DNP3_Taskobjects.This implementation supports:
Establishing and releasing associations with outstations.
Submitting tasks that generate and transmit APDUs.
Assigning and cycling application sequence numbers.
Supporting both blocking and non-blocking request patterns.
Optionally dispatching requests without expecting a return (
submit_noreturn()).
Example:
master = DNP3_Master(link_addr=0x0001) # connect to remote at 127.0.0.1:20000 with link addr 1024 master.associate((1024, "127.0.0.1", 20000)) # request class 1 objects objects = new_class_data_request(1) result = master.request(FunctionCode.READ, objects)
- Parameters:
link_addr (int) – The source link-layer address of the master.
initial_seq (int | None) – Optional initial application sequence number. Defaults to
0if not provided.sock (socket.socket | None) – Optional socket to bind the link layer to. If omitted, a new socket will be created when associating with an outstation.
- property tasks: Collection[DNP3_Task]¶
Return the currently registered tasks.
- Returns:
A collection of active tasks indexed by sequence number.
- Return type:
Collection[DNP3_Task]
- property transport: DNP3_Transport¶
Return the transport layer instance.
- Returns:
The active
DNP3_Transportobject.- Return type:
- property sequence: int¶
Return the current application sequence number.
The sequence number is incremented and wrapped automatically whenever a request is submitted.
- Returns:
The current APDU sequence number.
- Return type:
int
- associate(address: tuple[int, str, int], link_cls: type[DNP3_Link] | None = None) None[source]¶
Establish an association with an outstation.
This method sets up the underlying link and transport layers, connects to the given host/port, and starts background processing of incoming messages.
- Parameters:
- Raises:
ValueError – If the provided address tuple is invalid.
Changed in version 0.2.2: Added
link_clsparameter.
- release(timeout: float | None = None) None[source]¶
Release the association with the outstation.
This method stops the background thread, closes the transport connection, and invalidates the master instance.
- Parameters:
timeout (float | None) – Optional timeout (in seconds) to wait for the background thread to join.
- submit_task(task: DNP3_Task) None[source]¶
Submit a task and register it for response handling.
The task will be assigned the current application sequence number, which is then incremented. The request APDU is built by the task and transmitted through the transport layer.
- Parameters:
task (DNP3_Task) – The task to be submitted.
- Raises:
ConnectionError – If the task fails to produce a valid APDU.
- submit_noreturn(task: DNP3_Task) None[source]¶
Submit a task without expecting a response.
Unlike
submit_task(), the task is not registered for later lookup, meaning no response will be matched or delivered.- Parameters:
task (DNP3_Task) – The task to be submitted.
- Raises:
ConnectionError – If the task fails to produce a valid APDU.
- transmit(request: APDU, block: bool = True, callback: Callable[[DNP3_Master, APDU], None] | None = None, noreturn: bool = False) APDU | None[source]¶
Transmit a request APDU to the outstation.
This method provides both blocking and non-blocking transmission modes. In blocking mode, it waits for the corresponding response before returning. In non-blocking mode, the provided callback will be invoked when the response is received.
- Parameters:
request (APDU) – The APDU to transmit.
block (bool) – Whether to wait for the response. Defaults to
True.callback (Callable[["DNP3_Master", APDU], None] | None) – Optional callback to invoke with the response in non-blocking mode.
noreturn (bool) – If
True, the request is sent without expecting a response.
- Returns:
The received APDU if in blocking mode, otherwise
None.- Return type:
APDU | None
- request(function: FunctionCode, objects: DNP3Objects | None = None, need_confirm: bool = False, block: bool = True, callback: Callable[[DNP3_Master, APDU], None] | None = None, noreturn: bool = False) APDU | None[source]¶
Build and transmit a request APDU.
This is the high-level entry point for sending function code requests to an outstation. Optionally, objects can be included, confirmation can be requested, and the call may be synchronous or asynchronous.
- Parameters:
function (FunctionCode) – The DNP3 application function code.
objects (DNP3Objects | None) – Optional collection of DNP3 objects to include.
need_confirm (bool) – Whether a confirmation is required.
block (bool) – Whether to wait for the response.
callback (Callable[["DNP3_Master", APDU], None] | None) – Callback to invoke in asynchronous mode.
noreturn (bool) – If
True, send without expecting a response.
- Returns:
The APDU response in blocking mode, otherwise
None.- Return type:
APDU | None
- Raises:
ValueError – If both
need_confirmandnoreturnare set.