Ejemplo n.º 1
0
def AA_7(dul):
    """Association abort AA-7.

    If receive a association request or invalid PDU while waiting for connection
    to close, issue A-ABORT

    State-event triggers: Sta13 + Evt6/Evt19

    References
    ----------
    1. DICOM Standard 2015b, PS3.8, Table 9-7, "Associate Establishment
       Related Actions"

    Parameters
    ----------
    dul : pynetdicom.dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        Sta13, the next state of the state machine
    """
    # Send A-ABORT PDU.
    pdu = A_ABORT_RQ()
    pdu.source = 0x02
    pdu.reason_diagnostic = 0x02

    # Callback
    dul.assoc.acse.debug_send_abort(pdu)

    dul.socket.send(pdu.encode())

    return 'Sta13'
Ejemplo n.º 2
0
def AA_7(dul):
    """Association abort AA-7.

    If receive a association request or invalid PDU while waiting for
    connection to close, send A-ABORT PDU

    State-event triggers: Sta13 + Evt6/Evt19

    Parameters
    ----------
    dul : dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        ``'Sta13'``, the next state of the state machine
    """
    primitive = A_P_ABORT()
    primitive.provider_reason = 0x02

    # Send A-ABORT PDU.
    pdu = A_ABORT_RQ()
    pdu.from_primitive(primitive)

    dul.socket.send(dul.pdu.encode())
    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {'pdu': dul.pdu})

    return 'Sta13'
Ejemplo n.º 3
0
    def run(self):
        """
        The main threading.Thread run loop. Runs constantly, checking the
        connection for incoming data. When incoming data is received it
        categorises it and add its to the `to_user_queue`.
        """
        # Main DUL loop
        self._idle_timer.start()

        while True:
            # Let the assoc reactor off the leash
            if not self.assoc._dul_ready.is_set():
                self.assoc._dul_ready.set()

            # This effectively controls how quickly the DUL does anything
            time.sleep(self._run_loop_delay)

            if self._kill_thread:
                break

            # Check the ARTIM timer first so its event is placed on the queue
            #   ahead of any other events this loop
            if self.artim_timer.expired:
                self.event_queue.put('Evt18')

            # Check the connection for incoming data
            try:
                # We can either encode and send a primitive **OR**
                #   receive and decode a PDU per loop of the reactor
                if self._check_incoming_primitive():
                    pass
                elif self._is_transport_event():
                    self._idle_timer.restart()
            except Exception as exc:
                LOGGER.error("Exception in DUL.run(), aborting association")
                LOGGER.exception(exc)
                # Bypass the state machine and send an A-ABORT
                #   we do it this way because an exception here will mess up
                #   the state machine and we can't guarantee it'll get sent
                #   otherwise
                abort_pdu = A_ABORT_RQ()
                abort_pdu.source = 0x02
                abort_pdu.reason_diagnostic = 0x00
                self.socket.send(abort_pdu.encode())
                self.assoc.is_aborted = True
                self.assoc.is_established = False
                # Hard shutdown of the Association and DUL reactors
                self.assoc._kill = True
                self._kill_thread = True
                return

            # Check the event queue to see if there is anything to do
            try:
                event = self.event_queue.get(block=False)
            # If the queue is empty, return to the start of the loop
            except queue.Empty:
                continue

            self.state_machine.do_action(event)
Ejemplo n.º 4
0
    def test_conversion(self):
        """ Check conversion to a PDU produces the correct output """
        primitive = A_P_ABORT()
        primitive.provider_reason = 4

        pdu = A_ABORT_RQ()
        pdu.from_primitive(primitive)
        data = pdu.encode()

        assert data == b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x04"
Ejemplo n.º 5
0
    def test_conversion(self):
        """ Check conversion to a PDU produces the correct output """
        primitive = A_ABORT()
        primitive.abort_source = 0

        pdu = A_ABORT_RQ()
        pdu.from_primitive(primitive)
        data = pdu.encode()

        assert data == b"\x07\x00\x00\x00\x00\x04\x00\x00\x00\x00"
Ejemplo n.º 6
0
def AA_7(dul: "DULServiceProvider") -> str:
    """Association abort AA-7.

    If receive a association request or invalid PDU while waiting for
    connection to close, send A-ABORT PDU

    State-event triggers: Sta13 + Evt6/Evt19

    Parameters
    ----------
    dul : dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        ``'Sta13'``, the next state of the state machine
    """
    primitive = A_P_ABORT()
    primitive.provider_reason = 0x02

    # Send A-ABORT PDU
    dul._send(A_ABORT_RQ(primitive))

    return "Sta13"
Ejemplo n.º 7
0
def AA_1(dul):
    """Association abort AA-1.

    If on sending A-ASSOCIATE-RQ we receive an invalid reply, or an abort
    request then abort

    State-event triggers: Sta2 + Evt3/Evt4/Evt10/Evt12/Evt13/Evt19,
    Sta3/Sta5/Sta6/Sta7/Sta8/Sta9/Sta10/Sta11/Sta12 + Evt15

    Parameters
    ----------
    dul : dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        ``'Sta13'``, the next state of the state machine
    """
    # Send A-ABORT PDU (service-user source) and start (or restart
    # if already started) ARTIM timer.
    dul.pdu = A_ABORT_RQ()
    dul.pdu.source = 0x00
    # Reason not specified
    dul.pdu.reason_diagnostic = 0x00
    dul.pdu.from_primitive(dul.primitive)

    dul.socket.send(dul.pdu.encode())
    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {'pdu': dul.pdu})
    dul.artim_timer.restart()

    return 'Sta13'
Ejemplo n.º 8
0
def AA_8(dul):
    """Association abort AA-8.

    If receive invalid event, send A-ABORT, issue A-P-ABORT indication and start
    ARTIM timer

    State-event triggers: Evt3 + Sta3/6/7/8/9/10/11/12,
    Evt4 + Sta3/5/6/7/8/9/10/11/12, Evt6 + Sta3/5/6/7/8/9/10/11/12,
    Evt10 + Sta3/5/8/9/10/11/12, Evt12 + Sta3/5/8/9/10/11/12,
    Evt13 + Sta3/5/6/8/9/12, Evt19 + Sta3/5/6/7/8/9/10/11/12

    References
    ----------
    1. DICOM Standard 2015b, PS3.8, Table 9-7, "Associate Establishment
       Related Actions"

    Parameters
    ----------
    dul : pynetdicom.dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        Sta13, the next state of the state machine
    """
    # Send A-ABORT PDU (service-dul source), issue A-P-ABORT
    # indication, and start ARTIM timer.
    dul.pdu = A_ABORT_RQ()
    dul.pdu.source = 0x02
    dul.pdu.reason_diagnostic = 0x00

    dul.primitive = dul.pdu.to_primitive()
    dul.primitive.abort_source = 0x02
    dul.primitive.result = 0x01
    dul.primitive.diagnostic = 0x01

    if dul.scu_socket:
        # Callback
        dul.assoc.acse.debug_send_abort(dul.pdu)

        try:
            # Encode and send A-ABORT to peer
            dul.scu_socket.send(dul.pdu.encode())
        except socket.error:
            dul.scu_socket.close()
        except ConnectionResetError:
            dul.scu_socket.close()

        # Issue A-P-ABORT to user
        dul.to_user_queue.put(dul.primitive)
        dul.artim_timer.start()

    return 'Sta13'
Ejemplo n.º 9
0
def AA_8(dul: "DULServiceProvider") -> str:
    """Association abort AA-8.

    If receive invalid event, send A-ABORT PDU, issue A-P-ABORT indication and
    start ARTIM timer

    State-event triggers: Evt3 + Sta3/6/7/8/9/10/11/12,
    Evt4 + Sta3/5/6/7/8/9/10/11/12, Evt6 + Sta3/5/6/7/8/9/10/11/12,
    Evt10 + Sta3/5/8/9/10/11/12, Evt12 + Sta3/5/8/9/10/11/12,
    Evt13 + Sta3/5/6/8/9/12, Evt19 + Sta3/5/6/7/8/9/10/11/12

    Parameters
    ----------
    dul : dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        ``'Sta13'``, the next state of the state machine
    """
    # Send A-ABORT PDU (service-dul source), issue A-P-ABORT
    # indication, and start ARTIM timer.
    # Send A-ABORT PDU
    pdu = A_ABORT_RQ()
    pdu.source = 0x02  # A-P-ABORT
    pdu.reason_diagnostic = 0x00

    sock = cast("AssociationSocket", dul.socket)
    sock.send(pdu.encode())
    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {'pdu': pdu})

    # Issue A-P-ABORT to user
    primitive = A_P_ABORT()
    primitive.provider_reason = 0x05
    dul.to_user_queue.put(primitive)
    dul.artim_timer.start()

    return 'Sta13'
Ejemplo n.º 10
0
    def test__pdu_to_event(self):
        """Test that good PDU paramters return expected results"""
        dul = DummyDUL()
        p2e = dul._pdu_to_event

        pdu_types = [A_ASSOCIATE_RQ(), A_ASSOCIATE_AC(), A_ASSOCIATE_RJ(),
                     P_DATA_TF(), A_RELEASE_RQ(), A_RELEASE_RP(),
                     A_ABORT_RQ(), 'TEST']
        event_str = ['Evt6', 'Evt3', 'Evt4',
                     'Evt10', 'Evt12', 'Evt13',
                     'Evt16', 'Evt19']

        for pdu, evt in zip(pdu_types, event_str):
            assert p2e(pdu) == evt
Ejemplo n.º 11
0
def AA_8(dul):
    """Association abort AA-8.

    If receive invalid event, send A-ABORT, issue A-P-ABORT indication and
    start ARTIM timer

    State-event triggers: Evt3 + Sta3/6/7/8/9/10/11/12,
    Evt4 + Sta3/5/6/7/8/9/10/11/12, Evt6 + Sta3/5/6/7/8/9/10/11/12,
    Evt10 + Sta3/5/8/9/10/11/12, Evt12 + Sta3/5/8/9/10/11/12,
    Evt13 + Sta3/5/6/8/9/12, Evt19 + Sta3/5/6/7/8/9/10/11/12

    References
    ----------
    1. DICOM Standard 2015b, PS3.8, Table 9-7, "Associate Establishment
       Related Actions"

    Parameters
    ----------
    dul : pynetdicom.dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        Sta13, the next state of the state machine
    """
    # Send A-ABORT PDU (service-dul source), issue A-P-ABORT
    # indication, and start ARTIM timer.
    dul.pdu = A_ABORT_RQ()
    dul.pdu.source = 0x02
    dul.pdu.reason_diagnostic = 0x00

    dul.primitive = dul.pdu.to_primitive()
    dul.primitive.abort_source = 0x02
    dul.primitive.result = 0x01
    dul.primitive.diagnostic = 0x01

    dul.socket.send(dul.pdu.encode())
    evt.trigger(dul.assoc, evt.EVT_PDU_SENT, {'pdu': dul.pdu})

    # Issue A-P-ABORT to user
    primitive = A_P_ABORT()
    primitive.provider_reason = 0x05
    dul.to_user_queue.put(primitive)
    dul.artim_timer.start()

    return 'Sta13'
Ejemplo n.º 12
0
def AA_1(dul):
    """Association abort AA-1.

    If on sending A-ASSOCIATE-RQ we receive an invalid reply, or an abort
    request then abort

    State-event triggers: Sta2 + Evt3/Evt4/Evt10/Evt12/Evt13/Evt19,
    Sta3/Sta5/Sta6/Sta7/Sta8/Sta9/Sta10/Sta11/Sta12 + Evt15

    References
    ----------
    1. DICOM Standard 2015b, PS3.8, Table 9-7, "Associate Establishment
       Related Actions"

    Parameters
    ----------
    dul : pynetdicom.dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        Sta13, the next state of the state machine
    """
    # Send A-ABORT PDU (service-user source) and start (or restart
    # if already started) ARTIM timer.
    dul.pdu = A_ABORT_RQ()
    dul.pdu.source = 0x00

    # The reason for the abort should really be roughly defined by the
    #   current state of the State Machine
    if dul.state_machine.current_state == 'Sta2':
        # Unexpected PDU
        dul.pdu.reason_diagnostic = 0x02
    else:
        # Reason not specified
        dul.pdu.reason_diagnostic = 0x00

    dul.pdu.from_primitive(dul.primitive)

    # Callback
    dul.assoc.acse.debug_send_abort(dul.pdu)

    dul.scu_socket.send(dul.pdu.encode())
    dul.artim_timer.restart()

    return 'Sta13'
Ejemplo n.º 13
0
def AA_1(dul: "DULServiceProvider") -> str:
    """Association abort AA-1.

    If on sending A-ASSOCIATE-RQ we receive an invalid reply, or an abort
    request then abort

    State-event triggers: Sta2 + Evt3/Evt4/Evt10/Evt12/Evt13/Evt19,
    Sta3/Sta5/Sta6/Sta7/Sta8/Sta9/Sta10/Sta11/Sta12 + Evt15

    Parameters
    ----------
    dul : dul.DULServiceProvider
        The DICOM Upper Layer Service instance for the local AE

    Returns
    -------
    str
        ``'Sta13'``, the next state of the state machine
    """
    # Received invalid PDU from peer or an A-ABORT primitive from local user
    try:
        primitive = dul.to_provider_queue.queue[0]
        if isinstance(primitive, (A_ABORT, A_P_ABORT)):
            primitive = dul.to_provider_queue.get(False)
    except (queue.Empty, IndexError):
        primitive = None

    # Send A-ABORT PDU (service-user source) and start (or restart
    # if already started) ARTIM timer.
    pdu = A_ABORT_RQ()
    if primitive is not None:
        pdu.from_primitive(primitive)
    else:
        # Reason not specified
        pdu.source = 0x00
        pdu.reason_diagnostic = 0x00

    dul._send(pdu)
    dul.artim_timer.restart()

    return "Sta13"
Ejemplo n.º 14
0
    def run_reactor(self) -> None:
        """Run the DUL reactor.

        The main :class:`threading.Thread` run loop. Runs constantly, checking
        the connection for incoming data. When incoming data is received it
        categorises it and add its to the
        :attr:`~DULServiceProvider.to_user_queue`.
        """
        # Main DUL loop
        self._idle_timer.start()
        self.socket = cast("AssociationSocket", self.socket)
        sleep = False

        while True:
            # Let the assoc reactor off the leash
            if not self.assoc._dul_ready.is_set():
                self.assoc._dul_ready.set()
                # When single-stepping the reactor, sleep between events so that
                # test code has time to run.
                sleep = True

            if sleep:
                # If there were no events to process on the previous loop,
                #   sleep before checking again, otherwise check immediately
                # Setting `_run_loop_delay` higher will use less CPU when idle, but
                #   will also increase the latency to respond to new requests
                time.sleep(self._run_loop_delay)

            if self._kill_thread:
                break

            # Check the ARTIM timer first so its event is placed on the queue
            #   ahead of any other events this loop
            if self.artim_timer.expired:
                self.event_queue.put("Evt18")

            # Check the connection for incoming data
            try:
                # We can either encode and send a primitive **OR**
                #   receive and decode a PDU per loop of the reactor
                if self._process_recv_primitive(
                ):  # encode (sent by state machine)
                    pass
                elif self._is_transport_event():  # receive and decode PDU
                    self._idle_timer.restart()
            except Exception as exc:
                LOGGER.error("Exception in DUL.run(), aborting association")
                LOGGER.exception(exc)
                # Bypass the state machine and send an A-ABORT
                #   we do it this way because an exception here will mess up
                #   the state machine and we can't guarantee it'll get sent
                #   otherwise
                abort_pdu = A_ABORT_RQ()
                abort_pdu.source = 0x02
                abort_pdu.reason_diagnostic = 0x00
                self.socket.send(abort_pdu.encode())
                self.assoc.is_aborted = True
                self.assoc.is_established = False
                # Hard shutdown of the Association and DUL reactors
                self.assoc._kill = True
                self._kill_thread = True
                return

            # Check the event queue to see if there is anything to do
            try:
                event = self.event_queue.get(block=False)
            # If the queue is empty, return to the start of the loop
            except queue.Empty:
                sleep = True
                continue

            self.state_machine.do_action(event)
            sleep = False