def test_conversion(self): """ Check converting to PDU item works correctly """ primitive = SCP_SCU_RoleSelectionNegotiation() primitive.sop_class_uid = b'1.2.840.10008.5.1.4.1.1.2' primitive.scp_role = True primitive.scu_role = False item = primitive.from_primitive() self.assertTrue(item.encode() == b'\x54\x00\x00\x1d\x00\x19\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30' \ b'\x30\x30\x38\x2e\x35\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x2e\x32\x00' \ b'\x01') primitive = SCP_SCU_RoleSelectionNegotiation() primitive.sop_class_uid = b'1.2.840.10008.5.1.4.1.1.2' primitive.scp_role = False primitive.scu_role = False with self.assertRaises(ValueError): primitive.from_primitive()
def get(self,model=None,to_address=None,to_port=None,to_name=None, patient_name=None): # Set the extended negotiation SCP/SCU role selection to allow us to receive # C-STORE requests for the supported SOP classes # I don't totally understand this, leaving as is until test it out. ext_neg = [] for context in self.ae.presentation_contexts_scu: tmp = SCP_SCU_RoleSelectionNegotiation() tmp.sop_class_uid = context.AbstractSyntax tmp.scu_role = False tmp.scp_role = True ext_neg.append(tmp) if model is None: model = 'P' self.check_information_model(model) if patient_name is None: patient_name = "*" # Make the association - updates self.assoc self.make_assoc(address=to_address, name=to_name, port=to_port, ext_neg=ext_neg) # Create a query dataset dataset = Dataset() dataset.PatientName = patient_name dataset.QueryRetrieveLevel = "PATIENT" # Send query if self.assoc.is_established: response = self.assoc.send_c_get(dataset, query_model=model) time.sleep(1) if response is not None: for value in response: pass self.assoc.release() self.ae.quit() # Not sure if we want to quit here
scu_classes.extend(StorageSOPClassList) # Create application entity # Binding to port 0 lets the OS pick an available port ae = AE(ae_title=args.calling_aet, port=0, scu_sop_class=scu_classes, scp_sop_class=[], transfer_syntax=[ExplicitVRLittleEndian]) # Set the extended negotiation SCP/SCU role selection to allow us to receive # C-STORE requests for the supported SOP classes ext_neg = [] for context in ae.presentation_contexts_scu: tmp = SCP_SCU_RoleSelectionNegotiation() tmp.sop_class_uid = context.AbstractSyntax tmp.scu_role = False tmp.scp_role = True ext_neg.append(tmp) # Request association with remote assoc = ae.associate(args.peer, args.port, args.called_aet, ext_neg=ext_neg) # Create query dataset d = Dataset() d.PatientName = '*' d.QueryRetrieveLevel = "PATIENT" if args.worklist: query_model = 'W'
# Create application entity # Binding to port 0 lets the OS pick an available port ae = AE(ae_title=args.calling_aet, port=0) for context in QueryRetrievePresentationContexts: ae.add_requested_context(context.abstract_syntax) for context in StoragePresentationContexts[:115]: ae.add_requested_context(context.abstract_syntax) # Add SCP/SCU Role Selection Negotiation to the extended negotiation # We want to act as a Storage SCP ext_neg = [] for context in StoragePresentationContexts: role = SCP_SCU_RoleSelectionNegotiation() role.sop_class_uid = context.abstract_syntax role.scp_role = True role.scu_role = False ext_neg.append(role) # Create query dataset d = Dataset() d.PatientName = '*' d.QueryRetrieveLevel = "PATIENT" if args.worklist: query_model = 'W' elif args.patient: query_model = 'P' elif args.study: query_model = 'S'
def test_assignment_and_exceptions(self): """ Check incorrect types/values for properties raise exceptions """ primitive = SCP_SCU_RoleSelectionNegotiation() ## Check assignment # SOP Class UID reference_uid = UID('1.2.840.10008.5.1.4.1.1.2') primitive.sop_class_uid = b'1.2.840.10008.5.1.4.1.1.2' self.assertTrue(primitive.sop_class_uid == reference_uid) primitive.sop_class_uid = '1.2.840.10008.5.1.4.1.1.2' self.assertTrue(primitive.sop_class_uid == reference_uid) primitive.sop_class_uid = UID('1.2.840.10008.5.1.4.1.1.2') self.assertTrue(primitive.sop_class_uid == reference_uid) # SCP Role primitive.scp_role = False self.assertTrue(primitive.scp_role == False) # SCU Role primitive.scu_role = True self.assertTrue(primitive.scu_role == True) ## Check exceptions with pytest.raises(TypeError): primitive.sop_class_uid = 10 with pytest.raises(TypeError): primitive.sop_class_uid = 45.2 with pytest.raises(ValueError): primitive.sop_class_uid = 'abc' with pytest.raises(TypeError): primitive.scp_role = 1 with pytest.raises(TypeError): primitive.scp_role = 'abc' with pytest.raises(TypeError): primitive.scu_role = 1 with pytest.raises(TypeError): primitive.scu_role = 'abc' # No value set primitive = SCP_SCU_RoleSelectionNegotiation() with pytest.raises(ValueError): item = primitive.from_primitive() primitive.sop_class_uid = b'1.2.840.10008.5.1.4.1.1.2' with pytest.raises(ValueError): item = primitive.from_primitive() primitive.scp_role = False with pytest.raises(ValueError): item = primitive.from_primitive() primitive = SCP_SCU_RoleSelectionNegotiation() primitive.sop_class_uid = b'1.2.840.10008.5.1.4.1.1.2' primitive.scu_role = True item = primitive.from_primitive() assert item.scu_role assert not item.scp_role primitive = SCP_SCU_RoleSelectionNegotiation() primitive.scp_role = True primitive.scu_role = True with pytest.raises(ValueError): item = primitive.from_primitive()
def negotiate_as_acceptor(rq_contexts, ac_contexts, roles=None): """Process the Presentation Contexts as an Association acceptor. Parameters ---------- rq_contexts : list of PresentationContext The Presentation Contexts proposed by the peer. Each item has values for Context ID, Abstract Syntax and Transfer Syntax. ac_contexts : list of PresentationContext The Presentation Contexts supported by the local AE when acting as an Association acceptor. Each item has values for Context ID Abstract Syntax and Transfer Syntax. roles : dict or None If the requestor has included one or more SCP/SCU Role Selection Negotiation items then this will be a dict of {SOP Class UID : (SCU role, SCP role)}, otherwise None (default) Returns ------- list of PresentationContext The accepted presentation context items, each with a Result value a Context ID, an Abstract Syntax and one Transfer Syntax item. Items are sorted in increasing Context ID value. list of SCP_SCU_RoleSelectionNegotiation If `roles` is not None then this is a list of SCP/SCU Role Selection Negotiation items that can be sent back to the requestor. """ from pynetdicom3.pdu_primitives import SCP_SCU_RoleSelectionNegotiation roles = roles or {} result_contexts = [] reply_roles = {} # No requestor presentation contexts if not rq_contexts: return result_contexts, [] # Acceptor doesn't support any presentation contexts if not ac_contexts: for rq_context in rq_contexts: context = PresentationContext() context.context_id = rq_context.context_id context.abstract_syntax = rq_context.abstract_syntax context.transfer_syntax = [rq_context.transfer_syntax[0]] context.result = 0x03 result_contexts.append(context) return result_contexts, [] # Optimisation notes (for iterating through contexts only, not # including actual context negotiation) # - Create dict, use set intersection/difference of dict keys: ~600 us # - Create dict, iterate over dict keys: ~400 us # - Iterate over lists: ~52000 us # Requestor may use the same Abstract Syntax in multiple Presentation # Contexts so we need a more specific key than UID requestor_contexts = {(cx.context_id, cx.abstract_syntax): cx for cx in rq_contexts} # Acceptor supported SOP Classes must be unique so we can use UID as # the key acceptor_contexts = {cx.abstract_syntax: cx for cx in ac_contexts} for (cntx_id, ab_syntax) in requestor_contexts: # Convenience variable rq_context = requestor_contexts[(cntx_id, ab_syntax)] # Create a new PresentationContext item that will store the # results of the negotiation context = PresentationContext() context.context_id = cntx_id context.abstract_syntax = ab_syntax context._as_scu = False context._as_scp = False # Check if the acceptor supports the Abstract Syntax if ab_syntax in acceptor_contexts: # Convenience variables ac_context = acceptor_contexts[ab_syntax] ac_roles = (ac_context.scu_role, ac_context.scp_role) try: rq_roles = roles[ab_syntax] has_role = True except KeyError: rq_roles = (None, None) has_role = False # Abstract syntax supported so check Transfer Syntax for tr_syntax in rq_context.transfer_syntax: # If transfer syntax supported then (provisionally) accept if tr_syntax in ac_context.transfer_syntax: context.transfer_syntax = [tr_syntax] context.result = 0x00 result_contexts.append(context) break ## SCP/SCU Role Selection Negotiation # Only for (provisionally) accepted contexts if context.result == 0x00: if None in ac_roles: # Default roles context._as_scu = False context._as_scp = True # If either aq.scu_role or ac.scp_role is None then # don't send an SCP/SCU negotiation reply has_role = False else: # Use a LUT to make changes to outcomes easier # also its much simpler than coding if/then branches outcome = SCP_SCU_ROLES[rq_roles][ac_roles] context._as_scu = outcome[2] context._as_scp = outcome[3] # If can't act as either SCU nor SCP then reject the context if context.as_scu is False and context.as_scp is False: # User rejection context.result = 0x01 # Need to check against None as 0x00 is a possible value if context.result is None: # Reject context - transfer syntax not supported context.result = 0x04 context.transfer_syntax = [rq_context.transfer_syntax[0]] result_contexts.append(context) elif context.result == 0x00 and has_role: # Create new SCP/SCU Role Selection Negotiation item role = SCP_SCU_RoleSelectionNegotiation() role.sop_class_uid = context.abstract_syntax # Can't return 0x01 if proposed 0x00 if rq_roles[0] is False: role.scu_role = False else: role.scu_role = ac_context.scu_role if rq_roles[1] is False: role.scp_role = False else: role.scp_role = ac_context.scp_role reply_roles[context.abstract_syntax] = role else: # Reject context - abstract syntax not supported context.result = 0x03 context.transfer_syntax = [rq_context.transfer_syntax[0]] result_contexts.append(context) # Sort by presentation context ID # This isn't required by the DICOM Standard but its a nice thing to do result_contexts = sorted(result_contexts, key=lambda x: x.context_id) # Sort role selection by abstract syntax, also not required but nice reply_roles = sorted(reply_roles.values(), key=lambda x: x.sop_class_uid) return result_contexts, reply_roles