Beispiel #1
0
    def test_assignment(self):
        """ Check assignment works correctly """
        primitive = N_EVENT_REPORT()

        # AffectedSOPClassUID
        primitive.AffectedSOPClassUID = '1.1.1'
        assert primitive.AffectedSOPClassUID == UID('1.1.1')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPClassUID = UID('1.1.2')
        assert primitive.AffectedSOPClassUID == UID('1.1.2')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPClassUID = b'1.1.3'
        assert primitive.AffectedSOPClassUID == UID('1.1.3')
        assert isinstance(primitive.AffectedSOPClassUID, UID)

        # AffectedSOPInstanceUID
        primitive.AffectedSOPInstanceUID = b'1.2.1'
        assert primitive.AffectedSOPInstanceUID == UID('1.2.1')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPInstanceUID = UID('1.2.2')
        assert primitive.AffectedSOPInstanceUID == UID('1.2.2')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPInstanceUID = '1.2.3'
        assert primitive.AffectedSOPInstanceUID == UID('1.2.3')
        assert isinstance(primitive.AffectedSOPClassUID, UID)

        # Event Information
        ds = Dataset()
        ds.PatientID = '1234567'
        primitive.EventInformation = BytesIO(encode(ds, True, True))
        ds = decode(primitive.EventInformation, True, True)
        assert ds.PatientID == '1234567'

        # Event Reply
        ds = Dataset()
        ds.PatientID = '123456'
        primitive.EventReply = BytesIO(encode(ds, True, True))
        ds = decode(primitive.EventReply, True, True)
        assert ds.PatientID == '123456'

        # Event Type ID
        primitive.EventTypeID = 0x0000
        assert primitive.EventTypeID == 0x0000

        # MessageID
        primitive.MessageID = 11
        assert 11 == primitive.MessageID

        # MessageIDBeingRespondedTo
        primitive.MessageIDBeingRespondedTo = 13
        assert 13 == primitive.MessageIDBeingRespondedTo

        # Status
        primitive.Status = 0x0000
        assert primitive.Status == 0x0000
    def test_message_to_primitive_c_store(self):
        """Test converting C_STORE_RQ and _RSP to C_STORE primitive."""
        msg = C_STORE_RQ()
        for data in [c_store_rq_cmd, c_store_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_STORE)
        assert isinstance(primitive.DataSet, BytesIO)
        assert primitive.AffectedSOPClassUID == UID("1.1.1")
        assert primitive.AffectedSOPInstanceUID == UID("1.2.1")
        assert primitive.Priority == 2
        assert primitive.MoveOriginatorApplicationEntityTitle == "UNITTEST"
        assert primitive.MoveOriginatorMessageID == 3

        ds = decode(primitive.DataSet, True, True)
        assert ds.PatientName == "Tube^HeNe"
        assert ds.PatientID == "Test1101"

        msg = C_STORE_RSP()
        p_data = P_DATA()
        p_data.presentation_data_value_list.append([0, c_store_rsp_cmd])
        msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_STORE)
        assert primitive.DataSet is None
        for elem in msg.command_set:
            if hasattr(primitive, elem.keyword):
                item = getattr(primitive, elem.keyword)
                assert item == elem.value
    def test_message_to_primitive_c_move_with_duplicate_command_fields(self):
        msg = C_MOVE_RSP()
        for data in [c_move_rsp_cmd_with_dup, c_move_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_MOVE)
        assert isinstance(primitive.Identifier, BytesIO)
        assert primitive.AffectedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.Status == 65280
        assert primitive.MessageIDBeingRespondedTo == 5

        # Fields with VM > 1 that can only appear once
        assert primitive.NumberOfRemainingSuboperations == 3
        assert primitive.NumberOfCompletedSuboperations == 1
        assert primitive.NumberOfFailedSuboperations == 2
        assert primitive.NumberOfWarningSuboperations == 4

        # Fields with VM > 1 that are allowed to appear multiple times
        assert primitive.OffendingElement == [(0x0, 0x1), (0x0, 0x2)]

        ds = decode(primitive.Identifier, True, True)
        assert ds.QueryRetrieveLevel == "PATIENT"
        assert ds.PatientID == "*"
Beispiel #4
0
 def test_implicit_little(self):
     """Test decoding using implicit VR little endian."""
     bytestring = BytesIO()
     bytestring.write(b'\x10\x00\x10\x00\x0e\x00\x00\x00\x43\x49' \
                      b'\x54\x49\x5a\x45\x4e\x5e\x53\x6e\x69\x70' \
                      b'\x73\x20')
     ds = decode(bytestring, True, True)
     assert ds.PatientName == 'CITIZEN^Snips'
Beispiel #5
0
 def test_explicit_big(self):
     """Test decoding using explicit VR big endian."""
     bytestring = BytesIO()
     bytestring.write(b'\x00\x10\x00\x10\x50\x4e\x00\x0e\x43\x49' \
                      b'\x54\x49\x5a\x45\x4e\x5e\x53\x6e\x69\x70' \
                      b'\x73\x20')
     ds = decode(bytestring, False, False)
     assert ds.PatientName == 'CITIZEN^Snips'
Beispiel #6
0
    def _get_dataset(self, attr: str, exc_msg: str) -> Dataset:
        """Return DIMSE dataset-like parameter as a *pydicom* Dataset.

        Parameters
        ----------
        attr : str
            The name of the DIMSE primitive's dataset-like parameter, one of
            'DataSet', 'Identifier', 'AttributeList', 'ModificationList',
            'EventInformation', 'ActionInformation'.
        exc_msg : str
            The exception message to use if the request primitive has no
            dataset-like parameter.

        Returns
        -------
        pydicom.dataset.Dataset
            The decoded dataset-like parameter.

        Raises
        ------
        AttributeError
            If the corresponding event is not due to one of the DIMSE requests
            with a dataset-like parameter.
        """
        try:
            bytestream = getattr(self.request, attr)

            # If no change in encoded data then return stored decode
            if self._hash == hash(bytestream):
                return cast(Dataset, self._decoded)

            # Some dataset-like parameters are optional
            if bytestream and bytestream.getvalue() != b"":
                # Dataset-like parameter has been used
                t_syntax = self.context.transfer_syntax
                ds = decode(
                    bytestream,
                    t_syntax.is_implicit_VR,
                    t_syntax.is_little_endian,
                    t_syntax.is_deflated,
                )

                ds.is_little_endian = t_syntax.is_little_endian
                ds.is_implicit_VR = t_syntax.is_implicit_VR

                # Store the decoded dataset in case its accessed again
                self._decoded = ds
            else:
                # Dataset-like parameter hasn't been used
                self._decoded = Dataset()

            self._hash = hash(bytestream)
            return self._decoded

        except AttributeError as exc:
            pass

        raise AttributeError(exc_msg)
Beispiel #7
0
    def test_deflated(self):
        """Test decoding a deflated explicit VR little endian dataset."""
        with open(DEFL_DATASET, 'rb') as f:
            # Only the main dataset uses deflated encoding
            f.seek(334)
            b = BytesIO(f.read())

        ds = decode(b, False, True, True)
        assert ds.PatientName == "^^^^"
 def test_explicit_little(self):
     """Test decoding using explicit VR little endian."""
     bytestring = BytesIO()
     bytestring.write(
         b"\x10\x00\x10\x00\x50\x4e\x0e\x00\x43\x49"
         b"\x54\x49\x5a\x45\x4e\x5e\x53\x6e\x69\x70"
         b"\x73\x20"
     )
     ds = decode(bytestring, False, True)
     assert ds.PatientName == "CITIZEN^Snips"
    def test_message_to_primitive_c_move(self):
        """Test converting C_MOVE_RQ to C_MOVE primitive."""
        msg = C_MOVE_RQ()
        for data in [c_move_rq_cmd, c_move_rq_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_MOVE)
        assert isinstance(primitive.Identifier, BytesIO)
        assert primitive.AffectedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.Priority == 2
        assert primitive.MoveDestination == "MOVE_SCP"
        assert primitive.MessageID == 7

        ds = decode(primitive.Identifier, True, True)
        assert ds.QueryRetrieveLevel == "PATIENT"
        assert ds.PatientID == "*"

        msg = C_MOVE_RSP()
        for data in [c_move_rsp_cmd, c_move_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_MOVE)
        assert isinstance(primitive.Identifier, BytesIO)
        assert primitive.AffectedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.Status == 65280
        assert primitive.MessageIDBeingRespondedTo == 5
        assert primitive.NumberOfRemainingSuboperations == 3
        assert primitive.NumberOfCompletedSuboperations == 1
        assert primitive.NumberOfFailedSuboperations == 2
        assert primitive.NumberOfWarningSuboperations == 4

        ds = decode(primitive.Identifier, True, True)
        assert ds.QueryRetrieveLevel == "PATIENT"
        assert ds.PatientID == "*"
    def test_message_to_primitive_n_action(self):
        """Test converting N_ACTION_RQ and _RSP to primitive."""
        # N-ACTION-RQ
        msg = N_ACTION_RQ()
        for data in [n_action_rq_cmd, n_action_rq_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_ACTION)
        assert primitive.RequestedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.RequestedSOPInstanceUID == UID(
            "1.2.392.200036.9116.2.6.1.48")
        assert primitive.MessageID == 7
        assert primitive.ActionTypeID == 1

        ds = decode(primitive.ActionInformation, True, True)
        assert ds.PatientName == "Tube HeNe"
        assert ds.PatientID == "Test1101"

        # N-ACTION-RSP
        msg = N_ACTION_RSP()
        for data in [n_action_rsp_cmd, n_action_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_ACTION)
        assert primitive.AffectedSOPClassUID == UID("1.2.4.10")
        assert primitive.AffectedSOPInstanceUID == UID("1.2.4.5.7.8")
        assert primitive.MessageIDBeingRespondedTo == 5
        assert primitive.ActionTypeID == 1
        assert primitive.Status == 0x0000

        ds = decode(primitive.ActionReply, True, True)
        assert ds.PatientName == "Tube HeNe"
        assert ds.PatientID == "Test1101"
    def test_message_to_primitive_n_set(self):
        """Test converting N_SET_RQ and _RSP to primitive."""
        # N-SET-RQ
        msg = N_SET_RQ()
        for data in [n_set_rq_cmd, n_set_rq_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_SET)
        assert primitive.RequestedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.RequestedSOPInstanceUID == UID(
            "1.2.392.200036.9116.2.6.1.48")
        assert primitive.MessageID == 7

        ds = decode(primitive.ModificationList, True, True)
        assert ds.PatientName == "Tube HeNe"
        assert ds.PatientID == "Test1101"

        # N-SET-RSP
        msg = N_SET_RSP()
        for data in [n_set_rsp_cmd, n_set_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_SET)
        assert isinstance(primitive.AttributeList, BytesIO)
        assert primitive.AffectedSOPClassUID == UID("1.2.4.10")
        assert primitive.AffectedSOPInstanceUID == UID("1.2.4.5.7.8")
        assert primitive.MessageIDBeingRespondedTo == 5
        assert primitive.Status == 0x0000

        ds = decode(primitive.AttributeList, True, True)
        assert ds.PatientName == "Tube HeNe"
        assert ds.PatientID == "Test1101"
Beispiel #12
0
    def test_message_to_primitive_n_event_report(self):
        """Test converting N_EVENT_REPORT_RQ and _RSP to primitive."""
        # N-EVENT-REPORT-RQ
        msg = N_EVENT_REPORT_RQ()
        for data in [n_er_rq_cmd, n_er_rq_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_EVENT_REPORT)
        assert primitive.AffectedSOPClassUID == UID(
            '1.2.840.10008.5.1.4.1.1.2')
        assert primitive.AffectedSOPInstanceUID == UID(
            '1.2.392.200036.9116.2.6.1.48')
        assert primitive.MessageID == 7
        assert primitive.EventTypeID == 2

        ds = decode(primitive.EventInformation, True, True)
        assert ds.PatientName == 'Tube HeNe'
        assert ds.PatientID == 'Test1101'

        # N-EVENT-REPORT-RSP
        msg = N_EVENT_REPORT_RSP()
        for data in [n_er_rsp_cmd, n_er_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_EVENT_REPORT)
        assert primitive.AffectedSOPClassUID == UID('1.2.4.10')
        assert primitive.AffectedSOPInstanceUID == UID('1.2.4.5.7.8')
        assert primitive.MessageIDBeingRespondedTo == 5
        assert primitive.EventTypeID == 2
        assert primitive.Status == 0x0000

        ds = decode(primitive.EventReply, True, True)
        assert ds.PatientName == 'Tube HeNe'
        assert ds.PatientID == 'Test1101'
    def test_message_to_primitive_c_find(self):
        """Test converting C_FIND_RQ to C_FIND primitive."""
        msg = C_FIND_RQ()
        for data in [c_find_rq_cmd, c_find_rq_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_FIND)
        assert isinstance(primitive.Identifier, BytesIO)
        assert primitive.AffectedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.Priority == 2
        assert primitive.MessageID == 7

        ds = decode(primitive.Identifier, True, True)
        assert ds.QueryRetrieveLevel == "PATIENT"
        assert ds.PatientID == "*"

        msg = C_GET_RSP()
        for data in [c_find_rsp_cmd, c_find_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, C_FIND)
        assert isinstance(primitive.Identifier, BytesIO)
        assert primitive.AffectedSOPClassUID == UID(
            "1.2.840.10008.5.1.4.1.1.2")
        assert primitive.Status == 65280

        ds = decode(primitive.Identifier, True, True)
        assert ds.QueryRetrieveLevel == "PATIENT"
        assert ds.RetrieveAETitle == "FINDSCP"

        assert ds.PatientName == "ANON^A^B^C^D"
Beispiel #14
0
    def test_message_to_primitive_n_create(self):
        """Test converting N_CREATE_RQ and _RSP to primitive."""
        # N-CREATE-RQ
        msg = N_CREATE_RQ()
        for data in [n_create_rq_cmd, n_create_rq_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_CREATE)
        assert primitive.AffectedSOPClassUID == UID(
            '1.2.840.10008.5.1.4.1.1.2')
        assert primitive.AffectedSOPInstanceUID == UID(
            '1.2.392.200036.9116.2.6.1.48')
        assert primitive.MessageID == 7

        ds = decode(primitive.AttributeList, True, True)
        assert ds.PatientName == 'Tube HeNe'
        assert ds.PatientID == 'Test1101'

        # N-ACTION-RSP
        msg = N_CREATE_RSP()
        for data in [n_create_rsp_cmd, n_create_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_CREATE)
        assert primitive.AffectedSOPClassUID == UID('1.2.4.10')
        assert primitive.AffectedSOPInstanceUID == UID('1.2.4.5.7.8')
        assert primitive.MessageIDBeingRespondedTo == 5
        assert primitive.Status == 0x0000

        ds = decode(primitive.AttributeList, True, True)
        assert ds.PatientName == 'Tube HeNe'
        assert ds.PatientID == 'Test1101'
Beispiel #15
0
    def test_assignment(self):
        """ Check assignment works correctly """
        primitive = N_CREATE()

        # AffectedSOPClassUID
        primitive.AffectedSOPClassUID = '1.1.1'
        assert primitive.AffectedSOPClassUID == UID('1.1.1')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPClassUID = UID('1.1.2')
        assert primitive.AffectedSOPClassUID == UID('1.1.2')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPClassUID = b'1.1.3'
        assert primitive.AffectedSOPClassUID == UID('1.1.3')
        assert isinstance(primitive.AffectedSOPClassUID, UID)

        # AffectedSOPInstanceUID
        primitive.AffectedSOPInstanceUID = b'1.2.1'
        assert primitive.AffectedSOPInstanceUID == UID('1.2.1')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPInstanceUID = UID('1.2.2')
        assert primitive.AffectedSOPInstanceUID == UID('1.2.2')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPInstanceUID = '1.2.3'
        assert primitive.AffectedSOPInstanceUID == UID('1.2.3')
        assert isinstance(primitive.AffectedSOPClassUID, UID)

        # AttributeList
        ref_ds = Dataset()
        ref_ds.PatientID = '1234567'
        primitive.AttributeList = BytesIO(encode(ref_ds, True, True))
        ds = decode(primitive.AttributeList, True, True)
        assert ds.PatientID == '1234567'

        # MessageID
        primitive.MessageID = 11
        assert 11 == primitive.MessageID

        # MessageIDBeingRespondedTo
        primitive.MessageIDBeingRespondedTo = 13
        assert 13 == primitive.MessageIDBeingRespondedTo

        # Status
        primitive.Status = 0x0000
        assert primitive.Status == 0x0000
Beispiel #16
0
    def identifier(self):
        """Return a C-FIND, C-GET or C-MOVE request's `Identifier` as a
        *pydicom* Dataset.

        Because *pydicom* defers data parsing during decoding until an element
        is actually required the returned ``Dataset`` may raise an exception
        when any element is first accessed. It's therefore important that
        proper error handling be part of any handler that
        uses the returned ``Dataset``.

        Returns
        -------
        pydicom.dataset.Dataset
            The decoded *Identifier* dataset.

        Raises
        ------
        AttributeError
            If the corresponding event is not a C-FIND, C-GET or C-MOVE
            request.
        """
        try:
            # If no change in encoded data then return stored decode
            if self._hash == hash(self.request.Identifier):
                return self._decoded

            t_syntax = self.context.transfer_syntax
            ds = decode(self.request.Identifier,
                        t_syntax.is_implicit_VR,
                        t_syntax.is_little_endian)

            # Store the decoded dataset in case its accessed again
            self._hash = hash(self.request.Identifier)
            self._decoded = ds

            return ds
        except AttributeError:
            pass

        raise AttributeError(
            "The corresponding event is not a C-FIND, C-GET or C-MOVE request "
            "and has no 'Identifier' parameter"
        )
Beispiel #17
0
    def test_message_to_primitive_n_get(self):
        """Test converting N_GET_RQ and _RSP to primitive."""
        # N-GET-RQ
        msg = N_GET_RQ()
        for data in [n_get_rq_cmd]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_GET)
        assert primitive.RequestedSOPClassUID == UID(
            '1.2.840.10008.5.1.4.1.1.2')
        assert primitive.RequestedSOPInstanceUID == UID(
            '1.2.392.200036.9116.2.6.1.48')
        assert primitive.MessageID == 7
        primitive.AttributeIdentifierList = [(0x7fe0, 0x0010),
                                             (0x0000, 0x0000),
                                             (0xFFFF, 0xFFFF)]

        msg = N_GET_RSP()
        for data in [n_get_rsp_cmd, n_get_rsp_ds]:
            p_data = P_DATA()
            p_data.presentation_data_value_list.append([0, data])
            msg.decode_msg(p_data)
        msg.decode_msg(p_data)
        primitive = msg.message_to_primitive()
        assert isinstance(primitive, N_GET)
        assert primitive.AttributeIdentifierList is None
        assert primitive.AffectedSOPClassUID == UID('1.2.4.10')
        assert primitive.AffectedSOPInstanceUID == UID('1.2.4.5.7.8')
        assert primitive.MessageIDBeingRespondedTo == 5
        assert primitive.Status == 0x0000

        ds = decode(primitive.AttributeList, True, True)
        assert ds.PatientName == 'Tube HeNe'
        assert ds.PatientID == 'Test1101'
Beispiel #18
0
    def decode_msg(self, primitive):
        """Converts P-DATA primitives into a DIMSEMessage sub-class.

        Decodes the data from the P-DATA service primitive (which
        may contain the results of one or more P-DATA-TF PDUs) into the
        `command_set` and `data_set` attributes. Also sets the `ID` and
        `encoded_command_set` attributes of the DIMSEMessage sub-class object.

        Parameters
        ----------
        primitive : pdu_primitives.P_DATA
            The P-DATA service primitive to be decoded into a DIMSE message.

        Returns
        -------
        bool
            True when the DIMSE message is completely decoded, False otherwise.

        References
        ----------

        * DICOM Standard, Part 8, Annex E
        """
        # Make sure this is a P-DATA primitive
        if primitive.__class__ != P_DATA or primitive is None:
            return False

        for (context_id, data) in primitive.presentation_data_value_list:

            # The first byte of the P-DATA is the Message Control Header
            #   See Part 8, Annex E.2
            # The standard says that only the significant bits (ie the last
            #   two) should be checked
            # xxxxxx00 - Message Dataset information, not the last fragment
            # xxxxxx01 - Command information, not the last fragment
            # xxxxxx10 - Message Dataset information, the last fragment
            # xxxxxx11 - Command information, the last fragment

            ## Compatibility
            # Python 2
            #   - data[0] returns length 1 str
            #   - data[:1] returns length 1 str
            # Python 3
            #   - data[0] returns int
            #   - data[:1] returns length 1 bytes
            # So grab str/bytes and convert to int rather than grab
            #   str/int and convert the str to int. The reason for this is
            #   that a type check is twice as expensive as just converting
            #   str/bytes to int
            control_header_byte = ord(data[:1])

            # LOGGER.debug('Control header byte %s', control_header_byte)
            #print('Control header byte {}'.format(control_header_byte))

            # COMMAND SET
            # P-DATA fragment contains Command Set information
            #   (control_header_byte is xxxxxx01 or xxxxxx11)
            if control_header_byte & 1:
                # The command set may be spread out over a number
                #   of fragments and P-DATA primitives and we need to remember
                #   the elements from previous fragments, hence the
                #   encoded_command_set class attribute
                # This adds all the command set data to the class object
                self.encoded_command_set.write(data[1:])

                # The final command set fragment (xxxxxx11) has been added
                #   so decode the command set
                if control_header_byte & 2:
                    # Presentation Context ID
                    #   Set this now as must only be one final command set
                    #   fragment and command set must always be present
                    self.context_id = context_id

                    # Command Set is always encoded Implicit VR Little Endian
                    #   decode(dataset, is_implicit_VR, is_little_endian)
                    # pylint: disable=attribute-defined-outside-init
                    self.command_set = decode(self.encoded_command_set, True,
                                              True)

                    # Determine which DIMSE Message class to use
                    self.__class__ = _MESSAGE_CLASS_TYPES[
                        self.command_set.CommandField]

                    # Determine if a Data Set is present by checking for
                    #   (0000, 0800) CommandDataSetType US 1. If the value is
                    #   0x0101 no dataset present, otherwise one is.
                    if self.command_set.CommandDataSetType == 0x0101:
                        # By returning True we're indicating that the message
                        #   has been completely decoded
                        return True

            # DATA SET
            # P-DATA fragment contains Data Set information
            #   (control_header_byte is xxxxxx00 or xxxxxx10)
            else:
                # As with the command set, the data set may be spread over
                #   a number of fragments in each P-DATA primitive and a
                #   number of P-DATA primitives.
                self.data_set.write(data[1:])

                # The final data set fragment (xxxxxx10) has been added
                if control_header_byte & 2 != 0:
                    # By returning True we're indicating that the message
                    #   has been completely decoded
                    return True

        # We return False to indicate that the message isn't yet fully decoded
        return False
    def Decode(self, pdata):
        """ Converts a series of P-DATA primitives into data for the DIMSE
        Message
        
        Decodes the data from the P-DATA service primitive (which
        may contain the results of one or more P-DATA-TF PDUs) into the
        `command_set` and `data_set` attributes. Also sets the `ID` and
        `encoded_command_set` attributes
        
        PS3.9 Section 9.3.1: The encoding of the DICOM UL PDUs is
        big endian byte ordering, while the encoding of the PDV message
        fragments is defined by the negotiated Transfer Syntax at association
        establishment. A fragment is also known as an Application Protocol
        Data Unit (APDU) using the OSI nomenclature (PS3.7 8.1).
        
        Parameters
        ----------
        pdata : pynetdicom.DULparameters.P_DATA_ServiceParameters
            The P-DATA service primitive to be decoded into a DIMSE message
        
        Returns
        -------
        bool
            True when complete, False otherwise.
        """
        # Make sure this is a P-DATA primitive
        if pdata.__class__ != P_DATA or pdata is None:
            return False
        
        for pdv_item in pdata.presentation_data_value_list:
            # Presentation Context ID
            self.ID = pdv_item[0]

            # The first byte of the P-DATA is the Message Control Header
            #   See PS3.8 Annex E.2
            # The standard says that only the significant bits (ie the last
            #   two) should be checked
            # xxxxxx00 - Message Dataset information, not the last fragment
            # xxxxxx01 - Command information, not the last fragment
            # xxxxxx10 - Message Dataset information, the last fragment
            # xxxxxx11 - Command information, the last fragment
            control_header_byte = pdv_item[1][0]

            ## COMMAND SET
            # P-DATA fragment contains Command information 
            #   (control_header_byte is xxxxxx01 or xxxxxx11)
            if control_header_byte & 1:
                # The command set may be spread out over a number
                #   of fragments and we need to remember the elements
                #   from previous fragments, hence the encoded_command_set
                #   class attribute
                self.encoded_command_set.write(pdv_item[1][1:])

                # The P-DATA fragment is the last one (xxxxxx11)
                if control_header_byte & 2:
                    # Command Set is always encoded Implicit VR Little Endian
                    #   decode(dataset, is_implicit_VR, is_little_endian)
                    self.command_set = decode(self.encoded_command_set, 
                                              True, True)

                    # Determine which DIMSE Message class to use
                    self.__class__ = MessageType[self.command_set.CommandField]
                    
                    # (0000, 0800) CommandDataSetType US 1
                    #   if value is 0101H no dataset present
                    #   otherwise a dataset is included in the Message
                    if self.command_set.CommandDataSetType == 0x0101:
                        return True

            ## DATA SET
            # P-DATA fragment contains Message Dataset information 
            #   (control_header_byte is xxxxxx00 or xxxxxx10)
            else:
                self.data_set.write(pdv_item[1][1:])

                # The P-DATA fragment is the last one (xxxxxx10)
                if control_header_byte & 2 != 0:
                    return True

        return False
Beispiel #20
0
 def test_failure(self):
     bytestream = BytesIO(b'\x08\x00\x01\x00\x04\x00\x00\x00\x00\x08\x00\x49')
     with pytest.raises(NotImplementedError):
         ds = decode(bytestream, False, True)
         print(ds)
Beispiel #21
0
    def decode_msg(self, primitive, assoc=None):
        """Converts P-DATA primitives into a ``DIMSEMessage`` sub-class.

        Decodes the data from the P-DATA service primitive (which
        may contain the results of one or more P-DATA-TF PDUs) into the
        :attr:`~DIMSEMessage.command_set` and :attr:`~DIMSEMessage.data_set`
        attributes. Also sets the :attr:`~DIMSEMessage.context_id` and
        :attr:`~DIMSEMessage.encoded_command_set` attributes of the
        ``DIMSEMessage`` sub-class object.

        Parameters
        ----------
        primitive : pdu_primitives.P_DATA
            The P-DATA service primitive to be decoded into a DIMSE message.
        assoc : association.Association, optional
            The association processing the message. This is required when:

            * :attr:`~pynetdicom._config.STORE_RECV_CHUNKED_DATASET` is
              ``True``
            * The P-DATA primitive contains part of a C-STORE-RQ message

            In this case the association is consulted for its accepted
            transfer syntax, which is included in the File Meta Information
            of the stored dataset.

        Returns
        -------
        bool
            ``True`` when the DIMSE message is completely decoded, ``False``
            otherwise.

        References
        ----------

        * DICOM Standard, Part 8, :dcm:`Annex E<part08/chapter_E.html>`
        """
        # Make sure this is a P-DATA primitive
        if primitive.__class__ != P_DATA or primitive is None:
            return False

        for (context_id, data) in primitive.presentation_data_value_list:

            # The first byte of the P-DATA is the Message Control Header
            #   See Part 8, Annex E.2
            # The standard says that only the significant bits (ie the last
            #   two) should be checked
            # xxxxxx00 - Message Dataset information, not the last fragment
            # xxxxxx01 - Command information, not the last fragment
            # xxxxxx10 - Message Dataset information, the last fragment
            # xxxxxx11 - Command information, the last fragment
            control_header_byte = data[0]

            # LOGGER.debug('Control header byte %s', control_header_byte)
            #print(f'Control header byte {control_header_byte}')

            # COMMAND SET
            # P-DATA fragment contains Command Set information
            #   (control_header_byte is xxxxxx01 or xxxxxx11)
            if control_header_byte & 1:
                # The command set may be spread out over a number
                #   of fragments and P-DATA primitives and we need to remember
                #   the elements from previous fragments, hence the
                #   encoded_command_set class attribute
                # This adds all the command set data to the class object
                self.encoded_command_set.write(data[1:])

                # The final command set fragment (xxxxxx11) has been added
                #   so decode the command set
                if control_header_byte & 2:
                    # Presentation Context ID
                    #   Set this now as must only be one final command set
                    #   fragment and command set must always be present
                    self.context_id = context_id

                    # Command Set is always encoded Implicit VR Little Endian
                    #   decode(dataset, is_implicit_VR, is_little_endian)
                    # pylint: disable=attribute-defined-outside-init
                    self.command_set = decode(self.encoded_command_set, True,
                                              True)

                    # Determine which DIMSE Message class to use
                    self.__class__ = (
                        _MESSAGE_TYPES[self.command_set.CommandField][1])

                    # Determine if a Data Set is present by checking for
                    #   (0000, 0800) CommandDataSetType US 1. If the value is
                    #   0x0101 no dataset present, otherwise one is.
                    if self.command_set.CommandDataSetType == 0x0101:
                        # By returning True we're indicating that the message
                        #   has been completely decoded
                        return True

                    # Data Set is present
                    if (_config.STORE_RECV_CHUNKED_DATASET
                            and isinstance(self, C_STORE_RQ)):
                        # delete=False is a workaround for Windows
                        # Setting delete=True prevents us from re-opening
                        # the file after it is opened by NamedTemporaryFile
                        # below.
                        self._data_set_file = NamedTemporaryFile(delete=False,
                                                                 mode="wb",
                                                                 suffix=".dcm")
                        self._data_set_path = Path(self._data_set_file.name)
                        # Write the File Meta
                        self._data_set_file.write(b'\x00' * 128)
                        self._data_set_file.write(b'DICM')

                        cs = self.command_set
                        cx = assoc._accepted_cx[context_id]
                        write_file_meta_info(
                            self._data_set_file,
                            create_file_meta(
                                sop_class_uid=cs.AffectedSOPClassUID,
                                sop_instance_uid=cs.AffectedSOPInstanceUID,
                                transfer_syntax=cx.transfer_syntax[0]))

            # DATA SET
            # P-DATA fragment contains Data Set information
            #   (control_header_byte is xxxxxx00 or xxxxxx10)
            else:
                # As with the command set, the data set may be spread over
                #   a number of fragments in each P-DATA primitive and a
                #   number of P-DATA primitives.
                if self._data_set_file:
                    self._data_set_file.write(data[1:])
                else:
                    self.data_set.write(data[1:])

                # The final data set fragment (xxxxxx10) has been added
                if control_header_byte & 2 != 0:
                    # By returning True we're indicating that the message
                    #   has been completely decoded
                    return True

        # We return False to indicate that the message isn't yet fully decoded
        return False
Beispiel #22
0
    def test_assignment(self):
        """Check assignment works correctly"""
        primitive = N_GET()

        # AffectedSOPClassUID
        primitive.AffectedSOPClassUID = '1.1.1'
        assert primitive.AffectedSOPClassUID == UID('1.1.1')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPClassUID = UID('1.1.2')
        assert primitive.AffectedSOPClassUID == UID('1.1.2')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPClassUID = b'1.1.3'
        assert primitive.AffectedSOPClassUID == UID('1.1.3')
        assert isinstance(primitive.AffectedSOPClassUID, UID)

        # AffectedSOPInstanceUID
        primitive.AffectedSOPInstanceUID = b'1.2.1'
        assert primitive.AffectedSOPInstanceUID == UID('1.2.1')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPInstanceUID = UID('1.2.2')
        assert primitive.AffectedSOPInstanceUID == UID('1.2.2')
        assert isinstance(primitive.AffectedSOPClassUID, UID)
        primitive.AffectedSOPInstanceUID = '1.2.3'
        assert primitive.AffectedSOPInstanceUID == UID('1.2.3')
        assert isinstance(primitive.AffectedSOPClassUID, UID)

        # AttributeList
        ref_ds = Dataset()
        ref_ds.PatientID = '1234567'
        primitive.AttributeList = BytesIO(encode(ref_ds, True, True))
        ds = decode(primitive.AttributeList, True, True)
        assert ds.PatientID == '1234567'

        # AttributeIdentifierList
        primitive.AttributeIdentifierList = [
            0x00001000, (0x0000, 0x1000),
            Tag(0x7fe0, 0x0010)
        ]
        assert [Tag(0x0000, 0x1000),
                Tag(0x0000, 0x1000),
                Tag(0x7fe0, 0x0010)] == primitive.AttributeIdentifierList
        primitive.AttributeIdentifierList = [(0x7fe0, 0x0010)]
        assert [Tag(0x7fe0, 0x0010)] == primitive.AttributeIdentifierList
        primitive.AttributeIdentifierList = (0x7fe0, 0x0010)
        assert [Tag(0x7fe0, 0x0010)] == primitive.AttributeIdentifierList

        elem = DataElement((0x0000, 0x0005), 'AT', [Tag(0x0000, 0x1000)])
        assert isinstance(elem.value, MutableSequence)
        primitive.AttributeIdentifierList = elem.value
        assert [Tag(0x0000, 0x1000)] == primitive.AttributeIdentifierList

        # MessageID
        primitive.MessageID = 11
        assert 11 == primitive.MessageID

        # MessageIDBeingRespondedTo
        primitive.MessageIDBeingRespondedTo = 13
        assert 13 == primitive.MessageIDBeingRespondedTo

        # RequestedSOPClassUID
        primitive.RequestedSOPClassUID = '1.1.1'
        assert primitive.RequestedSOPClassUID == UID('1.1.1')
        assert isinstance(primitive.RequestedSOPClassUID, UID)
        primitive.RequestedSOPClassUID = UID('1.1.2')
        assert primitive.RequestedSOPClassUID == UID('1.1.2')
        assert isinstance(primitive.RequestedSOPClassUID, UID)
        primitive.RequestedSOPClassUID = b'1.1.3'
        assert primitive.RequestedSOPClassUID == UID('1.1.3')
        assert isinstance(primitive.RequestedSOPClassUID, UID)

        # RequestedSOPInstanceUID
        primitive.RequestedSOPInstanceUID = b'1.2.1'
        assert primitive.RequestedSOPInstanceUID == UID('1.2.1')
        assert isinstance(primitive.RequestedSOPInstanceUID, UID)
        primitive.RequestedSOPInstanceUID = UID('1.2.2')
        assert primitive.RequestedSOPInstanceUID == UID('1.2.2')
        assert isinstance(primitive.RequestedSOPInstanceUID, UID)
        primitive.RequestedSOPInstanceUID = '1.2.3'
        assert primitive.RequestedSOPInstanceUID == UID('1.2.3')
        assert isinstance(primitive.RequestedSOPInstanceUID, UID)

        # Status
        primitive.Status = 0x0000
        assert primitive.Status == 0x0000
Beispiel #23
0
    def test_failure(self):
        def dummy():
            pass

        with pytest.raises(AttributeError):
            print(decode(dummy, False, True))