File: association.rst

package info (click to toggle)
odil 0.12.2-5
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 5,320 kB
  • sloc: cpp: 55,035; python: 3,971; javascript: 460; xml: 182; makefile: 98
file content (149 lines) | stat: -rw-r--r-- 6,681 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
Network association
===================

.. highlight:: c++

The connection between two DICOM peers is handled by `odil::Association`_ and `odil::AssociationParameters`_. The former encapsulates the connection itself (TCP/IP layer and DICOM layer) while the latter handles the various options that parameterize the association (AE titles, presentation contexts, authentication, etc.).

Establishing an association
---------------------------

An association will at least require to define the application entity titles (both local and remote) the presentation contexts (i.e. representation of the data) that are acceptable for the application. The following example shows how to create a simple `odil::AssociationParameters`_ object.

::
  
  #include <odil/AssociationParameters.h>
  #include <odil/registry.h>
  
  odil::AssociationParameters
  create_parameters()
  {
      std::vector<std::string> transfer_syntaxes{
          odil::registry::ExplicitVRLittleEndian, 
          odil::registry::ExplicitVRLittleEndian
      };
      
      odil::AssociationParameters parameters;
      parameters
          .set_calling_ae_title("WORKSTATION")
          .set_called_ae_title("SERVER")
          .set_presentation_contexts({
              { 
                  odil::registry::Verification, 
                  transfer_syntaxes, 
                  odil::AssociationParameters::PresentationContext::Role::SCU
              },
              { 
                  odil::registry::PatientRootQueryRetrieveInformationModelFind,
                  transfer_syntaxes, 
                  odil::AssociationParameters::PresentationContext::Role::SCU
              }
          });
      return parameters;
  }

Note that the `odil::AssociationParameters`_ object uses parameter chaining. The objects passed to ``set_presentation_contexts`` are of type `odil::AssociationParameters::PresentationContext`_. In this example, the id of the presentation context is not specified: it is instead set to an special value by the constructor, and a valid id is then assigned in ``set_presentation_contexts``.

After setting the address and port of the remote peer, the association is established through the ``associate`` member function. Once established, the negotiated parameters (i.e. acceptable to both peers) may be obtained by the ``get_negotiated_parameters`` member function. 

::
  
  #include <odil/Association.h>
  
  int main()
  {
      odil::Association association;
      association.set_peer_host("www.dicomserver.co.uk");
      association.set_peer_port(11112);
      association.set_parameters(create_parameters());
      association.associate();
      
      auto const & contexts =
          association.get_negotiated_parameters().get_presentation_contexts();
      std::cout << "Presentation contexts (" << contexts.size() << ")\n";
      for(auto const & context: contexts)
      {
          std::cout
              << "\t"
              << odil::registry::uids_dictionary.at(context.abstract_syntax).name
              << ": "
              << odil::registry::uids_dictionary.at(context.transfer_syntaxes[0]).name
              << ", "
              << (context.scu_role_support?"SCU":"")
              << ((context.scu_role_support & context.scp_role_support)?"/":"")
              << (context.scp_role_support?"SCP":"")
              << std::endl;
      }
  }
  
Receiving an association
------------------------

Receiving an association is performed by the ``receive_association`` member function. It will listen on the given IP address and TCP port and block until an acceptable association request is received or until an error occurs. By default all association requests are deemed acceptable, and the first transfer syntax of each presentation context is accepted. Errors (unacceptable association, low-level protocol error) will throw an exception. After acceptance, the association object contains the negotiated parameters:

::
  
  #include <odil/Association.h>
  
  int main()
  {
      odil::Association association;
      
      association.receive_association(boost::asio::ip::tcp::v4(), 11112);
      
      std::cout
            << "Received association from "
            << association.get_peer_host() << ":" << association.get_peer_port()
            << std::endl ;

        auto const & contexts =
            association.get_negotiated_parameters().get_presentation_contexts();
        std::cout << "Presentation contexts (" << contexts.size() << ")\n";
        for(auto const & context: contexts)
        {
            std::cout
                << "\t"
                << odil::registry::uids_dictionary.at(context.abstract_syntax).name
                << ": "
                << odil::registry::uids_dictionary.at(context.transfer_syntaxes[0]).name
                << ", "
                << (context.scu_role_support?"SCU":"")
                << ((context.scu_role_support & context.scp_role_support)?"/":"")
                << (context.scp_role_support?"SCP":"")
                << std::endl;
        }
    }

Optionnally, a callback which indicates whether the association request is acceptable or may be specified. If the request is acceptable, the callback must return the accepted parameters, otherwise it must throw an exception of type `odil::AssociationRejected`_. As an example, the default acceptation callback, which accepts the first transfer syntax for all abstract syntaxes, would be:

::
  
  #include <odil/Association.h>
  
  odil::AssociationParameters
  acceptor(odil::AssociationParameters const & input)
  {
      odil::AssociationParameters output;

      output.set_called_ae_title(input.get_called_ae_title());
      output.set_calling_ae_title(input.get_calling_ae_title());

      auto presentation_contexts = input.get_presentation_contexts();
      for(auto & presentation_context: presentation_contexts)
      {
          presentation_context.transfer_syntaxes =
              { presentation_context.transfer_syntaxes[0] };
          presentation_context.result =
              odil::AssociationParameters::PresentationContext::Result::Acceptance;
      }
      output.set_presentation_contexts(presentation_contexts);
      
      output.set_maximum_length(input.get_maximum_length());
      
      return output;
  }

.. _odil::Association: ../../_static/doxygen/classodil_1_1Association.html
.. _odil::AssociationParameters: ../../_static/doxygen/classodil_1_1AssociationParameters.html
.. _odil::AssociationParameters::PresentationContext: ../../_static/doxygen/structodil_1_1AssociationParameters_1_1PresentationContext.html
.. _odil::AssociationRejected: ../../_static/doxygen/structodil_1_1AssociationRejected.html