DUL Service Provider (dulprovider)

This module implements the DUL service provider, allowing a DUL service user to send and receive DUL messages (PDUs). The User and Provider talk to each other using a TCP socket. The DULServer runs in a thread, polling TCP socket for incoming messages and sending messages from user queue. Underlying logic of the service is implemented via state machine that is described in DICOM standard.

In most of the cases you would not need to access DULServiceProvider directly, but rather would use higher level objects like sub-classes of Association or various services.

pynetdicom2.dulprovider.CLOSE_TIMEOUT: float = 10

Maximum time (in seconds) to wait for the remote peer to close its side of the connection during DULServiceProvider._close(). Without a bound a half-open or misbehaving peer would keep the association in STA_13 indefinitely. The wait is performed in small non-blocking steps so the event loop keeps running while waiting.

pynetdicom2.dulprovider.MAX_ASSOCIATION_PDU_LENGTH = 65536

Upper bound (in bytes) for the body of received association PDUs. Association PDUs are exchanged before the Maximum Length negotiation takes effect, so a fixed limit is applied: 64 KiB comfortably accommodates the largest sane association PDU (e.g. one carrying 128 presentation contexts) while still protecting the provider from a peer that declares an absurd length (up to 4 GiB) in order to exhaust memory.

pynetdicom2.dulprovider.ASSOCIATION_PDU_TYPES = frozenset({1, 2, 3})

PDU type bytes of the association PDUs: A-ASSOCIATE-RQ, A-ASSOCIATE-AC and A-ASSOCIATE-RJ.

pynetdicom2.dulprovider.POLL_INTERVAL = 0.05

Interval (in seconds) at which the DUL event loop polls the socket for incoming data. The same interval is used to pace the loop while no socket exists so an idle provider does not spin at 100% CPU.

pynetdicom2.dulprovider.DEFAULT_MAX_PDU_LENGTH = 65536

Default maximum PDU length (in bytes) used when no value is negotiated or provided by the caller.

pynetdicom2.dulprovider.SOCKET_TIMEOUT = 30

Timeout (in seconds) applied to all operations on the DUL socket. Without a bound a stalled peer (e.g. one that has stopped reading) would block sends indefinitely, hanging the DUL thread and deadlocking kill().

pynetdicom2.dulprovider.KILL_TIMEOUT = 5

Maximum time (in seconds) DULServiceProvider.kill() waits for the DUL thread to terminate.

class pynetdicom2.dulprovider.DULServiceProvider(store_in_file: set[UID], get_file_cb: Callable[[PContextDef, Dataset], tuple[BinaryIO, int]], dul_socket: socket | None = None, max_pdu_length: int = 65536, artim_timeout: int = 10)

Implements DUL service.

This class is responsible for low-level operations with incoming and outgoing PDUs.

Service can be initialized by providing open socket that service would use for sending and receiving PDUs. In case the socket is not provided service opens a client socket by itself when sending AAssociateRqPDU instance.

Underlying implementation relies on state machine that is defined in Service State Machine (fsm)

Variables:
  • primitive – current PDU

  • dimse_gen – generator, used break current outgoing DIMSE message into P-DATA-TF PDUs

  • event – current event

  • max_pdu_length – maximum PDU length for incoming P-DATA-TF PDUs

  • to_service_user – outgoing data queue

  • from_service_user – incoming data queue

  • dul_socket – socket, that service uses

  • is_killed – DUL service termination flag

property accepted_contexts: dict[int, PContextDef]

Accepted presentation contexts in the current association

create_socket() None

Creates a client socket and establishes a connection.

The connection attempt is bounded by SOCKET_TIMEOUT and no socket is leaked if the connection cannot be established.

send(primitive: Iterator[PDataTfPDU] | AAssociatePDUBase | AAssociateRqPDU | AAssociateAcPDU | AAssociateRjPDU | PDataTfPDU | AReleasePDUBase | AReleaseRqPDU | AReleaseRpPDU | AAbortPDU) None

Puts PDU into outgoing queue.

Note

PDU is not immediately written into the socket, but rather put into queue that is processed by the service event loop.

Parameters:

primitive – outgoing PDU. Possible PDU types are described in PDU Types (pdu)

receive(timeout: float) tuple[DIMSEMessage, int] | AAssociatePDUBase | AAssociateRqPDU | AAssociateAcPDU | AAssociateRjPDU | PDataTfPDU | AReleasePDUBase | AReleaseRqPDU | AReleaseRpPDU | AAbortPDU

Tries to get PDU from incoming queue.

If timeout is exceeded method raises DCMTimeoutError exception.

Parameters:

timeout – the amount of seconds method waits for PDU to appear in incoming queue

Returns:

PDU instance or a tuple containing DIMSE Message and Presentation Context ID. Possible PDU types are described in PDU Types (pdu). Possible DIMSE messages are described in DIMSE Messages (dimsemessages).

Raises:

exceptions.DCMTimeoutError – If specified timeout is exceeded

stop() bool

Tries to stop service for idle association.

If association is not in idle state, method will return False and association will not be stopped.

Returns:

True if service termination flag was successfully set (current association state was ‘idle’), False otherwise

kill() None

Sets termination flag for event loop and waits for thread to exit.

Waits at most KILL_TIMEOUT seconds: if a socket operation is blocked on a stalled peer the thread may take a little longer to wind down, in which case it is left to finish on its own rather than blocking the caller indefinitely.

run() None

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.